-
Notifications
You must be signed in to change notification settings - Fork 1.3k
CSHARP-5779: Support Dictionary Keys and Values properties #1807
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,49 @@ | ||
| /* Copyright 2010-present MongoDB Inc. | ||
| * | ||
| * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| * you may not use this file except in compliance with the License. | ||
| * You may obtain a copy of the License at | ||
| * | ||
| * http://www.apache.org/licenses/LICENSE-2.0 | ||
| * | ||
| * Unless required by applicable law or agreed to in writing, software | ||
| * distributed under the License is distributed on an "AS IS" BASIS, | ||
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| * See the License for the specific language governing permissions and | ||
| * limitations under the License. | ||
| */ | ||
|
|
||
| using System; | ||
| using System.Collections; | ||
| using System.Collections.Generic; | ||
| using MongoDB.Bson.Serialization; | ||
| using MongoDB.Bson.Serialization.Serializers; | ||
|
|
||
| namespace MongoDB.Driver.Linq.Linq3Implementation.Serializers; | ||
|
|
||
| internal static class DictionaryKeyCollectionSerializer | ||
| { | ||
| public static IBsonSerializer Create(IBsonSerializer keySerializer, IBsonSerializer valueSerializer) | ||
| { | ||
| var keyType = keySerializer.ValueType; | ||
| var valueType = valueSerializer.ValueType; | ||
| var serializerType = typeof(DictionaryKeyCollectionSerializer<,>).MakeGenericType(keyType, valueType); | ||
| return (IBsonSerializer)Activator.CreateInstance(serializerType, [keySerializer]); | ||
| } | ||
| } | ||
|
|
||
| internal class DictionaryKeyCollectionSerializer<TKey, TValue> : EnumerableSerializerBase<Dictionary<TKey, TValue>.KeyCollection> | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We need a serializer for |
||
| { | ||
| public DictionaryKeyCollectionSerializer(IBsonSerializer<TKey> keySerializer) | ||
| : base(itemSerializer: keySerializer) | ||
| { | ||
| } | ||
|
|
||
| protected override void AddItem(object accumulator, object item) => ((Dictionary<TKey, TValue>)accumulator).Add((TKey)item, default(TValue)); | ||
|
|
||
| protected override object CreateAccumulator() => new Dictionary<TKey, TValue>(); | ||
|
|
||
| protected override IEnumerable EnumerateItemsInSerializationOrder(Dictionary<TKey, TValue>.KeyCollection value) => value; | ||
|
|
||
| protected override Dictionary<TKey, TValue>.KeyCollection FinalizeResult(object accumulator) => ((Dictionary<TKey, TValue>)accumulator).Keys; | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,52 @@ | ||
| /* Copyright 2010-present MongoDB Inc. | ||
| * | ||
| * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| * you may not use this file except in compliance with the License. | ||
| * You may obtain a copy of the License at | ||
| * | ||
| * http://www.apache.org/licenses/LICENSE-2.0 | ||
| * | ||
| * Unless required by applicable law or agreed to in writing, software | ||
| * distributed under the License is distributed on an "AS IS" BASIS, | ||
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| * See the License for the specific language governing permissions and | ||
| * limitations under the License. | ||
| */ | ||
|
|
||
| using System; | ||
| using System.Collections.Generic; | ||
| using MongoDB.Bson.Serialization; | ||
| using MongoDB.Bson.Serialization.Options; | ||
| using MongoDB.Bson.Serialization.Serializers; | ||
|
|
||
| namespace MongoDB.Driver.Linq.Linq3Implementation.Serializers; | ||
|
|
||
| internal static class DictionarySerializer | ||
| { | ||
| public static IBsonSerializer Create( | ||
| DictionaryRepresentation dictionaryRepresentation, | ||
| IBsonSerializer keySerializer, | ||
| IBsonSerializer valueSerializer) | ||
| { | ||
| var keyType = keySerializer.ValueType; | ||
| var valueType = valueSerializer.ValueType; | ||
| var serializerType = typeof(DictionarySerializer<,>).MakeGenericType(keyType, valueType); | ||
| return (IBsonSerializer)Activator.CreateInstance(serializerType, [dictionaryRepresentation, keySerializer, valueSerializer]); | ||
| } | ||
| } | ||
|
|
||
| internal class DictionarySerializer<TKey, TValue> : DictionarySerializerBase<Dictionary<TKey, TValue>, TKey, TValue> | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is just a more convenient way to create a serializer for |
||
| { | ||
| public DictionarySerializer( | ||
| DictionaryRepresentation dictionaryRepresentation, | ||
| IBsonSerializer<TKey> keySerializer, | ||
| IBsonSerializer<TValue> valueSerializer) | ||
| : base(dictionaryRepresentation, keySerializer, valueSerializer) | ||
| { | ||
| } | ||
|
|
||
| protected override ICollection<KeyValuePair<TKey, TValue>> CreateAccumulator() | ||
| { | ||
| return new Dictionary<TKey, TValue>(); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,57 @@ | ||
| /* Copyright 2010-present MongoDB Inc. | ||
| * | ||
| * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| * you may not use this file except in compliance with the License. | ||
| * You may obtain a copy of the License at | ||
| * | ||
| * http://www.apache.org/licenses/LICENSE-2.0 | ||
| * | ||
| * Unless required by applicable law or agreed to in writing, software | ||
| * distributed under the License is distributed on an "AS IS" BASIS, | ||
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| * See the License for the specific language governing permissions and | ||
| * limitations under the License. | ||
| */ | ||
|
|
||
| using System; | ||
| using System.Collections.Generic; | ||
| using MongoDB.Bson.Serialization; | ||
| using MongoDB.Bson.Serialization.Options; | ||
| using MongoDB.Bson.Serialization.Serializers; | ||
|
|
||
| namespace MongoDB.Driver.Linq.Linq3Implementation.Serializers; | ||
|
|
||
| internal static class DictionaryValueCollectionSerializer | ||
| { | ||
| public static IBsonSerializer Create(IBsonSerializer keySerializer, IBsonSerializer valueSerializer) | ||
| { | ||
| var keyType = keySerializer.ValueType; | ||
| var valueType = valueSerializer.ValueType; | ||
| var serializerType = typeof(DictionaryValueCollectionSerializer<,>).MakeGenericType(keyType, valueType); | ||
| return (IBsonSerializer)Activator.CreateInstance(serializerType, [keySerializer, valueSerializer]); | ||
| } | ||
| } | ||
|
|
||
| internal class DictionaryValueCollectionSerializer<TKey, TValue> : SerializerBase<Dictionary<TKey, TValue>.ValueCollection>, IBsonArraySerializer | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We need a serializer for |
||
| { | ||
| private readonly IBsonSerializer<Dictionary<TKey, TValue>> _dictionarySerializer; | ||
| private readonly IBsonSerializer<TValue> _wrappedValueSerializer; | ||
|
|
||
| public DictionaryValueCollectionSerializer(IBsonSerializer<TKey> keySerializer, IBsonSerializer<TValue> valueSerializer) | ||
| { | ||
| _dictionarySerializer = (IBsonSerializer<Dictionary<TKey, TValue>>)DictionarySerializer.Create(DictionaryRepresentation.ArrayOfDocuments, keySerializer, valueSerializer); | ||
| _wrappedValueSerializer = (IBsonSerializer<TValue>)KeyValuePairWrappedValueSerializer.Create(keySerializer, valueSerializer); | ||
| } | ||
|
|
||
| public override Dictionary<TKey, TValue>.ValueCollection Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args) | ||
| { | ||
| var dictionary = _dictionarySerializer.Deserialize(context, args); | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Note that the only way to create a
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Further note that this means that we are returning the keys from the server also. We have to do that to create a dictionary because while in theory we could use dummy key values in practice we have no way to create unique dummy key values. Also note that this was not an issue for the |
||
| return dictionary.Values; | ||
| } | ||
|
|
||
| public bool TryGetItemSerializationInfo(out BsonSerializationInfo serializationInfo) | ||
| { | ||
| serializationInfo = new BsonSerializationInfo(null, _wrappedValueSerializer, typeof(TValue)); | ||
| return true; | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,48 @@ | ||
| /* Copyright 2010-present MongoDB Inc. | ||
| * | ||
| * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| * you may not use this file except in compliance with the License. | ||
| * You may obtain a copy of the License at | ||
| * | ||
| * http://www.apache.org/licenses/LICENSE-2.0 | ||
| * | ||
| * Unless required by applicable law or agreed to in writing, software | ||
| * distributed under the License is distributed on an "AS IS" BASIS, | ||
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| * See the License for the specific language governing permissions and | ||
| * limitations under the License. | ||
| */ | ||
|
|
||
| using System; | ||
| using System.Collections; | ||
| using System.Collections.Generic; | ||
| using MongoDB.Bson.Serialization; | ||
| using MongoDB.Bson.Serialization.Serializers; | ||
|
|
||
| namespace MongoDB.Driver.Linq.Linq3Implementation.Serializers; | ||
|
|
||
| internal static class ICollectionSerializer | ||
| { | ||
| public static IBsonSerializer Create(IBsonSerializer itemSerializer) | ||
| { | ||
| var itemType = itemSerializer.ValueType; | ||
| var serializerType = typeof(ICollectionSerializer<>).MakeGenericType(itemType); | ||
| return (IBsonSerializer)Activator.CreateInstance(serializerType, [itemSerializer]); | ||
| } | ||
| } | ||
|
|
||
| internal class ICollectionSerializer<TItem> : EnumerableSerializerBase<ICollection<TItem>> | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is just a more convenient way to create a serializer for |
||
| { | ||
| public ICollectionSerializer(IBsonSerializer<TItem> itemSerializer) | ||
| : base(itemSerializer) | ||
| { | ||
| } | ||
|
|
||
| protected override void AddItem(object accumulator, object item) => ((List<TItem>)accumulator).Add((TItem)item); | ||
|
|
||
| protected override object CreateAccumulator() => new List<TItem>(); | ||
|
|
||
| protected override IEnumerable EnumerateItemsInSerializationOrder(ICollection<TItem> value) => value; | ||
|
|
||
| protected override ICollection<TItem> FinalizeResult(object accumulator) => (ICollection<TItem>)accumulator; | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,54 @@ | ||
| /* Copyright 2010-present MongoDB Inc. | ||
| * | ||
| * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| * you may not use this file except in compliance with the License. | ||
| * You may obtain a copy of the License at | ||
| * | ||
| * http://www.apache.org/licenses/LICENSE-2.0 | ||
| * | ||
| * Unless required by applicable law or agreed to in writing, software | ||
| * distributed under the License is distributed on an "AS IS" BASIS, | ||
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| * See the License for the specific language governing permissions and | ||
| * limitations under the License. | ||
| */ | ||
|
|
||
| using System; | ||
| using System.Collections.Generic; | ||
| using MongoDB.Bson; | ||
| using MongoDB.Bson.Serialization; | ||
| using MongoDB.Bson.Serialization.Serializers; | ||
|
|
||
| namespace MongoDB.Driver.Linq.Linq3Implementation.Serializers; | ||
|
|
||
| internal static class KeyValuePairWrappedValueSerializer | ||
| { | ||
| public static IBsonSerializer Create(IBsonSerializer keySerializer, IBsonSerializer valueSerializer) | ||
| { | ||
| var keyType = keySerializer.ValueType; | ||
| var valueType = valueSerializer.ValueType; | ||
| var serializerType = typeof(KeyValuePairWrappedValueSerializer<,>).MakeGenericType(keyType, valueType); | ||
| return (IBsonSerializer)Activator.CreateInstance(serializerType, [keySerializer, valueSerializer]); | ||
| } | ||
| } | ||
|
|
||
| internal class KeyValuePairWrappedValueSerializer<TKey, TValue> : SerializerBase<TValue>, IWrappedValueSerializer | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is a special serializer that deserializes a It also represents values that are still coupled with the corresponding key as needed to work with a |
||
| { | ||
| private readonly IBsonSerializer<KeyValuePair<TKey, TValue>> _keyValuePairSerializer; | ||
| private readonly IBsonSerializer<TValue> _valueSerializer; | ||
|
|
||
| public KeyValuePairWrappedValueSerializer(IBsonSerializer<TKey> keySerializer, IBsonSerializer<TValue> valueSerializer) | ||
| { | ||
| _keyValuePairSerializer = (IBsonSerializer<KeyValuePair<TKey, TValue>>)KeyValuePairSerializer.Create(BsonType.Document, keySerializer, valueSerializer); | ||
| _valueSerializer = valueSerializer; | ||
| } | ||
|
|
||
| public string FieldName => "v"; | ||
| public IBsonSerializer ValueSerializer => _valueSerializer; | ||
|
|
||
| public override TValue Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args) | ||
| { | ||
| var keyValuePair = _keyValuePairSerializer.Deserialize(context, args); | ||
| return keyValuePair.Value; | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -30,6 +30,12 @@ internal static class ExpressionToAggregationExpressionTranslator | |
| { | ||
| // public static methods | ||
| public static TranslatedExpression Translate(TranslationContext context, Expression expression) | ||
| { | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Whenever we want to use a value we go ahead and unwrap it if it is wrapped. |
||
| var translatedExpression = TranslateWithoutUnwrapping(context, expression); | ||
| return UnwrapIfWrapped(expression, translatedExpression); | ||
| } | ||
|
|
||
| public static TranslatedExpression TranslateWithoutUnwrapping(TranslationContext context, Expression expression) | ||
| { | ||
| switch (expression.NodeType) | ||
| { | ||
|
|
@@ -113,12 +119,11 @@ public static TranslatedExpression TranslateEnumerable(TranslationContext contex | |
| { | ||
| var keySerializer = dictionarySerializer.KeySerializer; | ||
| var valueSerializer = dictionarySerializer.ValueSerializer; | ||
| var keyValuePairSerializer = KeyValuePairSerializer.Create(BsonType.Document, keySerializer, valueSerializer); | ||
|
|
||
| var ast = AstExpression.ObjectToArray(aggregateExpression.Ast); | ||
| var ienumerableSerializer = ArraySerializerHelper.CreateSerializer(keyValuePairSerializer); | ||
| var arrayOfDocumentsDictionarySerializer = DictionarySerializer.Create(DictionaryRepresentation.ArrayOfDocuments, keySerializer, valueSerializer); | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We were creating the "wrong" serializer before. |
||
|
|
||
| aggregateExpression = new TranslatedExpression(expression, ast, ienumerableSerializer); | ||
| aggregateExpression = new TranslatedExpression(expression, ast, arrayOfDocumentsDictionarySerializer); | ||
| } | ||
|
|
||
| return aggregateExpression; | ||
|
|
@@ -169,5 +174,16 @@ public static TranslatedExpression TranslateLambdaBody( | |
|
|
||
| return translatedBody; | ||
| } | ||
|
|
||
| private static TranslatedExpression UnwrapIfWrapped(Expression expression, TranslatedExpression translatedExpression) | ||
| { | ||
| if (translatedExpression.Serializer is IWrappedValueSerializer wrappedValueSerializer) | ||
| { | ||
| var unwrappedAst = AstExpression.GetField(translatedExpression.Ast, wrappedValueSerializer.FieldName); | ||
| return new TranslatedExpression(expression, unwrappedAst, wrappedValueSerializer.ValueSerializer); | ||
| } | ||
|
|
||
| return translatedExpression; | ||
| } | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A more convenient overload of
ComputedDocument.