Skip to content

Commit fcbc397

Browse files
committed
CSHARP-3435: FilterDefinition Inject method should use root serializer
1 parent 1713173 commit fcbc397

File tree

2 files changed

+126
-3
lines changed

2 files changed

+126
-3
lines changed

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

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,12 @@
1313
* limitations under the License.
1414
*/
1515

16+
using System.Linq;
1617
using System.Linq.Expressions;
1718
using System.Reflection;
1819
using MongoDB.Bson;
1920
using MongoDB.Bson.Serialization;
21+
using MongoDB.Driver.Linq.Linq3Implementation.Ast.Expressions;
2022
using MongoDB.Driver.Linq.Linq3Implementation.Ast.Filters;
2123
using MongoDB.Driver.Linq.Linq3Implementation.ExtensionMethods;
2224
using MongoDB.Driver.Linq.Linq3Implementation.Misc;
@@ -44,12 +46,21 @@ public static AstFilter Translate(TranslationContext context, MethodCallExpressi
4446
var filterExpression = arguments[0];
4547
var filterDefinition = filterExpression.GetConstantValue<object>(expression);
4648
var filterDefinitionType = filterDefinition.GetType(); // we KNOW it's a FilterDefinition<TDocument> because of the Inject method signature
47-
var documentType = filterDefinitionType.GetGenericArguments()[0];
49+
var filterDefinitionDocumentType = filterDefinitionType.GetGenericArguments()[0];
4850

51+
var rootSymbol = context.SymbolTable.Symbols.SingleOrDefault(s => s.Ast.IsRootVar());
52+
if (rootSymbol == null)
53+
{
54+
throw new ExpressionNotSupportedException(expression, because: "there is no current root symbol");
55+
}
56+
var documentSerializer = rootSymbol.Serializer;
57+
if (filterDefinitionDocumentType != documentSerializer.ValueType)
58+
{
59+
throw new ExpressionNotSupportedException(expression, because: $"FilterDefinition TDocument type: {filterDefinitionDocumentType} does not match document type {documentSerializer.ValueType} ");
60+
}
4961
var serializerRegistry = BsonSerializer.SerializerRegistry;
50-
var documentSerializer = serializerRegistry.GetSerializer(documentType); // TODO: is this the right serializer?
5162

52-
var renderFilterMethod = __renderFilterMethodInfo.MakeGenericMethod(documentType);
63+
var renderFilterMethod = __renderFilterMethodInfo.MakeGenericMethod(filterDefinitionDocumentType);
5364
var renderedFilter = (BsonDocument)renderFilterMethod.Invoke(null, new[] { filterDefinition, documentSerializer, serializerRegistry, context.TranslationOptions });
5465

5566
return AstFilter.Raw(renderedFilter);
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
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 MongoDB.Driver.Linq;
18+
using Xunit;
19+
20+
namespace MongoDB.Driver.Tests.Linq.Linq3Implementation.Jira;
21+
22+
public class CSharp3435Tests : LinqIntegrationTest<CSharp3435Tests.ClassFixture>
23+
{
24+
public CSharp3435Tests(ClassFixture fixture)
25+
: base(fixture)
26+
{
27+
}
28+
29+
[Fact]
30+
public void Where_should_work()
31+
{
32+
var queryable = CreateQueryable()
33+
.Where(x => x.NormalizedUsername == "PAPLABROS");
34+
35+
var stages = Translate(Fixture.UserClaimCollection, queryable);
36+
AssertStages(
37+
stages,
38+
"{ $project : { _outer : '$$ROOT', _id : 0 } }",
39+
"{ $lookup : { from : 'Users', localField : '_outer.UserId', foreignField : '_id', as : '_inner' } }",
40+
"{ $project : { claim : '$_outer', users : '$_inner', _id : 0 } }",
41+
"{ $match : { 'claim.ClaimType' : 'Moderator' } }",
42+
"{ $project : { _v : { $arrayElemAt : ['$users', 0] }, _id : 0 } }",
43+
"{ $match : { '_v.NormalizedUsername' : 'PAPLABROS' } }");
44+
}
45+
46+
[Fact]
47+
public void Where_with_Inject_should_work()
48+
{
49+
var filter = Builders<User>.Filter.Eq(x => x.NormalizedUsername, "PAPLABROS");
50+
var queryable = CreateQueryable()
51+
.Where(x => filter.Inject());
52+
53+
var stages = Translate(Fixture.UserClaimCollection, queryable);
54+
AssertStages(
55+
stages,
56+
"{ $project : { _outer : '$$ROOT', _id : 0 } }",
57+
"{ $lookup : { from : 'Users', localField : '_outer.UserId', foreignField : '_id', as : '_inner' } }",
58+
"{ $project : { claim : '$_outer', users : '$_inner', _id : 0 } }",
59+
"{ $match : { 'claim.ClaimType' : 'Moderator' } }",
60+
"{ $project : { _v : { $arrayElemAt : ['$users', 0] }, _id : 0 } }",
61+
"{ $match : { '_v.NormalizedUsername' : 'PAPLABROS' } }");
62+
}
63+
64+
public IQueryable<User> CreateQueryable()
65+
{
66+
var usersCollection = Fixture.UserCollection;
67+
var userClaimsCollection = Fixture.UserClaimCollection;
68+
69+
var queryable =
70+
from claim in userClaimsCollection.AsQueryable()
71+
join user in usersCollection.AsQueryable() on claim.UserId equals user.Id into users
72+
where claim.ClaimType == "Moderator"
73+
select users.First();
74+
75+
// this is the equivalent method syntax
76+
// var queryable = userClaimsCollection.AsQueryable()
77+
// .GroupJoin(
78+
// usersCollection.AsQueryable(),
79+
// claim => claim.UserId,
80+
// user => user.Id,
81+
// (claim, users) => new { claim, users })
82+
// .Where(x => x.claim.ClaimType == "Moderator")
83+
// .Select(x => x.users.First());
84+
85+
return queryable;
86+
}
87+
88+
public class User
89+
{
90+
public int Id { get; set; }
91+
public string NormalizedUsername { get; set; }
92+
}
93+
94+
public class UserClaim
95+
{
96+
public int Id { get; set; }
97+
public int UserId { get; set; }
98+
public string ClaimType { get; set; }
99+
}
100+
101+
public sealed class ClassFixture : MongoDatabaseFixture
102+
{
103+
public IMongoCollection<User> UserCollection { get; private set; }
104+
public IMongoCollection<UserClaim> UserClaimCollection { get; private set; }
105+
106+
protected override void InitializeFixture()
107+
{
108+
UserCollection = CreateCollection<User>("Users");
109+
UserClaimCollection = CreateCollection<UserClaim>("UserClaims");
110+
}
111+
}
112+
}

0 commit comments

Comments
 (0)