Skip to content

Commit 9335995

Browse files
DmitryLukyanovrstam
authored andcommitted
CSHARP-2699: Fix an $elemMatch query for nested Anys.
1 parent baa8ccd commit 9335995

File tree

4 files changed

+156
-9
lines changed

4 files changed

+156
-9
lines changed

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

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,18 @@ private bool DoesExpressionUseDocumentItself(Expression node)
243243
return false;
244244
}
245245

246+
private string PrepareFieldName(IFieldExpression fieldExpression)
247+
{
248+
if (fieldExpression.Document is IFieldExpression documentFieldExpression)
249+
{
250+
return $"{documentFieldExpression.FieldName}{fieldExpression.FieldName}";
251+
}
252+
else
253+
{
254+
return fieldExpression.FieldName;
255+
}
256+
}
257+
246258
private FilterDefinition<BsonDocument> TranslateArrayLength(Expression variableNode, ExpressionType operatorType, ConstantExpression constantNode)
247259
{
248260
var allowedOperators = new[]
@@ -952,15 +964,17 @@ private FilterDefinition<BsonDocument> TranslatePipelineAny(PipelineExpression n
952964
FilterDefinition<BsonDocument> filter;
953965
var renderWithoutElemMatch = CanAnyBeRenderedWithoutElemMatch(whereExpression.Predicate);
954966

967+
var fieldName = PrepareFieldName(fieldExpression);
955968
if (renderWithoutElemMatch)
956969
{
957-
var predicate = FieldNamePrefixer.Prefix(whereExpression.Predicate, fieldExpression.FieldName);
970+
var predicate = FieldNamePrefixer.Prefix(whereExpression.Predicate, fieldName);
958971
filter = Translate(predicate);
959972
}
960973
else
961974
{
962975
var predicate = DocumentToFieldConverter.Convert(whereExpression.Predicate);
963-
filter = __builder.ElemMatch(fieldExpression.FieldName, Translate(predicate));
976+
977+
filter = __builder.ElemMatch(fieldName, Translate(predicate));
964978

965979
filter = ConvertElemMatchFilterToScalarElementMatchIfNeeded(filter, fieldExpression, whereExpression.Predicate);
966980
}

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

Lines changed: 62 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -99,10 +99,67 @@ private void InsertFirst()
9999
S = new [] {
100100
new C
101101
{
102-
D = "Delilah"
102+
D = "Delilah",
103+
Z = new []
104+
{
105+
new E()
106+
{
107+
F = 1,
108+
I = new [] { "I1", "I2" },
109+
}
110+
}
103111
}
104112
},
105-
Ids = new [] { new ObjectId("111111111111111111111111") }
113+
Ids = new [] { new ObjectId("111111111111111111111111") },
114+
Y = new C
115+
{
116+
D = "Don't",
117+
E = new E
118+
{
119+
F = 33,
120+
H = 44,
121+
I = new [] { "ignanimous"}
122+
},
123+
S = new [] {
124+
new C
125+
{
126+
D = "Delilah",
127+
Z = new []
128+
{
129+
new E
130+
{
131+
F = 1,
132+
I = new [] { "I1", "I2" },
133+
C = new C
134+
{
135+
D = "D",
136+
E = new E
137+
{
138+
C = new C
139+
{
140+
X = new []
141+
{
142+
new E
143+
{
144+
F = 2
145+
}
146+
}
147+
}
148+
},
149+
X = new []
150+
{
151+
new E
152+
{
153+
F = 4
154+
}
155+
}
156+
}
157+
}
158+
}
159+
}
160+
},
161+
Ids = new [] { new ObjectId("111111111111111111111111") }
162+
}
106163
},
107164
new C
108165
{
@@ -261,6 +318,8 @@ public class C
261318
public IEnumerable<C> S { get; set; }
262319

263320
public IEnumerable<E> X { get; set; }
321+
public C Y { get; set; }
322+
public IEnumerable<E> Z { get; set; }
264323
}
265324

266325
public class E
@@ -270,6 +329,7 @@ public class E
270329
public int H { get; set; }
271330

272331
public IEnumerable<string> I { get; set; }
332+
public C C { get; set; }
273333
}
274334

275335
public class V : E

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

Lines changed: 77 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ public void Any_with_a_predicate_on_documents_itself_and_ClassEquals()
167167
Assert(
168168
x => x.G.Any(g => g == c1),
169169
1,
170-
"{ \"G\" : { \"$elemMatch\" : { \"Ids\" : null, \"D\" : \"Dolphin\", \"E\" : { \"F\" : 55, \"H\" : 66, \"I\" : [\"insecure\"] }, \"S\" : null, \"X\" : null } } }");
170+
"{ \"G\" : { \"$elemMatch\" : { \"Ids\" : null, \"D\" : \"Dolphin\", \"E\" : { \"F\" : 55, \"H\" : 66, \"I\" : [\"insecure\"], \"C\" : null }, \"S\" : null, \"X\" : null, \"Y\" : null, \"Z\" : null } } }");
171171
}
172172

173173
[Fact]
@@ -239,6 +239,79 @@ public void Any_with_a_multi_conditions_predicate_on_documents()
239239
"{ \"G\" : { \"$elemMatch\" : { \"$or\" : [{ \"D\" : \"Don't\" }, { \"E.F\" : { \"$ne\" : 32 } }] } } }");
240240
}
241241

242+
[Fact]
243+
public void Any_with_advanced_nested_Anys()
244+
{
245+
Assert(
246+
i => i.G.Any(g => g.Y.S.Any(s => s.Z.Any(z => z.C.E.C.X.Any()))),
247+
1,
248+
"{ \"G.Y.S.Z\" : { $elemMatch : { \"C.E.C.X\" : { $ne : null, $not : { $size : 0 } } } } }");
249+
250+
Assert(
251+
i => i.G.Any(g => g.Y.S.Any(s => s.Z.Any(z => z.C.X.Any(x => x.F == 4)))),
252+
1,
253+
"{ \"G.Y.S.Z.C.X\" : { $elemMatch : { \"F\" : 4 } } }");
254+
255+
Assert(
256+
i => i.G.Any(g => g.D == "Don't" && g.S.Any(s => s.Z.Any(x => x.H == 0))),
257+
1,
258+
"{ G : { $elemMatch : { \"D\" : \"Don't\", \"S.Z\" : { $elemMatch : { \"H\" : 0 } } } } }");
259+
260+
Assert(
261+
i => i.G.Any(g => g.D == "Don't" && g.Y.S.Any(s => s.Z.Any(x => x.H == 0))),
262+
1,
263+
"{ G : { $elemMatch : { \"D\" : \"Don't\", \"Y.S.Z\" : { $elemMatch : { \"H\" : 0 } } } } }");
264+
265+
Assert(
266+
i => i.G.Any(g => g.D == "Don't" && g.S.Any(s => s.E == null && s.Z.Any(x => x.H == 0))),
267+
1,
268+
"{ G : { $elemMatch : { \"D\" : \"Don't\", \"S\" : { $elemMatch : { \"E\" : null, \"Z\" : { $elemMatch : { \"H\" : 0 } } } } } } }");
269+
270+
Assert(
271+
i => i.G.Any(g => g.D == "Don't" && g.Y.S.Any(s => s.E == null && s.Z.Any(x => x.H == 0))),
272+
1,
273+
"{ G : { $elemMatch : { \"D\" : \"Don't\", \"Y.S\" : { $elemMatch : { \"E\" : null, \"Z\" : { $elemMatch : { \"H\" : 0 } } } } } } }");
274+
275+
Assert(
276+
i => i.G.Any(g => g.D == "Don't" && g.Y.S.Any(s => s.E == null && s.Z.Any(z => z.C.X.Any(x => x.F == 4)))),
277+
1,
278+
"{ G: { $elemMatch : { \"D\" : \"Don't\", \"Y.S\" : { $elemMatch : { \"E\" : null, \"Z.C.X\" : { $elemMatch : { \"F\" : 4 } } } } } } }");
279+
280+
Assert(
281+
i => i.G.Any(g => g.D == "Don't" && g.Y.S.Any(s => s.E == null && s.Z.Any(z => z.C.X.Any(x => x.F == 4 && x.H == 0)))),
282+
1,
283+
"{ G : { $elemMatch : { \"D\" : \"Don't\", \"Y.S\" : { $elemMatch : { \"E\" : null, \"Z.C.X\" : { $elemMatch : { \"F\" : 4, \"H\" : 0 } } } } } } }");
284+
285+
Assert(
286+
i => i.G.Any(g => g.D == "Don't" && g.Y.S.Any(s => s.E == null && s.Z.Any(z => z.F == 1 && z.C.X.Any(x => x.F == 4 && x.H == 0)))),
287+
1,
288+
"{ G : { $elemMatch : { \"D\" : \"Don't\", \"Y.S\" : { $elemMatch : { \"E\" : null, \"Z\" : { $elemMatch : { \"F\" : 1, \"C.X\" : { $elemMatch : { \"F\" : 4, \"H\" : 0 } } } } } } } } }");
289+
290+
Assert(
291+
i => i.G.Any(
292+
g => g.D == "Don't" &&
293+
g.Y.S.Any(s => s.Z.Any(z => z.C.X.Any(x => x.F == 4))) &&
294+
g.S.Any(s => s.D == "Delilah" && s.Z.Any(z => z.F == 1 && z.H == 0))),
295+
1,
296+
@"{ G : { $elemMatch : {
297+
""D"" : ""Don't"",
298+
""Y.S.Z.C.X"" : { $elemMatch : { ""F"" : 4 } },
299+
""S"" : { $elemMatch : { ""D"" : ""Delilah"", ""Z"" : { $elemMatch : { ""F"" : 1, ""H"" : 0 } } } }
300+
} } }");
301+
302+
Assert(
303+
i => i.G.Any(
304+
g => g.D == "Don't" &&
305+
g.Y.S.Any(s => s.E == null && s.Z.Any(z => z.F == 1 && z.C.X.Any(x => x.F == 4 && x.H == 0))) &&
306+
g.S.Any(s => s.D == "Delilah" && s.Z.Any(z => z.F == 1 && z.H == 0))),
307+
1,
308+
@"{ G : { $elemMatch : {
309+
""D"" : ""Don't"",
310+
""Y.S"" : { $elemMatch : { ""E"" : null, ""Z"" : { $elemMatch : { ""F"" : 1, ""C.X"" : { $elemMatch : { ""F"" : 4, ""H"" : 0 } } } } } },
311+
""S"" : { $elemMatch : { ""D"" : ""Delilah"", ""Z"" : { $elemMatch : { ""F"" : 1, ""H"" : 0 } } } }
312+
} } }");
313+
}
314+
242315
[Fact]
243316
public void Any_with_a_nested_Any()
244317
{
@@ -635,7 +708,7 @@ public void ClassEquals()
635708
Assert(
636709
x => x.C == new C { D = "Dexter" },
637710
0,
638-
"{ C : { Ids : null, D : 'Dexter', E : null, S : null, X : null } }");
711+
"{ C : { Ids : null, D : 'Dexter', E : null, S : null, X : null, Y : null, Z : null } }");
639712
}
640713

641714
[Fact]
@@ -644,7 +717,7 @@ public void ClassEqualsMethod()
644717
Assert(
645718
x => x.C.Equals(new C { D = "Dexter" }),
646719
0,
647-
"{ C : { Ids : null, D : 'Dexter', E : null, S : null, X : null } }");
720+
"{ C : { Ids : null, D : 'Dexter', E : null, S : null, X : null, Y : null, Z : null } }");
648721
}
649722

650723
[Fact]
@@ -653,7 +726,7 @@ public void ClassNotEquals()
653726
Assert(
654727
x => x.C != new C { D = "Dexter" },
655728
2,
656-
"{ C : { $ne : { Ids : null, D : 'Dexter', E : null, S : null, X : null } } }");
729+
"{ C : { $ne : { Ids : null, D : 'Dexter', E : null, S : null, X : null, Y : null, Z : null } } }");
657730
}
658731

659732
[Fact]

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ public void Value_conversion_with_custom_type_converter()
4646
Assert(
4747
x => x.C == objectToConvert,
4848
0,
49-
"{ C : { Ids : null, D : 'Dexter', E : null, S : null, X : null } }");
49+
"{ C : { Ids : null, D : 'Dexter', E : null, S : null, X : null, Y : null, Z : null } }");
5050
}
5151

5252
public void Assert(Expression<Func<Root, bool>> filter, int expectedCount, string expectedFilter)

0 commit comments

Comments
 (0)