Skip to content

Commit 910e9da

Browse files
CSHARP-2963: And filter with $near translates to invalid MongoDb query.
1 parent dec7cb4 commit 910e9da

File tree

2 files changed

+40
-4
lines changed

2 files changed

+40
-4
lines changed

src/MongoDB.Driver/FilterDefinitionBuilder.cs

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1493,6 +1493,16 @@ public FilterDefinition<TDocument> Where(Expression<Func<TDocument, bool>> expre
14931493

14941494
internal sealed class AndFilterDefinition<TDocument> : FilterDefinition<TDocument>
14951495
{
1496+
#region static
1497+
private static readonly string[] __operatorsThatCannotBeCombined = new[]
1498+
{
1499+
"$geoWithin",
1500+
"$near",
1501+
"$geoIntersects",
1502+
"$nearSphere"
1503+
};
1504+
#endregion
1505+
14961506
private readonly List<FilterDefinition<TDocument>> _filters;
14971507

14981508
public AndFilterDefinition(IEnumerable<FilterDefinition<TDocument>> filters)
@@ -1541,11 +1551,11 @@ private static void AddClause(BsonDocument document, BsonElement clause)
15411551
else if (document.Contains(clause.Name))
15421552
{
15431553
var existingClause = document.GetElement(clause.Name);
1544-
if (existingClause.Value is BsonDocument && clause.Value is BsonDocument)
1554+
if (existingClause.Value is BsonDocument existingClauseValue && clause.Value is BsonDocument clauseValue)
15451555
{
1546-
var clauseValue = (BsonDocument)clause.Value;
1547-
var existingClauseValue = (BsonDocument)existingClause.Value;
1548-
if (clauseValue.Names.Any(op => existingClauseValue.Contains(op)))
1556+
var clauseOperator = clauseValue.ElementCount > 0 ? clauseValue.GetElement(0).Name : null;
1557+
if (clauseValue.Names.Any(op => existingClauseValue.Contains(op)) ||
1558+
__operatorsThatCannotBeCombined.Contains(clauseOperator))
15491559
{
15501560
PromoteFilterToDollarForm(document, clause);
15511561
}

tests/MongoDB.Driver.Tests/FilterDefinitionBuilderTests.cs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,32 @@ public void And_with_clashing_keys_but_different_operators_should_get_merged()
7878
Assert(filter, "{a: {$gt: 1, $lt: 10}}");
7979
}
8080

81+
[Theory]
82+
[InlineData("{ geoField : { $geoWithin : { $box : [ [ 1.0, 2.0 ], [ 3.0, 4.0 ] ] } } }", "{ geoField : { $near : [ 5.0, 6.0 ] } }")]
83+
[InlineData("{ geoField : { $near : [ 5.0, 6.0 ] } }", "{ geoField : { $geoWithin : { $box : [ [ 1.0, 2.0 ], [ 3.0, 4.0 ] ] } } }")]
84+
[InlineData("{ geoField : { $nearSphere : { $geometry : { type : 'Point', coordinates : [ 1, 2 ] } } } }", "{ geoField : { $geoIntersects : { $geometry : { type : 'Polygon', coordinates: [ [ [ 1, 2 ], [ 3, 4 ], [ 5, 6 ], [ 7, 8 ] ] ] } } } }")]
85+
[InlineData("{ geoField : { $geoIntersects : { $geometry : { type : 'Polygon', coordinates: [ [ [ 1, 2 ], [ 3, 4 ], [ 5, 6 ], [ 7, 8 ] ] ] } } } }", "{ geoField : { $nearSphere : { $geometry : { type : 'Point', coordinates : [ 1, 2 ] } } } }")]
86+
public void And_with_clashing_keys_and_different_operators_but_with_filters_that_support_only_dollar_form_should_get_promoted_to_dollar_form(string firstFilter, string secondFilter)
87+
{
88+
var subject = CreateSubject<BsonDocument>();
89+
90+
var combinedFilter = subject.And(firstFilter, secondFilter);
91+
92+
Assert(combinedFilter, $"{{ $and : [ {firstFilter}, {secondFilter} ] }}");
93+
}
94+
95+
[Fact]
96+
public void And_with_clashing_keys_and_different_operators_but_with_filters_that_support_only_dollar_form_and_empty_filter_should_ignore_empty_filter()
97+
{
98+
var subject = CreateSubject<BsonDocument>();
99+
100+
var combinedFilter = subject.And(
101+
"{ geoField : { $near : [ 5.0, 6.0 ] } }",
102+
"{ geoField : { } }");
103+
104+
Assert(combinedFilter, "{ geoField : { $near : [ 5.0, 6.0 ] } }");
105+
}
106+
81107
[Fact]
82108
public void And_with_an_empty_filter()
83109
{

0 commit comments

Comments
 (0)