Skip to content

Commit ee12544

Browse files
authored
CSHARP-4926: Support token field type with Equals operators (#1565)
1 parent ab31af9 commit ee12544

File tree

4 files changed

+72
-12
lines changed

4 files changed

+72
-12
lines changed

src/MongoDB.Driver/Search/OperatorSearchDefinitions.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,7 @@ private static BsonValue ToBsonValue(TField value) =>
145145
DateTime v => (BsonDateTime)v,
146146
DateTimeOffset v => (BsonDateTime)v.UtcDateTime,
147147
ObjectId v => (BsonObjectId)v,
148+
string v => (BsonString)v,
148149
_ => throw new InvalidCastException()
149150
};
150151
}

src/MongoDB.Driver/Search/SearchDefinitionBuilder.cs

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ public SearchDefinition<TDocument> EmbeddedDocument<TField>(
111111
/// <summary>
112112
/// Creates a search definition that queries for documents where an indexed field is equal
113113
/// to the specified value.
114-
/// Supported value types are boolean, numeric, ObjectId and date.
114+
/// Supported value types are boolean, numeric, ObjectId, date and string.
115115
/// </summary>
116116
/// <typeparam name="TField">The type of the field.</typeparam>
117117
/// <param name="path">The indexed field to search.</param>
@@ -121,14 +121,13 @@ public SearchDefinition<TDocument> EmbeddedDocument<TField>(
121121
public SearchDefinition<TDocument> Equals<TField>(
122122
FieldDefinition<TDocument, TField> path,
123123
TField value,
124-
SearchScoreDefinition<TDocument> score = null)
125-
where TField : struct, IComparable<TField> =>
124+
SearchScoreDefinition<TDocument> score = null) =>
126125
new EqualsSearchDefinition<TDocument, TField>(path, value, score);
127126

128127
/// <summary>
129128
/// Creates a search definition that queries for documents where an indexed field is equal
130129
/// to the specified value.
131-
/// Supported value types are boolean, numeric, ObjectId and date.
130+
/// Supported value types are boolean, numeric, ObjectId, date and string.
132131
/// </summary>
133132
/// <typeparam name="TField">The type of the field.</typeparam>
134133
/// <param name="path">The indexed field to search.</param>
@@ -138,9 +137,25 @@ public SearchDefinition<TDocument> Equals<TField>(
138137
public SearchDefinition<TDocument> Equals<TField>(
139138
Expression<Func<TDocument, TField>> path,
140139
TField value,
141-
SearchScoreDefinition<TDocument> score = null)
142-
where TField : struct, IComparable<TField> =>
140+
SearchScoreDefinition<TDocument> score = null) =>
143141
Equals(new ExpressionFieldDefinition<TDocument, TField>(path), value, score);
142+
143+
/// <summary>
144+
/// Creates a search definition that queries for documents where at least one element in an indexed array field is equal
145+
/// to the specified value.
146+
/// Supported value types are boolean, numeric, ObjectId, date and string.
147+
/// </summary>
148+
/// <typeparam name="TField">The type of elements contained in the indexed array field.</typeparam>
149+
/// <param name="path">The indexed array field to search.</param>
150+
/// <param name="value">The value to query for.</param>
151+
/// <param name="score">The score modifier.</param>
152+
/// <returns>An equality search definition.</returns>
153+
public SearchDefinition<TDocument> Equals<TField>(
154+
Expression<Func<TDocument, IEnumerable<TField>>> path,
155+
TField value,
156+
SearchScoreDefinition<TDocument> score = null) =>
157+
new EqualsSearchDefinition<TDocument, TField>(
158+
new ExpressionFieldDefinition<TDocument, IEnumerable<TField>>(path), value, score);
144159

145160
/// <summary>
146161
/// Creates a search definition that tests if a path to a specified indexed field name
@@ -574,7 +589,7 @@ public SearchDefinition<TDocument> QueryString<TField>(
574589
/// <param name="path">The indexed field or fields to search.</param>
575590
/// <param name="range">The field range.</param>
576591
/// <param name="score">The score modifier.</param>
577-
/// <returns>A a range search definition.</returns>
592+
/// <returns>A range search definition.</returns>
578593
public SearchDefinition<TDocument> Range<TField>(
579594
Expression<Func<TDocument, TField>> path,
580595
SearchRange<TField> range,
@@ -589,7 +604,7 @@ public SearchDefinition<TDocument> Range<TField>(
589604
/// <param name="path">The indexed field or fields to search.</param>
590605
/// <param name="range">The field range.</param>
591606
/// <param name="score">The score modifier.</param>
592-
/// <returns>A a range search definition.</returns>
607+
/// <returns>A range search definition.</returns>
593608
public SearchDefinition<TDocument> Range<TField>(
594609
SearchPathDefinition<TDocument> path,
595610
SearchRange<TField> range,

tests/MongoDB.Driver.Tests/Search/AtlasSearchTests.cs

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@
2323
using MongoDB.Driver.Core.TestHelpers.Logging;
2424
using MongoDB.Driver.GeoJsonObjectModel;
2525
using MongoDB.Driver.Search;
26-
using MongoDB.Driver.TestHelpers;
2726
using MongoDB.TestHelpers.XunitExtensions;
2827
using Xunit;
2928
using Xunit.Abstractions;
@@ -132,6 +131,35 @@ public void EmbeddedDocument()
132131
}
133132
}
134133

134+
[Fact]
135+
public void EqualsArrayField()
136+
{
137+
var results = GetSynonymTestCollection().Aggregate()
138+
.Search(Builders<Movie>.Search.Equals(p => p.Genres, "family"))
139+
.Limit(3)
140+
.ToList();
141+
142+
results.Should().HaveCount(3);
143+
foreach (var result in results)
144+
{
145+
result.Genres.Should().Contain("Family");
146+
}
147+
148+
results[0].Title.Should().Be("The Poor Little Rich Girl");
149+
results[1].Title.Should().Be("Robin Hood");
150+
results[2].Title.Should().Be("Peter Pan");
151+
}
152+
153+
[Fact]
154+
public void EqualsStringField()
155+
{
156+
var results = GetSynonymTestCollection().Aggregate()
157+
.Search(Builders<Movie>.Search.Equals(p => p.Title, "a corner in wheat"))
158+
.ToList();
159+
160+
results.Should().ContainSingle().Which.Title.Should().Be("A Corner in Wheat");
161+
}
162+
135163
[Fact]
136164
public void Exists()
137165
{
@@ -749,6 +777,9 @@ public class Comment
749777
[BsonIgnoreExtraElements]
750778
public class Movie
751779
{
780+
[BsonElement("genres")]
781+
public string[] Genres { get; set; }
782+
752783
[BsonElement("title")]
753784
public string Title { get; set; }
754785

tests/MongoDB.Driver.Tests/Search/SearchDefinitionBuilderTests.cs

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,16 @@ public void EmbeddedDocument_typed()
261261
subjectFamily.EmbeddedDocument(p => p.Children, subjectPerson.QueryString(p => p.LastName, "berg")),
262262
"{ embeddedDocument: { path : 'Children', operator : { 'queryString' : { defaultPath : 'Children.ln', query : 'berg' } } } }");
263263
}
264+
265+
[Fact]
266+
public void Equals_with_array_should_render_supported_type()
267+
{
268+
var subjectTyped = CreateSubject<Person>();
269+
270+
AssertRendered(
271+
subjectTyped.Equals(p => p.Hobbies, "soccer"),
272+
"{ equals: { path: 'hobbies', value: 'soccer' } }");
273+
}
264274

265275
[Theory]
266276
[MemberData(nameof(EqualsSupportedTypesTestData))]
@@ -269,7 +279,6 @@ public void Equals_should_render_supported_type<T>(
269279
string valueRendered,
270280
Expression<Func<Person, T>> fieldExpression,
271281
string fieldRendered)
272-
where T : struct, IComparable<T>
273282
{
274283
var subject = CreateSubject<BsonDocument>();
275284
var subjectTyped = CreateSubject<Person>();
@@ -302,12 +311,13 @@ public void Equals_should_render_supported_type<T>(
302311
new object[] { (double)1, "1", Exp(p => p.Double), nameof(Person.Double) },
303312
new object[] { DateTime.MinValue, "ISODate(\"0001-01-01T00:00:00Z\")", Exp(p => p.Birthday), "dob" },
304313
new object[] { DateTimeOffset.MaxValue, "ISODate(\"9999-12-31T23:59:59.999Z\")", Exp(p => p.DateTimeOffset), nameof(Person.DateTimeOffset) },
305-
new object[] { ObjectId.Empty, "{ $oid: '000000000000000000000000' }", Exp(p => p.Id), "_id" }
314+
new object[] { ObjectId.Empty, "{ $oid: '000000000000000000000000' }", Exp(p => p.Id), "_id" },
315+
new object[] { "Jim", "\"Jim\"", Exp(p => p.FirstName), "fn" }
306316
};
307317

308318
[Theory]
309319
[MemberData(nameof(EqualsUnsupportedTypesTestData))]
310-
public void Equals_should_throw_on_unsupported_type<T>(T value, Expression<Func<Person, T>> fieldExpression) where T : struct, IComparable<T>
320+
public void Equals_should_throw_on_unsupported_type<T>(T value, Expression<Func<Person, T>> fieldExpression)
311321
{
312322
var subject = CreateSubject<BsonDocument>();
313323
Record.Exception(() => subject.Equals("x", value)).Should().BeOfType<InvalidCastException>();
@@ -1248,6 +1258,9 @@ public class Person : SimplePerson
12481258

12491259
[BsonElement("ret")]
12501260
public bool Retired { get; set; }
1261+
1262+
[BsonElement("hobbies")]
1263+
public string[] Hobbies { get; set; }
12511264

12521265
public object Object { get; set; }
12531266
}

0 commit comments

Comments
 (0)