Skip to content

Commit c471e1b

Browse files
authored
CSHARP-5430: Add array field support for In and Range search operators (#1576)
1 parent aedf097 commit c471e1b

File tree

2 files changed

+74
-1
lines changed

2 files changed

+74
-1
lines changed

src/MongoDB.Driver/Search/SearchDefinitionBuilder.cs

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -330,7 +330,7 @@ public SearchDefinition<TDocument> In<TField>(
330330
/// <summary>
331331
/// Creates a search definition that queries for documents where the value of the field equals to any of specified values.
332332
/// </summary>
333-
/// <typeparam name="TField">The type of the field. Valid types are: boolean, ObjectId, Guid, number, date, string.</typeparam>
333+
/// <typeparam name="TField">The type of the field. Valid types are: boolean, ObjectId, Guid, number, date, string, arrays.</typeparam>
334334
/// <param name="path">The indexed field or fields to search.</param>
335335
/// <param name="values">Values to compare the field with.</param>
336336
/// <param name="score">The score modifier.</param>
@@ -340,6 +340,20 @@ public SearchDefinition<TDocument> In<TField>(
340340
IEnumerable<TField> values,
341341
SearchScoreDefinition<TDocument> score = null) =>
342342
In(new ExpressionFieldDefinition<TDocument>(path), values, score);
343+
344+
/// <summary>
345+
/// Creates a search definition that queries for documents where the value of the field equals to any of specified values.
346+
/// </summary>
347+
/// <typeparam name="TField">The type of the field. Valid types are: boolean, ObjectId, Guid, number, date, string, arrays.</typeparam>
348+
/// <param name="path">The indexed field or fields to search.</param>
349+
/// <param name="values">Values to compare the field with.</param>
350+
/// <param name="score">The score modifier.</param>
351+
/// <returns>An In search definition.</returns>
352+
public SearchDefinition<TDocument> In<TField>(
353+
Expression<Func<TDocument, IEnumerable<TField>>> path,
354+
IEnumerable<TField> values,
355+
SearchScoreDefinition<TDocument> score = null) =>
356+
In(new ExpressionFieldDefinition<TDocument>(path), values, score);
343357

344358
/// <summary>
345359
/// Creates a search definition that returns documents similar to the input documents.
@@ -596,6 +610,21 @@ public SearchDefinition<TDocument> Range<TField>(
596610
SearchScoreDefinition<TDocument> score = null)
597611
where TField : struct, IComparable<TField> =>
598612
Range(new ExpressionFieldDefinition<TDocument>(path), range, score);
613+
614+
/// <summary>
615+
/// Creates a search definition that queries for documents where a field is in the specified range.
616+
/// </summary>
617+
/// <typeparam name="TField">The type of the field.</typeparam>
618+
/// <param name="path">The indexed field or fields to search.</param>
619+
/// <param name="range">The field range.</param>
620+
/// <param name="score">The score modifier.</param>
621+
/// <returns>A range search definition.</returns>
622+
public SearchDefinition<TDocument> Range<TField>(
623+
Expression<Func<TDocument, IEnumerable<TField>>> path,
624+
SearchRange<TField> range,
625+
SearchScoreDefinition<TDocument> score = null)
626+
where TField : struct, IComparable<TField> =>
627+
Range(new ExpressionFieldDefinition<TDocument>(path), range, score);
599628

600629
/// <summary>
601630
/// Creates a search definition that queries for documents where a field is in the specified range.

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

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,9 @@ public void Autocomplete_typed()
106106
AssertRendered(
107107
subject.Autocomplete("FirstName", "foo"),
108108
"{ autocomplete: { query: 'foo', path: 'fn' } }");
109+
AssertRendered(
110+
subject.Autocomplete(x => x.Hobbies, "foo"),
111+
"{ autocomplete: { query: 'foo', path: 'hobbies' } }");
109112

110113
AssertRendered(
111114
subject.Autocomplete(
@@ -352,6 +355,9 @@ public void Exists_typed()
352355
AssertRendered(
353356
subject.Exists(x => x.FirstName),
354357
"{ exists: { path: 'fn' } }");
358+
AssertRendered(
359+
subject.Exists(x => x.Hobbies),
360+
"{ exists: { path: 'hobbies' } }");
355361
AssertRendered(
356362
subject.Exists("FirstName"),
357363
"{ exists: { path: 'fn' } }");
@@ -599,6 +605,16 @@ public void In_should_throw_when_values_are_not_of_same_type()
599605
var subjectTyped = CreateSubject<Person>();
600606
Record.Exception(() => subjectTyped.In(p => p.Object, values)).Should().BeOfType<ArgumentException>();
601607
}
608+
609+
[Fact]
610+
public void In_with_array_field_should_render_correctly()
611+
{
612+
var subjectTyped = CreateSubject<Person>();
613+
614+
AssertRendered(
615+
subjectTyped.In(p => p.Hobbies, ["dance", "ski"]),
616+
"{ in: { path: 'hobbies', value: ['dance', 'ski'] } }");
617+
}
602618

603619
[Fact]
604620
public void MoreLikeThis()
@@ -771,6 +787,9 @@ public void Phrase_typed()
771787
AssertRendered(
772788
subject.Phrase("FirstName", "foo"),
773789
"{ phrase: { query: 'foo', path: 'fn' } }");
790+
AssertRendered(
791+
subject.Phrase(x => x.Hobbies, "foo"),
792+
"{ phrase: { query: 'foo', path: 'hobbies' } }");
774793

775794
AssertRendered(
776795
subject.Phrase(
@@ -832,6 +851,9 @@ public void QueryString_typed()
832851
AssertRendered(
833852
subject.QueryString("FirstName", "foo"),
834853
"{ queryString: { defaultPath: 'fn', query: 'foo' } }");
854+
AssertRendered(
855+
subject.QueryString(x => x.Hobbies, "foo"),
856+
"{ queryString: { defaultPath: 'hobbies', query: 'foo' } }");
835857
}
836858

837859
[Fact]
@@ -943,6 +965,16 @@ public void Range_should_throw_on_unsupported_types<T>(T value, Expression<Func<
943965
new object[] { (ulong)1, Exp(p => p.UInt64) },
944966
new object[] { TimeSpan.Zero, Exp(p => p.TimeSpan) },
945967
};
968+
969+
[Fact]
970+
public void Range_with_array_field_should_render_correctly()
971+
{
972+
var subject = CreateSubject<Person>();
973+
974+
AssertRendered(
975+
subject.Range(x => x.SalaryHistory, SearchRangeBuilder.Gte(1000).Lt(2000)),
976+
"{ range: { path: 'salaries', gte: 1000, lt: 2000 } }");
977+
}
946978

947979
[Fact]
948980
public void Regex()
@@ -986,6 +1018,9 @@ public void Regex_typed()
9861018
AssertRendered(
9871019
subject.Regex("FirstName", "foo"),
9881020
"{ regex: { query: 'foo', path: 'fn' } }");
1021+
AssertRendered(
1022+
subject.Regex(x => x.Hobbies, "foo"),
1023+
"{ regex: { query: 'foo', path: 'hobbies' } }");
9891024

9901025
AssertRendered(
9911026
subject.Regex(
@@ -1100,6 +1135,9 @@ public void Text_typed()
11001135
AssertRendered(
11011136
subject.Text("FirstName", "foo"),
11021137
"{ text: { query: 'foo', path: 'fn' } }");
1138+
AssertRendered(
1139+
subject.Text(x => x.Hobbies, "foo"),
1140+
"{ text: { query: 'foo', path: 'hobbies' } }");
11031141

11041142
AssertRendered(
11051143
subject.Text(
@@ -1180,6 +1218,9 @@ public void Wildcard_typed()
11801218
AssertRendered(
11811219
subject.Wildcard("FirstName", "foo"),
11821220
"{ wildcard: { query: 'foo', path: 'fn' } }");
1221+
AssertRendered(
1222+
subject.Wildcard(x => x.Hobbies, "foo"),
1223+
"{ wildcard: { query: 'foo', path: 'hobbies' } }");
11831224

11841225
AssertRendered(
11851226
subject.Wildcard(
@@ -1265,6 +1306,9 @@ public class Person : SimplePerson
12651306
[BsonElement("hobbies")]
12661307
public string[] Hobbies { get; set; }
12671308

1309+
[BsonElement("salaries")]
1310+
public int[] SalaryHistory { get; set; }
1311+
12681312
public object Object { get; set; }
12691313

12701314
public string Name { get; set; }

0 commit comments

Comments
 (0)