Skip to content

Commit 1a49c6e

Browse files
CSHARP-2012: Fix Any workflow with ne operator.
1 parent 8fb4450 commit 1a49c6e

File tree

4 files changed

+91
-21
lines changed

4 files changed

+91
-21
lines changed

src/MongoDB.Driver/Linq/Translators/PredicateTranslator.cs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,13 @@
1616
using System;
1717
using System.Collections;
1818
using System.Collections.Generic;
19-
using System.Globalization;
2019
using System.Linq;
2120
using System.Linq.Expressions;
2221
using System.Reflection;
2322
using System.Text.RegularExpressions;
2423
using MongoDB.Bson;
2524
using MongoDB.Bson.Serialization;
2625
using MongoDB.Bson.Serialization.Options;
27-
using MongoDB.Bson.Serialization.Serializers;
2826
using MongoDB.Driver.Linq.Expressions;
2927
using MongoDB.Driver.Linq.Expressions.ResultOperators;
3028
using MongoDB.Driver.Linq.Processors;
@@ -177,7 +175,13 @@ private bool CanAnyBeRenderedWithoutElemMatch(Expression node)
177175
case ExpressionType.LessThan:
178176
case ExpressionType.LessThanOrEqual:
179177
case ExpressionType.NotEqual:
180-
return true;
178+
// the SERVER processes a $ne operator in a different way with
179+
// other comparison operators (see CSHARP-2012).
180+
// So, a NotEqual operator should be handled only by a "$elemMatch".
181+
// To simplify the logic and do not take responsibility for analysis
182+
// an expression here, other comparison operators are processed in the
183+
// same way as $ne.
184+
return false;
181185
case ExpressionType.Call:
182186
var callNode = (MethodCallExpression)node;
183187
switch (callNode.Method.Name)
@@ -1657,4 +1661,4 @@ protected internal override Expression VisitPipeline(PipelineExpression node)
16571661
}
16581662
}
16591663
}
1660-
}
1664+
}

tests/MongoDB.Driver.Tests/Linq/MongoQueryableTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1489,7 +1489,7 @@ public void Where_method_with_predicated_any()
14891489

14901490
Assert(query,
14911491
1,
1492-
"{ $match: { 'G.D': \"Don't\" } }");
1492+
"{ $match : { 'G' : { '$elemMatch' : { 'D' : \"Don't\" } } } }");
14931493
}
14941494

14951495
private List<T> Assert<T>(IMongoQueryable<T> queryable, int resultCount, params string[] expectedStages)

tests/MongoDB.Driver.Tests/Linq/Translators/LegacyPredicateTranslatorTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -286,7 +286,7 @@ public void TestWhereDNotEquals11Not()
286286
[Fact]
287287
public void TestWhereDAAnyWithPredicate()
288288
{
289-
Assert<C>(c => c.DA.Any(d => d.Z == 333), 1, "{ \"da.z\" : 333 }");
289+
Assert<C>(c => c.DA.Any(d => d.Z == 333), 1, "{ \"da\" : { \"$elemMatch\" : { \"z\" : 333 } } }");
290290

291291
Assert<C>(c => c.DA.Any(d => d.Z >= 222 && d.Z <= 444), 2, "{ \"da\" : { \"$elemMatch\" : { \"z\" : { \"$gte\" : 222, \"$lte\" : 444 } } } }");
292292
}

tests/MongoDB.Driver.Tests/Linq/Translators/PredicateTranslatorTests.cs

Lines changed: 81 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,9 @@
1818
using System.Linq;
1919
using System.Linq.Expressions;
2020
using System.Text.RegularExpressions;
21-
using System.Threading.Tasks;
2221
using FluentAssertions;
2322
using MongoDB.Bson;
2423
using MongoDB.Bson.Serialization;
25-
using MongoDB.Bson.TestHelpers.XunitExtensions;
26-
using MongoDB.Driver.Core;
2724
using MongoDB.Driver.Core.TestHelpers.XunitExtensions;
2825
using MongoDB.Driver.Linq;
2926
using MongoDB.Driver.Linq.Translators;
@@ -115,12 +112,81 @@ public void Any_with_a_predicate_on_documents()
115112
Assert(
116113
x => x.G.Any(g => g.D == "Don't"),
117114
1,
118-
"{\"G.D\": \"Don't\"}");
115+
"{ G : { $elemMatch : { D : \"Don't\" } } }");
119116

120117
Assert(
121118
x => x.G.Any(g => g.D == "Don't" && g.E.F == 33),
122119
1,
123-
"{G: {$elemMatch: {D: \"Don't\", 'E.F': 33}}}");
120+
"{ G : { $elemMatch : { D : \"Don't\", 'E.F' : 33 } } }");
121+
}
122+
123+
[Fact]
124+
public void Any_with_a_gte_predicate_on_documents()
125+
{
126+
Assert(
127+
x => x.G.Any(g => g.E.F >= 100),
128+
1,
129+
"{ \"G\" : { \"$elemMatch\" : { \"E.F\" : { \"$gte\" : 100 } } } }");
130+
}
131+
132+
[Fact]
133+
public void Any_with_a_ne_and_Equal_predicate_on_documents()
134+
{
135+
Assert(
136+
x => x.G.Any(g => !g.D.Equals("Don't")),
137+
2,
138+
"{ \"G\" : { \"$elemMatch\" : { \"D\" : { \"$ne\" : \"Don't\" } } } }");
139+
}
140+
141+
[Fact]
142+
public void Any_with_a_ne_predicate_on_documents()
143+
{
144+
Assert(
145+
x => x.G.Any(g => g.S != null),
146+
1,
147+
"{ \"G\" : { \"$elemMatch\" : { \"S\" : { \"$ne\" : null } } } }");
148+
Assert(
149+
x => x.G.Any(g => !(g.S == null)),
150+
1,
151+
"{ \"G\" : { \"$elemMatch\" : { \"S\" : { \"$ne\" : null } } } }");
152+
}
153+
154+
[Fact]
155+
public void Any_with_a_multi_not_brackets_predicate_on_documents()
156+
{
157+
Assert(
158+
x => x.G.Any(g => !(!(g.D == "Don't"))),
159+
1,
160+
"{ \"G\" : { \"$elemMatch\" : { \"D\" : \"Don't\" } } }");
161+
162+
Assert(
163+
x => x.G.Any(g => !(!(!(!(g.D == "Don't"))))),
164+
1,
165+
"{ \"G\" : { \"$elemMatch\" : { \"D\" : \"Don't\" } } }");
166+
167+
Assert(
168+
x => x.G.Any(g => !(g.S == null)),
169+
1,
170+
"{ \"G\" : { \"$elemMatch\" : { \"S\" : { \"$ne\" : null } } } }");
171+
172+
Assert(
173+
x => x.G.Any(g => !(!(!(g.S == null)))),
174+
1,
175+
"{ \"G\" : { \"$elemMatch\" : { \"S\" : { \"$ne\" : null } } } }");
176+
}
177+
178+
[Fact]
179+
public void Any_with_a_multi_conditions_predicate_on_documents()
180+
{
181+
Assert(
182+
x => x.G.Any(g => g.D != "Don't" && g.E.F == 333),
183+
1,
184+
"{ \"G\" : { \"$elemMatch\" : { \"D\" : { \"$ne\" : \"Don't\" }, \"E.F\" : 333 } } }");
185+
186+
Assert(
187+
x => x.G.Any(g => g.D == "Don't" || g.E.F != 32),
188+
2,
189+
"{ \"G\" : { \"$elemMatch\" : { \"$or\" : [{ \"D\" : \"Don't\" }, { \"E.F\" : { \"$ne\" : 32 } }] } } }");
124190
}
125191

126192
[Fact]
@@ -129,22 +195,22 @@ public void Any_with_a_nested_Any()
129195
Assert(
130196
x => x.G.Any(g => g.S.Any()),
131197
1,
132-
"{G: {$elemMatch: {S: {$ne: null, $not: {$size: 0}}}}}");
198+
"{ G : { $elemMatch : { S : { $ne : null, $not : { $size : 0 } } } } }");
133199

134200
Assert(
135201
x => x.G.Any(g => g.S.Any(s => s.D == "Delilah")),
136202
1,
137-
"{\"G.S.D\": \"Delilah\"}");
203+
"{ \"G.S\" : { $elemMatch : { \"D\" : \"Delilah\" } } }");
138204

139205
Assert(
140206
x => x.G.Any(g => g.D == "Don't" && g.S.Any(s => s.D == "Delilah")),
141207
1,
142-
"{G: {$elemMatch: {D: \"Don't\", \"S.D\": \"Delilah\" }}}");
208+
"{ \"G\" : { \"$elemMatch\" : { \"D\" : \"Don't\", \"S\" : { \"$elemMatch\" : { \"D\" : \"Delilah\" } } } } }");
143209

144210
Assert(
145211
x => x.G.Any(g => g.D == "Don't" && g.S.Any(s => s.E == null && s.D == "Delilah")),
146212
1,
147-
"{G: {$elemMatch: {D: \"Don't\", \"S\": {$elemMatch: {E: null, D: \"Delilah\" }}}}}");
213+
"{ G : { $elemMatch : { D : \"Don't\", \"S\" : { $elemMatch : { E : null, D : \"Delilah\" } } } } }");
148214
}
149215

150216
[Fact]
@@ -153,12 +219,12 @@ public void Any_with_a_not()
153219
Assert(
154220
x => x.G.Any(g => !g.S.Any()),
155221
2,
156-
"{G: {$elemMatch: {$nor: [{S: {$ne: null, $not: {$size: 0}}}]}}}");
222+
"{ G : { $elemMatch : { $nor : [{ S : { $ne : null, $not : { $size : 0 } } }] } } }");
157223

158224
Assert(
159225
x => x.G.Any(g => !g.S.Any(s => s.D == "Delilah")),
160226
1,
161-
"{\"G.S.D\": {$ne: \"Delilah\"}}");
227+
"{\"G.S\" : { $not : { $elemMatch : { \"D\" : \"Delilah\" } } } }");
162228
}
163229

164230
[Fact]
@@ -167,12 +233,12 @@ public void Any_with_a_predicate_on_scalars_legacy()
167233
Assert(
168234
x => x.M.Any(m => m > 5),
169235
1,
170-
"{M: {$gt: 5}}");
236+
"{ M : { $elemMatch : { $gt : 5 } } }");
171237

172238
Assert(
173239
x => x.M.Any(m => m > 2 && m < 6),
174240
2,
175-
"{M: {$elemMatch: {$gt: 2, $lt: 6}}}");
241+
"{ M : { $elemMatch : { $gt : 2, $lt : 6 } } }");
176242
}
177243

178244
[SkippableFact]
@@ -240,7 +306,7 @@ public void AsQueryable()
240306
Assert(
241307
x => x.G.AsQueryable().Any(filter),
242308
1,
243-
"{ 'G.D': \"Don't\" }");
309+
"{ 'G' : { '$elemMatch' : { 'D' : \"Don't\" } } }");
244310
}
245311

246312
[SkippableFact]
@@ -865,4 +931,4 @@ public void Assert(Expression<Func<Root, bool>> filter, int expectedCount, BsonD
865931
Assert(__collection, filter, expectedCount, expectedFilter);
866932
}
867933
}
868-
}
934+
}

0 commit comments

Comments
 (0)