Skip to content

Commit dd98794

Browse files
committed
CSHARP-1432: added support for $all in predicates.
1 parent 823e44c commit dd98794

File tree

3 files changed

+148
-6
lines changed

3 files changed

+148
-6
lines changed

Docs/reference/content/reference/driver/expressions.md

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,21 @@ Find(p => localAges.Contains(p.Age));
114114
{ Age: { $in: [10, 20, 30] } }
115115
```
116116

117+
```csharp
118+
int[] localNames = new [] { "Fluffy", "Scruffy" };
119+
Find(p => p.Pets.Any(i => localNames.Contains(i.Name));
120+
```
121+
```json
122+
{ "Pets.Name": { $in: ["Fluffy", "Scruffy"] } }
123+
```
124+
125+
```csharp
126+
int[] localNumbers = new [] { 30, 40 };
127+
Find(p => localNumbers.Any(i => p.FavoriteNumbers.Contains(i));
128+
```
129+
```json
130+
{ FavoriteNumbers: { $in: [30, 40] } }
131+
117132
#### $nin
118133

119134
```csharp
@@ -247,14 +262,18 @@ See the [MongoDB documentation]({{< docsref "reference/operator/query/#geospatia
247262
// no example yet
248263
```
249264

250-
### Geospatial
265+
### Array
251266

252267
See the [MongoDB documentation]({{< docsref "reference/operator/query/#array" >}}) for more information on each operator.
253268

254269
#### $all
255270

256271
```csharp
257-
// no example yet
272+
var local = new [] { 10, 20 };
273+
Find(x => local.All(i => FavoriteNumbers.Contains(i));
274+
```
275+
```json
276+
{ FavoriteNumbers: { $all: [10, 20] } }
258277
```
259278

260279
#### $elemMatch
@@ -270,7 +289,7 @@ Find(x => x.Pets.Any(p => p.Name == "Fluffy" && p.Age > 21);
270289
Find(x => x.FavoriteNumbers.Any(n => n < 42 && n > 21));
271290
```
272291
```json
273-
{ FavoriteNumbers: { $elemMatch: { { $lt: 42, $gt: 21 } } } }
292+
{ FavoriteNumbers: { $elemMatch: { $lt: 42, $gt: 21 } } }
274293
```
275294

276295
{{% note %}}Depending on the complexity and the operators involved in the Any method call, the driver might eliminate the $elemMatch completely. For instance,

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

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,28 @@ namespace MongoDB.Driver.Tests.Linq.Translators
3131
[TestFixture]
3232
public class PredicateTranslatorTests : IntegrationTestBase
3333
{
34+
[Test]
35+
public void All()
36+
{
37+
var local = new[] { "itchy" };
38+
39+
Assert(
40+
x => local.All(i => x.C.E.I.Contains(i)),
41+
1,
42+
"{'C.E.I': { $all: [ 'itchy' ] } }");
43+
}
44+
45+
[Test]
46+
public void All_with_a_not()
47+
{
48+
var local = new[] { "itchy" };
49+
50+
Assert(
51+
x => !local.All(i => x.C.E.I.Contains(i)),
52+
1,
53+
"{'C.E.I': { $not: { $all: [ 'itchy' ] } } }");
54+
}
55+
3456
[Test]
3557
public void Any_without_a_predicate()
3658
{
@@ -122,7 +144,7 @@ public void Any_with_a_predicate_on_scalars()
122144
}
123145

124146
[Test]
125-
public void Any_with_local_contains()
147+
public void Any_with_local_contains_on_an_embedded_document()
126148
{
127149
var local = new List<string> { "Delilah", "Dolphin" };
128150

@@ -132,6 +154,17 @@ public void Any_with_local_contains()
132154
"{\"G.D\": { $in: [\"Delilah\", \"Dolphin\" ] } }");
133155
}
134156

157+
[Test]
158+
public void Any_with_local_contains_on_a_scalar_array()
159+
{
160+
var local = new List<string> { "itchy" };
161+
162+
Assert(
163+
x => local.Any(i => x.C.E.I.Contains(i)),
164+
1,
165+
"{\"C.E.I\": { $in: [\"itchy\" ] } }");
166+
}
167+
135168
[Test]
136169
public void LocalIListContains()
137170
{

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

Lines changed: 92 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -750,11 +750,14 @@ private FilterDefinition<BsonDocument> TranslateOr(BinaryExpression binaryExpres
750750

751751
private FilterDefinition<BsonDocument> TranslatePipeline(PipelineExpression node)
752752
{
753+
if (node.ResultOperator is AllResultOperator)
754+
{
755+
return TranslatePipelineAll(node);
756+
}
753757
if (node.ResultOperator is AnyResultOperator)
754758
{
755759
return TranslatePipelineAny(node);
756760
}
757-
758761
if (node.ResultOperator is ContainsResultOperator)
759762
{
760763
return TranslatePipelineContains(node);
@@ -763,6 +766,48 @@ private FilterDefinition<BsonDocument> TranslatePipeline(PipelineExpression node
763766
return null;
764767
}
765768

769+
private FilterDefinition<BsonDocument> TranslatePipelineAll(PipelineExpression node)
770+
{
771+
var whereExpression = node.Source as WhereExpression;
772+
if (whereExpression == null)
773+
{
774+
return null;
775+
}
776+
777+
var constant = whereExpression.Source as ConstantExpression;
778+
if (constant == null)
779+
{
780+
return null;
781+
}
782+
783+
var embeddedPipeline = whereExpression.Predicate as PipelineExpression;
784+
if (!(embeddedPipeline?.ResultOperator is ContainsResultOperator))
785+
{
786+
return null;
787+
}
788+
789+
var fieldExpression = embeddedPipeline.Source as IFieldExpression;
790+
if (fieldExpression == null)
791+
{
792+
return null;
793+
}
794+
795+
var arraySerializer = fieldExpression.Serializer as IBsonArraySerializer;
796+
if (arraySerializer == null)
797+
{
798+
return null;
799+
}
800+
801+
BsonSerializationInfo itemSerializationInfo;
802+
if (!arraySerializer.TryGetItemSerializationInfo(out itemSerializationInfo))
803+
{
804+
return null;
805+
}
806+
807+
var serializedValues = itemSerializationInfo.SerializeValues((IEnumerable)constant.Value);
808+
return __builder.All(fieldExpression.FieldName, serializedValues);
809+
}
810+
766811
private FilterDefinition<BsonDocument> TranslatePipelineAny(PipelineExpression node)
767812
{
768813
var fieldExpression = node.Source as IFieldExpression;
@@ -773,7 +818,6 @@ private FilterDefinition<BsonDocument> TranslatePipelineAny(PipelineExpression n
773818
__builder.Not(__builder.Size(fieldExpression.FieldName, 0)));
774819
}
775820

776-
777821
var whereExpression = node.Source as WhereExpression;
778822
if (whereExpression == null)
779823
{
@@ -783,6 +827,10 @@ private FilterDefinition<BsonDocument> TranslatePipelineAny(PipelineExpression n
783827
fieldExpression = whereExpression.Source as IFieldExpression;
784828
if (fieldExpression == null)
785829
{
830+
if (whereExpression.Source is ConstantExpression)
831+
{
832+
return TranslatePipelineAnyScalar(node);
833+
}
786834
return null;
787835
}
788836

@@ -807,6 +855,48 @@ private FilterDefinition<BsonDocument> TranslatePipelineAny(PipelineExpression n
807855
return filter;
808856
}
809857

858+
private FilterDefinition<BsonDocument> TranslatePipelineAnyScalar(PipelineExpression node)
859+
{
860+
var whereExpression = node.Source as WhereExpression;
861+
if (whereExpression == null)
862+
{
863+
return null;
864+
}
865+
866+
var constant = whereExpression.Source as ConstantExpression;
867+
if (constant == null)
868+
{
869+
return null;
870+
}
871+
872+
var embeddedPipeline = whereExpression.Predicate as PipelineExpression;
873+
if (!(embeddedPipeline?.ResultOperator is ContainsResultOperator))
874+
{
875+
return null;
876+
}
877+
878+
var fieldExpression = embeddedPipeline.Source as IFieldExpression;
879+
if (fieldExpression == null)
880+
{
881+
return null;
882+
}
883+
884+
var arraySerializer = fieldExpression.Serializer as IBsonArraySerializer;
885+
if (arraySerializer == null)
886+
{
887+
return null;
888+
}
889+
890+
BsonSerializationInfo itemSerializationInfo;
891+
if (!arraySerializer.TryGetItemSerializationInfo(out itemSerializationInfo))
892+
{
893+
return null;
894+
}
895+
896+
var serializedValues = itemSerializationInfo.SerializeValues((IEnumerable)constant.Value);
897+
return __builder.In(fieldExpression.FieldName, serializedValues);
898+
}
899+
810900
private FilterDefinition<BsonDocument> TranslatePipelineContains(PipelineExpression node)
811901
{
812902
var value = ((ContainsResultOperator)node.ResultOperator).Value;

0 commit comments

Comments
 (0)