Skip to content

Commit 2c6ad30

Browse files
authored
CSHARP-5779: Support Dictionary Keys and Values properties (#1807)
1 parent bc1760a commit 2c6ad30

File tree

10 files changed

+1019
-4
lines changed

10 files changed

+1019
-4
lines changed

src/MongoDB.Driver/Linq/Linq3Implementation/Ast/Expressions/AstExpression.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,11 @@ public static AstExpression ComputedDocument(IEnumerable<AstComputedField> field
224224
return new AstComputedDocumentExpression(fields);
225225
}
226226

227+
public static AstExpression ComputedDocument(IEnumerable<(string Name, AstExpression Value)> fields)
228+
{
229+
return new AstComputedDocumentExpression(fields.Select(f => AstExpression.ComputedField(f.Name, f.Value)));
230+
}
231+
227232
public static AstComputedField ComputedField(string name, AstExpression value)
228233
{
229234
return new AstComputedField(name, value);
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
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;
17+
using System.Collections;
18+
using System.Collections.Generic;
19+
using MongoDB.Bson.Serialization;
20+
using MongoDB.Bson.Serialization.Serializers;
21+
22+
namespace MongoDB.Driver.Linq.Linq3Implementation.Serializers;
23+
24+
internal static class DictionaryKeyCollectionSerializer
25+
{
26+
public static IBsonSerializer Create(IBsonSerializer keySerializer, IBsonSerializer valueSerializer)
27+
{
28+
var keyType = keySerializer.ValueType;
29+
var valueType = valueSerializer.ValueType;
30+
var serializerType = typeof(DictionaryKeyCollectionSerializer<,>).MakeGenericType(keyType, valueType);
31+
return (IBsonSerializer)Activator.CreateInstance(serializerType, [keySerializer]);
32+
}
33+
}
34+
35+
internal class DictionaryKeyCollectionSerializer<TKey, TValue> : EnumerableSerializerBase<Dictionary<TKey, TValue>.KeyCollection>
36+
{
37+
public DictionaryKeyCollectionSerializer(IBsonSerializer<TKey> keySerializer)
38+
: base(itemSerializer: keySerializer)
39+
{
40+
}
41+
42+
protected override void AddItem(object accumulator, object item) => ((Dictionary<TKey, TValue>)accumulator).Add((TKey)item, default(TValue));
43+
44+
protected override object CreateAccumulator() => new Dictionary<TKey, TValue>();
45+
46+
protected override IEnumerable EnumerateItemsInSerializationOrder(Dictionary<TKey, TValue>.KeyCollection value) => value;
47+
48+
protected override Dictionary<TKey, TValue>.KeyCollection FinalizeResult(object accumulator) => ((Dictionary<TKey, TValue>)accumulator).Keys;
49+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
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;
17+
using System.Collections.Generic;
18+
using MongoDB.Bson.Serialization;
19+
using MongoDB.Bson.Serialization.Options;
20+
using MongoDB.Bson.Serialization.Serializers;
21+
22+
namespace MongoDB.Driver.Linq.Linq3Implementation.Serializers;
23+
24+
internal static class DictionarySerializer
25+
{
26+
public static IBsonSerializer Create(
27+
DictionaryRepresentation dictionaryRepresentation,
28+
IBsonSerializer keySerializer,
29+
IBsonSerializer valueSerializer)
30+
{
31+
var keyType = keySerializer.ValueType;
32+
var valueType = valueSerializer.ValueType;
33+
var serializerType = typeof(DictionarySerializer<,>).MakeGenericType(keyType, valueType);
34+
return (IBsonSerializer)Activator.CreateInstance(serializerType, [dictionaryRepresentation, keySerializer, valueSerializer]);
35+
}
36+
}
37+
38+
internal class DictionarySerializer<TKey, TValue> : DictionarySerializerBase<Dictionary<TKey, TValue>, TKey, TValue>
39+
{
40+
public DictionarySerializer(
41+
DictionaryRepresentation dictionaryRepresentation,
42+
IBsonSerializer<TKey> keySerializer,
43+
IBsonSerializer<TValue> valueSerializer)
44+
: base(dictionaryRepresentation, keySerializer, valueSerializer)
45+
{
46+
}
47+
48+
protected override ICollection<KeyValuePair<TKey, TValue>> CreateAccumulator()
49+
{
50+
return new Dictionary<TKey, TValue>();
51+
}
52+
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
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;
17+
using System.Collections.Generic;
18+
using MongoDB.Bson.Serialization;
19+
using MongoDB.Bson.Serialization.Options;
20+
using MongoDB.Bson.Serialization.Serializers;
21+
22+
namespace MongoDB.Driver.Linq.Linq3Implementation.Serializers;
23+
24+
internal static class DictionaryValueCollectionSerializer
25+
{
26+
public static IBsonSerializer Create(IBsonSerializer keySerializer, IBsonSerializer valueSerializer)
27+
{
28+
var keyType = keySerializer.ValueType;
29+
var valueType = valueSerializer.ValueType;
30+
var serializerType = typeof(DictionaryValueCollectionSerializer<,>).MakeGenericType(keyType, valueType);
31+
return (IBsonSerializer)Activator.CreateInstance(serializerType, [keySerializer, valueSerializer]);
32+
}
33+
}
34+
35+
internal class DictionaryValueCollectionSerializer<TKey, TValue> : SerializerBase<Dictionary<TKey, TValue>.ValueCollection>, IBsonArraySerializer
36+
{
37+
private readonly IBsonSerializer<Dictionary<TKey, TValue>> _dictionarySerializer;
38+
private readonly IBsonSerializer<TValue> _wrappedValueSerializer;
39+
40+
public DictionaryValueCollectionSerializer(IBsonSerializer<TKey> keySerializer, IBsonSerializer<TValue> valueSerializer)
41+
{
42+
_dictionarySerializer = (IBsonSerializer<Dictionary<TKey, TValue>>)DictionarySerializer.Create(DictionaryRepresentation.ArrayOfDocuments, keySerializer, valueSerializer);
43+
_wrappedValueSerializer = (IBsonSerializer<TValue>)KeyValuePairWrappedValueSerializer.Create(keySerializer, valueSerializer);
44+
}
45+
46+
public override Dictionary<TKey, TValue>.ValueCollection Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args)
47+
{
48+
var dictionary = _dictionarySerializer.Deserialize(context, args);
49+
return dictionary.Values;
50+
}
51+
52+
public bool TryGetItemSerializationInfo(out BsonSerializationInfo serializationInfo)
53+
{
54+
serializationInfo = new BsonSerializationInfo(null, _wrappedValueSerializer, typeof(TValue));
55+
return true;
56+
}
57+
}
Lines changed: 48 additions & 0 deletions
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;
17+
using System.Collections;
18+
using System.Collections.Generic;
19+
using MongoDB.Bson.Serialization;
20+
using MongoDB.Bson.Serialization.Serializers;
21+
22+
namespace MongoDB.Driver.Linq.Linq3Implementation.Serializers;
23+
24+
internal static class ICollectionSerializer
25+
{
26+
public static IBsonSerializer Create(IBsonSerializer itemSerializer)
27+
{
28+
var itemType = itemSerializer.ValueType;
29+
var serializerType = typeof(ICollectionSerializer<>).MakeGenericType(itemType);
30+
return (IBsonSerializer)Activator.CreateInstance(serializerType, [itemSerializer]);
31+
}
32+
}
33+
34+
internal class ICollectionSerializer<TItem> : EnumerableSerializerBase<ICollection<TItem>>
35+
{
36+
public ICollectionSerializer(IBsonSerializer<TItem> itemSerializer)
37+
: base(itemSerializer)
38+
{
39+
}
40+
41+
protected override void AddItem(object accumulator, object item) => ((List<TItem>)accumulator).Add((TItem)item);
42+
43+
protected override object CreateAccumulator() => new List<TItem>();
44+
45+
protected override IEnumerable EnumerateItemsInSerializationOrder(ICollection<TItem> value) => value;
46+
47+
protected override ICollection<TItem> FinalizeResult(object accumulator) => (ICollection<TItem>)accumulator;
48+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
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;
17+
using System.Collections.Generic;
18+
using MongoDB.Bson;
19+
using MongoDB.Bson.Serialization;
20+
using MongoDB.Bson.Serialization.Serializers;
21+
22+
namespace MongoDB.Driver.Linq.Linq3Implementation.Serializers;
23+
24+
internal static class KeyValuePairWrappedValueSerializer
25+
{
26+
public static IBsonSerializer Create(IBsonSerializer keySerializer, IBsonSerializer valueSerializer)
27+
{
28+
var keyType = keySerializer.ValueType;
29+
var valueType = valueSerializer.ValueType;
30+
var serializerType = typeof(KeyValuePairWrappedValueSerializer<,>).MakeGenericType(keyType, valueType);
31+
return (IBsonSerializer)Activator.CreateInstance(serializerType, [keySerializer, valueSerializer]);
32+
}
33+
}
34+
35+
internal class KeyValuePairWrappedValueSerializer<TKey, TValue> : SerializerBase<TValue>, IWrappedValueSerializer
36+
{
37+
private readonly IBsonSerializer<KeyValuePair<TKey, TValue>> _keyValuePairSerializer;
38+
private readonly IBsonSerializer<TValue> _valueSerializer;
39+
40+
public KeyValuePairWrappedValueSerializer(IBsonSerializer<TKey> keySerializer, IBsonSerializer<TValue> valueSerializer)
41+
{
42+
_keyValuePairSerializer = (IBsonSerializer<KeyValuePair<TKey, TValue>>)KeyValuePairSerializer.Create(BsonType.Document, keySerializer, valueSerializer);
43+
_valueSerializer = valueSerializer;
44+
}
45+
46+
public string FieldName => "v";
47+
public IBsonSerializer ValueSerializer => _valueSerializer;
48+
49+
public override TValue Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args)
50+
{
51+
var keyValuePair = _keyValuePairSerializer.Deserialize(context, args);
52+
return keyValuePair.Value;
53+
}
54+
}

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

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,12 @@ internal static class ExpressionToAggregationExpressionTranslator
3030
{
3131
// public static methods
3232
public static TranslatedExpression Translate(TranslationContext context, Expression expression)
33+
{
34+
var translatedExpression = TranslateWithoutUnwrapping(context, expression);
35+
return UnwrapIfWrapped(expression, translatedExpression);
36+
}
37+
38+
public static TranslatedExpression TranslateWithoutUnwrapping(TranslationContext context, Expression expression)
3339
{
3440
switch (expression.NodeType)
3541
{
@@ -113,12 +119,11 @@ public static TranslatedExpression TranslateEnumerable(TranslationContext contex
113119
{
114120
var keySerializer = dictionarySerializer.KeySerializer;
115121
var valueSerializer = dictionarySerializer.ValueSerializer;
116-
var keyValuePairSerializer = KeyValuePairSerializer.Create(BsonType.Document, keySerializer, valueSerializer);
117122

118123
var ast = AstExpression.ObjectToArray(aggregateExpression.Ast);
119-
var ienumerableSerializer = ArraySerializerHelper.CreateSerializer(keyValuePairSerializer);
124+
var arrayOfDocumentsDictionarySerializer = DictionarySerializer.Create(DictionaryRepresentation.ArrayOfDocuments, keySerializer, valueSerializer);
120125

121-
aggregateExpression = new TranslatedExpression(expression, ast, ienumerableSerializer);
126+
aggregateExpression = new TranslatedExpression(expression, ast, arrayOfDocumentsDictionarySerializer);
122127
}
123128

124129
return aggregateExpression;
@@ -169,5 +174,16 @@ public static TranslatedExpression TranslateLambdaBody(
169174

170175
return translatedBody;
171176
}
177+
178+
private static TranslatedExpression UnwrapIfWrapped(Expression expression, TranslatedExpression translatedExpression)
179+
{
180+
if (translatedExpression.Serializer is IWrappedValueSerializer wrappedValueSerializer)
181+
{
182+
var unwrappedAst = AstExpression.GetField(translatedExpression.Ast, wrappedValueSerializer.FieldName);
183+
return new TranslatedExpression(expression, unwrappedAst, wrappedValueSerializer.ValueSerializer);
184+
}
185+
186+
return translatedExpression;
187+
}
172188
}
173189
}

0 commit comments

Comments
 (0)