Skip to content

Commit 4255410

Browse files
author
Oleksandr Poliakov
committed
Simplifier
1 parent 04b8f48 commit 4255410

File tree

3 files changed

+69
-31
lines changed

3 files changed

+69
-31
lines changed

src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Optimizers/AstSimplifier.cs

+35
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
*/
1515

1616
using System;
17+
using System.Collections.Generic;
1718
using System.Linq;
1819
using MongoDB.Bson;
1920
using MongoDB.Driver.Linq.Linq3Implementation.Ast.Expressions;
@@ -454,8 +455,42 @@ public override AstNode VisitMapExpression(AstMapExpression node)
454455
}
455456
}
456457

458+
if (node.In is AstComputedDocumentExpression inComputedDocumentExpression &&
459+
inComputedDocumentExpression.Fields.All(f => f.Value is AstGetFieldExpression getFieldExpression && getFieldExpression.Input == node.As && getFieldExpression.CanBeConvertedToFieldPath()))
460+
{
461+
462+
// { $map : { input : { $map : { input : <input>, as : "y", in : { A : "$$y.FieldA" } } }, as: "v", in : { B : '$$v.A' } } } => { $map : { input : <input>, as: "v", in : { B : "$$v.FieldA" } } }
463+
if (node.Input is AstMapExpression inputMapExpression &&
464+
inputMapExpression.In is AstComputedDocumentExpression innerInComputedDocumentExpression)
465+
{
466+
var simplified = AstExpression.Map(
467+
inputMapExpression.Input,
468+
inputMapExpression.As,
469+
AstExpression.ComputedDocument(inComputedDocumentExpression.Fields.Select(f => RemapField(f, node.As.Name, innerInComputedDocumentExpression.Fields))));
470+
471+
return Visit(simplified);
472+
}
473+
474+
// { $map : { input : [{ A: "$$ROOT.FieldA" }], as : "v", in: { B : "$$v.A" } } } => [{ B : "$FieldA }]
475+
if (node.Input is AstComputedArrayExpression inputArrayExpression &&
476+
inputArrayExpression.Items.All(i => i is AstComputedDocumentExpression))
477+
{
478+
var simplified = AstExpression.ComputedArray(inputArrayExpression.Items.Select(i =>
479+
AstExpression.ComputedDocument(inComputedDocumentExpression.Fields.Select(f => RemapField(f, node.As.Name, ((AstComputedDocumentExpression)i).Fields)))));
480+
return Visit(simplified);
481+
}
482+
}
483+
457484
return base.VisitMapExpression(node);
458485

486+
static AstComputedField RemapField(AstComputedField field, string @as, IEnumerable<AstComputedField> innerFields)
487+
{
488+
var fieldPath = ((AstGetFieldExpression)field.Value).ConvertToFieldPath().Replace($"$${@as}.", string.Empty);
489+
var innerField = innerFields.Single(f => f.Path == fieldPath);
490+
491+
return AstExpression.ComputedField(field.Path, innerField.Value);
492+
}
493+
459494
static AstExpression UltimateGetFieldInput(AstGetFieldExpression getField)
460495
{
461496
if (getField.Input is AstGetFieldExpression nestedInputGetField)

src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/NewDictionaryExpressionToAggregationExpressionTranslator.cs

+3-3
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ public static TranslatedExpression Translate(TranslationContext context, NewExpr
5151
if (!itemDocumentSerializer.TryGetMemberSerializationInfo("Key", out var keyMemberSerializationInfo) ||
5252
!itemDocumentSerializer.TryGetMemberSerializationInfo("Value", out var valueMemberSerializationInfo))
5353
{
54-
throw new ExpressionNotSupportedException(expression);
54+
throw new ExpressionNotSupportedException(expression, because: $"document serializer class {itemSerializationInfo.Serializer.GetType()} does not provide member serialization info for required fields.");
5555
}
5656

5757
if (keyMemberSerializationInfo.ElementName == "k" && valueMemberSerializationInfo.ElementName == "v")
@@ -73,13 +73,13 @@ public static TranslatedExpression Translate(TranslationContext context, NewExpr
7373
}
7474
else
7575
{
76-
throw new ExpressionNotSupportedException(expression);
76+
throw new ExpressionNotSupportedException(expression, because: $"document serializer class {itemSerializationInfo.Serializer.GetType()} does not implement {nameof(IBsonDocumentSerializer)}");
7777
}
7878

7979
if (keySerializer is not IRepresentationConfigurable representationConfigurableSerializer
8080
|| representationConfigurableSerializer.Representation != BsonType.String)
8181
{
82-
throw new ExpressionNotSupportedException(expression);
82+
throw new ExpressionNotSupportedException(expression, because: "key did not serialize as a string");
8383
}
8484

8585
var ast = AstExpression.Unary(AstUnaryOperator.ArrayToObject, collectionTranslationAst);

tests/MongoDB.Driver.Tests/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/NewDictionaryExpressionToAggregationExpressionTranslatorTests.cs

+31-28
Original file line numberDiff line numberDiff line change
@@ -35,25 +35,41 @@ public NewDictionaryExpressionToAggregationExpressionTranslatorTests(ClassFixtur
3535
}
3636

3737
[Fact]
38-
public void NewDictionary_should_translate()
38+
public void NewDictionary_with_KeyValuePairs_should_translate()
3939
{
4040
var collection = Fixture.Collection;
4141

4242
var queryable = collection.AsQueryable()
4343
.Select(d => new Dictionary<string, string>(
44-
new[] { new KeyValuePair<string, string>("A", d.A) }));
44+
new[] { new KeyValuePair<string, string>("A", d.A), new KeyValuePair<string, string>("B", d.B) }));
4545

4646
var stages = Translate(collection, queryable);
4747

48-
//AssertStages(stages, "{ $project : { _v : { $arrayToObject : [[['A', '$A']]] }, _id : 0 } }");
49-
AssertStages(stages, "{ $project : { _v : { $arrayToObject : { $map : { input : [{ Key : 'A', Value : '$A' }], as : 'pair', in : { k : '$$pair.Key', v : '$$pair.Value' } } } }, _id : 0 } }");
48+
AssertStages(stages, "{ $project : { _v : { $arrayToObject : [[{ k : 'A', v : '$A' }, { k : 'B', v : '$B' }]] }, _id : 0 } }");
5049

5150
var result = queryable.Single();
52-
result.Should().Equal(new Dictionary<string, string>{ { "A", "a" } });
51+
result.Should().Equal(new Dictionary<string, string>{ ["A"] = "a", ["B"] = "b" });
5352
}
5453

54+
// [Fact]
55+
// public void NewDictionary_with_KeyValuePairs_Create_should_translate()
56+
// {
57+
// var collection = Fixture.Collection;
58+
//
59+
// var queryable = collection.AsQueryable()
60+
// .Select(d => new Dictionary<string, string>(
61+
// new[] { KeyValuePair.Create("A", d.A), KeyValuePair.Create("B", d.B) }));
62+
//
63+
// var stages = Translate(collection, queryable);
64+
//
65+
// AssertStages(stages, "{ $project : { _v : { $arrayToObject : [[{ k : 'A', v : '$A' }, { k : 'B', v : '$B' }]] }, _id : 0 } }");
66+
//
67+
// var result = queryable.Single();
68+
// result.Should().Equal(new Dictionary<string, string>{ ["A"] = "a", ["B"] = "b" });
69+
// }
70+
5571
[Fact]
56-
public void NewDictionary_should_translate_Guid_as_string_key()
72+
public void NewDictionary_with_KeyValuePairs_should_translate_Guid_as_string_key()
5773
{
5874
var collection = Fixture.Collection;
5975

@@ -63,16 +79,15 @@ public void NewDictionary_should_translate_Guid_as_string_key()
6379

6480
var stages = Translate(collection, queryable);
6581

66-
// AssertStages(stages, "{ $project : { _v : { $arrayToObject : [[['$GuidAsString', '$A']]] }, _id : 0 } }");
67-
AssertStages(stages, "{ $project : { _v : { $arrayToObject : { $map : { input : [{ Key : '$GuidAsString', Value : '$A' }], as : 'pair', in : { k : '$$pair.Key', v : '$$pair.Value' } } } }, _id : 0 } }");
82+
AssertStages(stages, "{ $project : { _v : { $arrayToObject : [[{ k : '$GuidAsString', v : '$A' }]] }, _id : 0 } }");
6883

6984
var result = queryable.Single();
70-
result.Should().Equal(new Dictionary<Guid, string>{ { Guid.Parse("3E9AE467-9705-4C17-9655-EE7730BCC2EE"), "a" } });
85+
result.Should().Equal(new Dictionary<Guid, string>{ [Guid.Parse("3E9AE467-9705-4C17-9655-EE7730BCC2EE")] = "a" });
7186
}
7287

7388

7489
[Fact]
75-
public void NewDictionary_should_translate_dynamic_array()
90+
public void NewDictionary_with_KeyValuePairs_should_translate_dynamic_array()
7691
{
7792
var collection = Fixture.Collection;
7893

@@ -82,14 +97,14 @@ public void NewDictionary_should_translate_dynamic_array()
8297

8398
var stages = Translate(collection, queryable);
8499

85-
//AssertStages(stages, "{ $project : { _v : { $arrayToObject : [[['$GuidAsString', '$A']]] }, _id : 0 } }");
100+
AssertStages(stages, "{ $project : { _v : { $arrayToObject : { $map: { input: '$Items', as: 'i', in: { k: '$$i.P', v: '$$i.W' } } } }, _id : 0 } }");
86101

87102
var result = queryable.Single();
88-
result.Should().Equal(new Dictionary<string, string>{ { "x", "y" } });
103+
result.Should().Equal(new Dictionary<string, string>{ ["x"] = "y" });
89104
}
90105

91106
[Fact]
92-
public void NewDictionary_throws_on_non_string_key()
107+
public void NewDictionary_with_KeyValuePairs_throws_on_non_string_key()
93108
{
94109
var collection = Fixture.Collection;
95110

@@ -103,25 +118,12 @@ public void NewDictionary_throws_on_non_string_key()
103118
exception.Should().BeOfType<ExpressionNotSupportedException>();
104119
}
105120

106-
107-
// Delete this test before merge, probably need to have additional ticket for this.
108-
[Fact(Skip = "Not implemented yet")]
109-
public void DictionaryInitializer_should_translate()
110-
{
111-
var collection = Fixture.Collection;
112-
113-
var queryable = collection.AsQueryable()
114-
.Select(d => new Dictionary<string, string>() { { "A", d.A } });
115-
116-
var stages = Translate(collection, queryable);
117-
118-
AssertStages(stages, []);
119-
}
120-
121121
public class C
122122
{
123123
public string A { get; set; }
124124

125+
public string B { get; set; }
126+
125127
[BsonRepresentation(BsonType.String)]
126128
public Guid GuidAsString { get; set; }
127129

@@ -142,6 +144,7 @@ public sealed class ClassFixture : MongoCollectionFixture<C>
142144
new C
143145
{
144146
A = "a",
147+
B = "b",
145148
GuidAsString = Guid.Parse("3E9AE467-9705-4C17-9655-EE7730BCC2EE"),
146149
Items = [ new Item { P = "x", W = "y" } ]
147150
},

0 commit comments

Comments
 (0)