Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,11 @@ public static AstExpression ComputedDocument(IEnumerable<AstComputedField> field
return new AstComputedDocumentExpression(fields);
}

public static AstExpression ComputedDocument(IEnumerable<(string Name, AstExpression Value)> fields)
Copy link
Contributor Author

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.

{
return new AstComputedDocumentExpression(fields.Select(f => AstExpression.ComputedField(f.Name, f.Value)));
}

public static AstComputedField ComputedField(string name, AstExpression value)
{
return new AstComputedField(name, value);
Expand Down
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>
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need a serializer for Dictionary<TKey, TValue>.KeyCollection to be able to work with this type in LINQ queries or to deserialize them client-side.

{
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>
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is just a more convenient way to create a serializer for Dictionary<TKey, TValue>.

{
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
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need a serializer for Dictionary<TKey, TValue>.ValueCollection to be able to work with this type in LINQ queries or to deserialize them client-side.

{
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);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that the only way to create a ValueCollection is to create the whole dictionary first and then access the Values property.

Copy link
Contributor Author

Choose a reason for hiding this comment

The 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 KeyCollection because every value can be default since we only care about the keys.

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>>
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is just a more convenient way to create a serializer for ICollection<TItem>.

{
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
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a special serializer that deserializes a KeyValuePair but then extracts only the Value portion of it as the result.

It also represents values that are still coupled with the corresponding key as needed to work with a Dictionary<TKey, TValue>.ValueCollection.

{
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
Expand Up @@ -30,6 +30,12 @@ internal static class ExpressionToAggregationExpressionTranslator
{
// public static methods
public static TranslatedExpression Translate(TranslationContext context, Expression expression)
{
Copy link
Contributor Author

Choose a reason for hiding this comment

The 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)
{
Expand Down Expand Up @@ -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);
Copy link
Contributor Author

Choose a reason for hiding this comment

The 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;
Expand Down Expand Up @@ -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;
}
}
}
Loading