Skip to content

Commit 8f3b30c

Browse files
committed
added support for elemMatch queries when array elements are documents.
1 parent 0f11e78 commit 8f3b30c

File tree

2 files changed

+80
-15
lines changed

2 files changed

+80
-15
lines changed

Driver/Linq/Translators/SelectQuery.cs

Lines changed: 37 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ namespace MongoDB.Driver.Linq
3636
public class SelectQuery : TranslatedQuery
3737
{
3838
// private fields
39+
private readonly Dictionary<ParameterExpression, IBsonSerializer> _parameterSerializers;
3940
private LambdaExpression _where;
4041
private Type _ofType;
4142
private List<OrderByClause> _orderBy;
@@ -54,6 +55,7 @@ public class SelectQuery : TranslatedQuery
5455
public SelectQuery(MongoCollection collection, Type documentType)
5556
: base(collection, documentType)
5657
{
58+
_parameterSerializers = new Dictionary<ParameterExpression, IBsonSerializer>();
5759
}
5860

5961
// public properties
@@ -247,19 +249,41 @@ private IMongoQuery BuildAnyQuery(MethodCallExpression methodCallExpression)
247249
if (methodCallExpression.Method.DeclaringType == typeof(Enumerable))
248250
{
249251
var arguments = methodCallExpression.Arguments.ToArray();
252+
var serializationInfo = GetSerializationInfo(arguments[0]);
253+
var itemSerializerProvider = serializationInfo.Serializer as IBsonItemSerializationInfoProvider;
254+
if (itemSerializerProvider == null)
255+
{
256+
return null;
257+
}
258+
259+
var itemSerializationInfo = itemSerializerProvider.GetItemSerializationInfo();
260+
if (itemSerializationInfo == null)
261+
{
262+
return null;
263+
}
264+
250265
if (arguments.Length == 1)
251266
{
252-
var serializationInfo = GetSerializationInfo(arguments[0]);
253-
if (serializationInfo != null)
254-
{
255-
return Query.And(
256-
Query.NE(serializationInfo.ElementName, BsonNull.Value),
257-
Query.Not(serializationInfo.ElementName).Size(0));
258-
}
267+
return Query.And(
268+
Query.NE(serializationInfo.ElementName, BsonNull.Value),
269+
Query.Not(serializationInfo.ElementName).Size(0));
259270
}
260271
else if (arguments.Length == 2)
261272
{
262-
throw new NotSupportedException("Enumerable.Any with a predicate is not supported.");
273+
if (!(itemSerializationInfo.Serializer is IBsonMemberSerializationInfoProvider))
274+
{
275+
var message = string.Format("Any is only support for items that serialize into documents. The current serializer is {0} and must implement {1} for participation in Any queries.",
276+
BsonUtils.GetFriendlyTypeName(itemSerializationInfo.GetType()),
277+
BsonUtils.GetFriendlyTypeName(typeof(IBsonMemberSerializationInfoProvider)));
278+
throw new NotSupportedException(message);
279+
}
280+
var itemSerializer = itemSerializationInfo.Serializer;
281+
var lambda = (LambdaExpression)arguments[1];
282+
_parameterSerializers[lambda.Parameters[0]] = itemSerializer;
283+
var query = BuildQuery(lambda.Body);
284+
query = Query.ElemMatch(serializationInfo.ElementName, query);
285+
_parameterSerializers.Remove(lambda.Parameters[0]);
286+
return query;
263287
}
264288
}
265289
return null;
@@ -1481,7 +1505,9 @@ private BsonSerializationInfo GetSerializationInfo(Expression expression)
14811505
var parameterExpression = expression as ParameterExpression;
14821506
if (parameterExpression != null)
14831507
{
1484-
var serializer = BsonSerializer.LookupSerializer(parameterExpression.Type);
1508+
IBsonSerializer serializer;
1509+
if(!_parameterSerializers.TryGetValue(parameterExpression, out serializer))
1510+
serializer = BsonSerializer.LookupSerializer(parameterExpression.Type);
14851511
return new BsonSerializationInfo(
14861512
null, // elementName
14871513
serializer,
@@ -1493,8 +1519,8 @@ private BsonSerializationInfo GetSerializationInfo(Expression expression)
14931519
parameterExpression = ExpressionParameterFinder.FindParameter(expression);
14941520
if (parameterExpression != null)
14951521
{
1496-
var serializer = BsonSerializer.LookupSerializer(parameterExpression.Type);
1497-
return GetSerializationInfo(serializer, expression);
1522+
var info = GetSerializationInfo(parameterExpression);
1523+
return GetSerializationInfo(info.Serializer, expression);
14981524
}
14991525

15001526
return null;

DriverUnitTests/Linq/SelectQueryTests.cs

Lines changed: 43 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ public class C
4848
public int Y { get; set; }
4949
[BsonElement("d")]
5050
public D D { get; set; }
51+
[BsonElement("da")]
52+
public List<D> DA { get; set; }
5153
[BsonElement("s")]
5254
[BsonIgnoreIfNull]
5355
public string S { get; set; }
@@ -149,11 +151,11 @@ public void Setup()
149151

150152
// documents inserted deliberately out of order to test sorting
151153
_collection.Drop();
152-
_collection.Insert(new C { Id = _id2, X = 2, Y = 11, D = new D { Z = 22 }, A = new [] { 2, 3, 4 }, L = new List<int> { 2, 3, 4 } });
154+
_collection.Insert(new C { Id = _id2, X = 2, Y = 11, D = new D { Z = 22 }, A = new [] { 2, 3, 4 }, DA = new List<D> { new D { Z = 111 }, new D { Z = 222 } }, L = new List<int> { 2, 3, 4 } });
153155
_collection.Insert(new C { Id = _id1, X = 1, Y = 11, D = new D { Z = 11 }, S = "abc", SA = new string[] { "Tom", "Dick", "Harry" } });
154156
_collection.Insert(new C { Id = _id3, X = 3, Y = 33, D = new D { Z = 33 }, B = true, BA = new bool[] { true }, E = E.A, EA = new E[] { E.A, E.B } });
155157
_collection.Insert(new C { Id = _id5, X = 5, Y = 44, D = new D { Z = 55 }, DBRef = new MongoDBRef("db", "c", 1) });
156-
_collection.Insert(new C { Id = _id4, X = 4, Y = 44, D = new D { Z = 44 }, S = " xyz " });
158+
_collection.Insert(new C { Id = _id4, X = 4, Y = 44, D = new D { Z = 44 }, S = " xyz ", DA = new List<D> { new D { Z = 333 }, new D { Z = 444 } } });
157159
}
158160

159161
[Test]
@@ -1939,13 +1941,27 @@ where c.A.Any()
19391941
}
19401942

19411943
[Test]
1942-
[ExpectedException(typeof(NotSupportedException), ExpectedMessage = "Enumerable.Any with a predicate is not supported.")]
1944+
[ExpectedException(typeof(NotSupportedException), ExpectedMessage = "Any is only support for items that serialize into documents. The current serializer is BsonSerializationInfo and must implement IBsonMemberSerializationInfoProvider for participation in Any queries.")]
19431945
public void TestWhereAAnyWithPredicate()
19441946
{
19451947
var query = from c in _collection.AsQueryable<C>()
19461948
where c.A.Any(a => a > 3)
19471949
select c;
1948-
query.ToList(); // execute query
1950+
1951+
var translatedQuery = MongoQueryTranslator.Translate(query);
1952+
Assert.IsInstanceOf<SelectQuery>(translatedQuery);
1953+
Assert.AreSame(_collection, translatedQuery.Collection);
1954+
Assert.AreSame(typeof(C), translatedQuery.DocumentType);
1955+
1956+
var selectQuery = (SelectQuery)translatedQuery;
1957+
Assert.AreEqual("(C c) => Enumerable.Any<Int32>(c.A, (Int32 a) => (a > 3))", ExpressionFormatter.ToString(selectQuery.Where));
1958+
Assert.IsNull(selectQuery.OrderBy);
1959+
Assert.IsNull(selectQuery.Projection);
1960+
Assert.IsNull(selectQuery.Skip);
1961+
Assert.IsNull(selectQuery.Take);
1962+
1963+
Assert.AreEqual("{ \"a\" : 2 }", selectQuery.BuildQuery().ToJson());
1964+
Assert.AreEqual(1, Consume(query));
19491965
}
19501966

19511967
[Test]
@@ -3033,6 +3049,29 @@ public void TestWhereDNotEquals11Not()
30333049
Assert.AreEqual(1, Consume(query));
30343050
}
30353051

3052+
[Test]
3053+
public void TestWhereDAAnyWithPredicate()
3054+
{
3055+
var query = from c in _collection.AsQueryable<C>()
3056+
where c.DA.Any(x => x.Z == 333)
3057+
select c;
3058+
3059+
var translatedQuery = MongoQueryTranslator.Translate(query);
3060+
Assert.IsInstanceOf<SelectQuery>(translatedQuery);
3061+
Assert.AreSame(_collection, translatedQuery.Collection);
3062+
Assert.AreSame(typeof(C), translatedQuery.DocumentType);
3063+
3064+
var selectQuery = (SelectQuery)translatedQuery;
3065+
Assert.AreEqual("(C c) => Enumerable.Any<D>(c.DA, (D x) => (x.Z == 333))", ExpressionFormatter.ToString(selectQuery.Where));
3066+
Assert.IsNull(selectQuery.OrderBy);
3067+
Assert.IsNull(selectQuery.Projection);
3068+
Assert.IsNull(selectQuery.Skip);
3069+
Assert.IsNull(selectQuery.Take);
3070+
3071+
Assert.AreEqual("{ \"da\" : { \"$elemMatch\" : { \"z\" : 333 } } }", selectQuery.BuildQuery().ToJson());
3072+
Assert.AreEqual(1, Consume(query));
3073+
}
3074+
30363075
[Test]
30373076
public void TestWhereEAContainsAll()
30383077
{

0 commit comments

Comments
 (0)