Skip to content

Commit ab31af9

Browse files
committed
CSHARP-5427: Support Mql methods in filters when possible.
1 parent d19790d commit ab31af9

File tree

4 files changed

+194
-0
lines changed

4 files changed

+194
-0
lines changed

src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToFilterTranslators/MethodTranslators/ExistsMethodToFilterTranslator.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
using MongoDB.Driver.Linq.Linq3Implementation.Ast.Filters;
1818
using MongoDB.Driver.Linq.Linq3Implementation.Misc;
1919
using MongoDB.Driver.Linq.Linq3Implementation.Reflection;
20+
using MongoDB.Driver.Linq.Linq3Implementation.Translators.ExpressionToFilterTranslators.ToFilterFieldTranslators;
2021

2122
namespace MongoDB.Driver.Linq.Linq3Implementation.Translators.ExpressionToFilterTranslators.MethodTranslators
2223
{
@@ -25,12 +26,20 @@ internal static class ExistsMethodToFilterTranslator
2526
public static AstFilter Translate(TranslationContext context, MethodCallExpression expression)
2627
{
2728
var method = expression.Method;
29+
var arguments = expression.Arguments;
2830

2931
if (method.Is(ArrayMethod.Exists) || ListMethod.IsExistsMethod(method))
3032
{
3133
return AllOrAnyMethodToFilterTranslator.Translate(context, expression);
3234
}
3335

36+
if (method.Is(MqlMethod.Exists))
37+
{
38+
var fieldExpression = arguments[0];
39+
var field = ExpressionToFilterFieldTranslator.Translate(context, fieldExpression);
40+
return AstFilter.Exists(field);
41+
}
42+
3443
throw new ExpressionNotSupportedException(expression);
3544
}
3645
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/* Copyright 2010-present MongoDB Inc.
2+
*
3+
* Licensed under the Apache License, Version 2.0 (the "License");
4+
* you may not use this file except in compliance with the License.
5+
* You may obtain a copy of the License at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software
10+
* distributed under the License is distributed on an "AS IS" BASIS,
11+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
* See the License for the specific language governing permissions and
13+
* limitations under the License.
14+
*/
15+
16+
using System.Linq.Expressions;
17+
using MongoDB.Bson;
18+
using MongoDB.Driver.Linq.Linq3Implementation.Ast.Filters;
19+
using MongoDB.Driver.Linq.Linq3Implementation.Misc;
20+
using MongoDB.Driver.Linq.Linq3Implementation.Reflection;
21+
using MongoDB.Driver.Linq.Linq3Implementation.Translators.ExpressionToFilterTranslators.ToFilterFieldTranslators;
22+
23+
namespace MongoDB.Driver.Linq.Linq3Implementation.Translators.ExpressionToFilterTranslators.MethodTranslators
24+
{
25+
internal static class IsMissingMethodToFilterTranslator
26+
{
27+
public static AstFilter Translate(TranslationContext context, MethodCallExpression expression)
28+
{
29+
var method = expression.Method;
30+
var arguments = expression.Arguments;
31+
32+
if (method.IsOneOf(MqlMethod.IsMissing, MqlMethod.IsNullOrMissing))
33+
{
34+
var fieldExpression = arguments[0];
35+
var field = ExpressionToFilterFieldTranslator.Translate(context, fieldExpression);
36+
37+
return method switch
38+
{
39+
_ when method.Is(MqlMethod.IsMissing) => AstFilter.NotExists(field),
40+
_ when method.Is(MqlMethod.IsNullOrMissing) => AstFilter.Eq(field, BsonNull.Value), // matches missing fields also
41+
_ => throw new ExpressionNotSupportedException(expression)
42+
};
43+
}
44+
45+
throw new ExpressionNotSupportedException(expression);
46+
}
47+
}
48+
}

src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToFilterTranslators/MethodTranslators/MethodCallExpressionToFilterTranslator.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,10 @@ public static AstFilter Translate(TranslationContext context, MethodCallExpressi
4646
case "StringIn":
4747
case "StringNin":
4848
return StringInOrNinMethodToFilterTranslator.Translate(context, expression);
49+
50+
case "IsMissing":
51+
case "IsNullOrMissing":
52+
return IsMissingMethodToFilterTranslator.Translate(context, expression);
4953
}
5054

5155
throw new ExpressionNotSupportedException(expression);
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
/* Copyright 2010-present MongoDB Inc.
2+
*
3+
* Licensed under the Apache License, Version 2.0 (the "License");
4+
* you may not use this file except in compliance with the License.
5+
* You may obtain a copy of the License at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software
10+
* distributed under the License is distributed on an "AS IS" BASIS,
11+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
* See the License for the specific language governing permissions and
13+
* limitations under the License.
14+
*/
15+
16+
using System.Linq;
17+
using FluentAssertions;
18+
using MongoDB.Bson;
19+
using MongoDB.Bson.Serialization.Serializers;
20+
using Xunit;
21+
22+
namespace MongoDB.Driver.Tests.Linq.Linq3Implementation.Jira
23+
{
24+
public class CSharp5427Tests : Linq3IntegrationTest
25+
{
26+
[Fact]
27+
public void Where_Mql_Exists_with_property_should_work()
28+
{
29+
var collection = GetCollection();
30+
31+
var queryable = collection.AsQueryable()
32+
.Where(x => Mql.Exists(x.X));
33+
34+
var stages = Translate(collection, queryable);
35+
AssertStages(stages, "{ $match : { X : { $exists : true } } }");
36+
37+
var results = queryable.ToList();
38+
results.Select(x => x.Id).Should().Equal(2, 3);
39+
}
40+
41+
[Fact]
42+
public void Where_Mql_Exists_with_shadow_property_should_work()
43+
{
44+
var collection = GetCollection();
45+
46+
var queryable = collection.AsQueryable()
47+
.Where(x => Mql.Exists(Mql.Field(x, "X", new NullableSerializer<int>(Int32Serializer.Instance))));
48+
49+
var stages = Translate(collection, queryable);
50+
AssertStages(stages, "{ $match : { X : { $exists : true } } }");
51+
52+
var results = queryable.ToList();
53+
results.Select(x => x.Id).Should().Equal(2, 3);
54+
}
55+
56+
[Fact]
57+
public void Where_Mql_IsMissing_with_property_should_work()
58+
{
59+
var collection = GetCollection();
60+
61+
var queryable = collection.AsQueryable()
62+
.Where(x => Mql.IsMissing(x.X));
63+
64+
var stages = Translate(collection, queryable);
65+
AssertStages(stages, "{ $match : { X : { $exists : false } } }");
66+
67+
var results = queryable.ToList();
68+
results.Select(x => x.Id).Should().Equal(1);
69+
}
70+
71+
[Fact]
72+
public void Where_Mql_IsMissing_with_shadow_property_should_work()
73+
{
74+
var collection = GetCollection();
75+
76+
var queryable = collection.AsQueryable()
77+
.Where(x => Mql.IsMissing(Mql.Field(x, "X", new NullableSerializer<int>(Int32Serializer.Instance))));
78+
79+
var stages = Translate(collection, queryable);
80+
AssertStages(stages, "{ $match : { X : { $exists : false } } }");
81+
82+
var results = queryable.ToList();
83+
results.Select(x => x.Id).Should().Equal(1);
84+
}
85+
86+
[Fact]
87+
public void Where_Mql_IsNullOrMissing_with_property_should_work()
88+
{
89+
var collection = GetCollection();
90+
91+
var queryable = collection.AsQueryable()
92+
.Where(x => Mql.IsNullOrMissing(x.X));
93+
94+
var stages = Translate(collection, queryable);
95+
AssertStages(stages, "{ $match : { X : null } }");
96+
97+
var results = queryable.ToList();
98+
results.Select(x => x.Id).Should().Equal(1, 2);
99+
}
100+
101+
[Fact]
102+
public void Where_Mql_IsNullOrMissing_with_shadow_property_should_work()
103+
{
104+
var collection = GetCollection();
105+
106+
var queryable = collection.AsQueryable()
107+
.Where(x => Mql.IsNullOrMissing(Mql.Field(x, "X", new NullableSerializer<int>(Int32Serializer.Instance))));
108+
109+
var stages = Translate(collection, queryable);
110+
AssertStages(stages, "{ $match : { X : null } }");
111+
112+
var results = queryable.ToList();
113+
results.Select(x => x.Id).Should().Equal(1, 2);
114+
}
115+
116+
private IMongoCollection<C> GetCollection()
117+
{
118+
var collection = GetCollection<C>("test");
119+
CreateCollection(
120+
collection.Database.GetCollection<BsonDocument>("test"),
121+
BsonDocument.Parse("{ _id : 1 }"),
122+
BsonDocument.Parse("{ _id : 2, X : null }"),
123+
BsonDocument.Parse("{ _id : 3, X : 3 }"));
124+
return collection;
125+
}
126+
127+
private class C
128+
{
129+
public int Id { get; set; }
130+
public int? X { get; set; }
131+
}
132+
}
133+
}

0 commit comments

Comments
 (0)