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 @@ -29,6 +29,22 @@ public interface IKeyValuePairSerializer
BsonType Representation { get; }
}

/// <summary>
/// An extended interface for KeyValuePairSerializer that provides access to key and value serializers.
/// </summary>
public interface IKeyValuePairSerializerV2 : IKeyValuePairSerializer
{
/// <summary>
/// Gets the key serializer.
/// </summary>
IBsonSerializer KeySerializer { get; }

/// <summary>
/// Gets the value serializer.
/// </summary>
IBsonSerializer ValueSerializer { get; }
}

/// <summary>
/// Static factory class for KeyValuePairSerializers.
/// </summary>
Expand Down Expand Up @@ -61,7 +77,7 @@ public static IBsonSerializer Create(
public sealed class KeyValuePairSerializer<TKey, TValue> :
StructSerializerBase<KeyValuePair<TKey, TValue>>,
IBsonDocumentSerializer,
IKeyValuePairSerializer
IKeyValuePairSerializerV2
{
// private constants
private static class Flags
Expand Down Expand Up @@ -191,6 +207,16 @@ public IBsonSerializer<TValue> ValueSerializer
get { return _lazyValueSerializer.Value; }
}

/// <summary>
/// Gets the key serializer.
/// </summary>
IBsonSerializer IKeyValuePairSerializerV2.KeySerializer => KeySerializer;

/// <summary>
/// Gets the value serializer.
/// </summary>
IBsonSerializer IKeyValuePairSerializerV2.ValueSerializer => ValueSerializer;

// public methods
/// <summary>
/// Deserializes a value.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,11 @@ public static AstExpression ArrayElemAt(AstExpression array, AstExpression index
return new AstBinaryExpression(AstBinaryOperator.ArrayElemAt, array, index);
}

public static AstExpression ArrayToObject(AstExpression arg)
{
return new AstUnaryExpression(AstUnaryOperator.ArrayToObject, arg);
}

public static AstExpression Avg(AstExpression array)
{
return new AstUnaryExpression(AstUnaryOperator.Avg, array);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
*/

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Reflection;
Expand Down Expand Up @@ -66,11 +65,21 @@ public static TranslatedExpression Translate(TranslationContext context, MemberE

if (!DocumentSerializerHelper.AreMembersRepresentedAsFields(containerTranslation.Serializer, out _))
{
if (member is PropertyInfo propertyInfo && propertyInfo.Name == "Length")
if (member is PropertyInfo propertyInfo && propertyInfo.Name == "Length")
{
return LengthPropertyToAggregationExpressionTranslator.Translate(context, expression);
}

if (TryTranslateDictionaryProperty(expression, containerTranslation, member, out var translatedDictionaryProperty))
{
return translatedDictionaryProperty;
}

if (TryTranslateKeyValuePairProperty(expression, containerTranslation, member, out var translatedKeyValuePairProperty))
{
return translatedKeyValuePairProperty;
}

if (TryTranslateCollectionCountProperty(expression, containerTranslation, member, out var translatedCount))
{
return translatedCount;
Expand Down Expand Up @@ -121,11 +130,20 @@ private static bool TryTranslateCollectionCountProperty(MemberExpression express
{
if (EnumerableProperty.IsCountProperty(expression))
{
SerializationHelper.EnsureRepresentationIsArray(expression, container.Serializer);
AstExpression ast;

var ast = AstExpression.Size(container.Ast);
var serializer = Int32Serializer.Instance;
if (container.Serializer is IBsonDictionarySerializer dictionarySerializer &&
dictionarySerializer.DictionaryRepresentation == DictionaryRepresentation.Document)
{
ast = AstExpression.Size(AstExpression.ObjectToArray(container.Ast));
}
else
{
SerializationHelper.EnsureRepresentationIsArray(expression, container.Serializer);
ast = AstExpression.Size(container.Ast);
}

var serializer = Int32Serializer.Instance;
result = new TranslatedExpression(expression, ast, serializer);
return true;
}
Expand Down Expand Up @@ -187,5 +205,142 @@ private static bool TryTranslateDateTimeProperty(MemberExpression expression, Tr

return false;
}

private static bool TryTranslateKeyValuePairProperty(MemberExpression expression, TranslatedExpression container, MemberInfo memberInfo, out TranslatedExpression result)
{
result = null;

if (container.Expression.Type.IsGenericType &&
container.Expression.Type.GetGenericTypeDefinition() == typeof(KeyValuePair<,>) &&
container.Serializer is IKeyValuePairSerializerV2 { Representation: BsonType.Array } kvpSerializer)
{
AstExpression ast;
IBsonSerializer serializer;

switch (memberInfo.Name)
{
case "Key":
ast = AstExpression.ArrayElemAt(container.Ast, 0);
serializer = kvpSerializer.KeySerializer;
break;
case "Value":
ast = AstExpression.ArrayElemAt(container.Ast, 1);
serializer = kvpSerializer.ValueSerializer;
break;
default:
throw new ExpressionNotSupportedException(expression);
}
result = new TranslatedExpression(expression, ast, serializer);
return true;
}

return false;
}

private static bool TryTranslateDictionaryProperty(MemberExpression expression, TranslatedExpression container, MemberInfo memberInfo, out TranslatedExpression result)
{
result = null;

if (memberInfo is PropertyInfo propertyInfo &&
propertyInfo.DeclaringType.IsGenericType &&
(propertyInfo.DeclaringType.GetGenericTypeDefinition() == typeof(Dictionary<,>) ||
propertyInfo.DeclaringType.GetGenericTypeDefinition() == typeof(IDictionary<,>)))
{
if (container.Serializer is IBsonDictionarySerializer dictionarySerializer)
{
switch (propertyInfo.Name)
{
case "Count":
{
AstExpression countAst;
switch (dictionarySerializer.DictionaryRepresentation)
{
case DictionaryRepresentation.Document:
countAst = AstExpression.Size(AstExpression.ObjectToArray(container.Ast));
break;
case DictionaryRepresentation.ArrayOfArrays:
case DictionaryRepresentation.ArrayOfDocuments:
countAst = AstExpression.Size(container.Ast);
break;
default:
throw new ExpressionNotSupportedException(expression);
}

var serializer = Int32Serializer.Instance;
result = new TranslatedExpression(expression, countAst, serializer);
return true;
}

case "Keys":
{
AstExpression keysAst;
switch (dictionarySerializer.DictionaryRepresentation)
{
case DictionaryRepresentation.Document:
keysAst = AstExpression.GetField(AstExpression.ObjectToArray(container.Ast), "k");
break;
case DictionaryRepresentation.ArrayOfArrays:
{
var kvp = AstExpression.Var("kvp");
keysAst = AstExpression.Map(
input: container.Ast,
@as: kvp,
@in: AstExpression.ArrayElemAt(kvp, 0));
break;
}
case DictionaryRepresentation.ArrayOfDocuments:
keysAst = AstExpression.GetField(container.Ast, "k");
break;

default:
throw new ExpressionNotSupportedException(expression);
}

var serializer = ArraySerializerHelper.CreateSerializer(dictionarySerializer.KeySerializer);
result = new TranslatedExpression(expression, keysAst, serializer);
return true;
}

case "Values":
{
AstExpression valuesAst;
switch (dictionarySerializer.DictionaryRepresentation)
{
case DictionaryRepresentation.Document:
valuesAst = AstExpression.GetField(AstExpression.ObjectToArray(container.Ast), "v");
break;
case DictionaryRepresentation.ArrayOfArrays:
{
var kvp = AstExpression.Var("kvp");
valuesAst = AstExpression.Map(
input: container.Ast,
@as: kvp,
@in: AstExpression.ArrayElemAt(kvp, 1));
break;
}
case DictionaryRepresentation.ArrayOfDocuments:
valuesAst = AstExpression.GetField(container.Ast, "v");
break;

default:
throw new ExpressionNotSupportedException(expression);
}

var serializer = ArraySerializerHelper.CreateSerializer(dictionarySerializer.ValueSerializer);
result = new TranslatedExpression(expression, valuesAst, serializer);
return true;
}

default:
throw new ExpressionNotSupportedException(expression);
}
}

throw new ExpressionNotSupportedException(expression, because: "serializer does not implement IBsonDictionarySerializer");
}

return false;
}

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,27 +36,65 @@ public static TranslatedExpression Translate(TranslationContext context, MethodC
{
var dictionaryExpression = expression.Object;
var keyExpression = arguments[0];
return TranslateContainsKey(context, expression, dictionaryExpression, keyExpression);
}

throw new ExpressionNotSupportedException(expression);
}

var dictionaryTranslation = ExpressionToAggregationExpressionTranslator.Translate(context, dictionaryExpression);
var dictionarySerializer = GetDictionarySerializer(expression, dictionaryTranslation);
var dictionaryRepresentation = dictionarySerializer.DictionaryRepresentation;
public static TranslatedExpression TranslateContainsKey(TranslationContext context, Expression expression, Expression dictionaryExpression, Expression keyExpression)
{
var dictionaryTranslation = ExpressionToAggregationExpressionTranslator.Translate(context, dictionaryExpression);
var dictionarySerializer = GetDictionarySerializer(expression, dictionaryTranslation);
var dictionaryRepresentation = dictionarySerializer.DictionaryRepresentation;

AstExpression ast;
switch (dictionaryRepresentation)
{
case DictionaryRepresentation.Document:
AstExpression ast;
switch (dictionaryRepresentation)
{
case DictionaryRepresentation.Document:
{
var keyFieldName = GetKeyFieldName(context, expression, keyExpression, dictionarySerializer.KeySerializer);
ast = AstExpression.IsNotMissing(AstExpression.GetField(dictionaryTranslation.Ast, keyFieldName));
break;
}

default:
throw new ExpressionNotSupportedException(expression, because: $"ContainsKey is not supported when DictionaryRepresentation is: {dictionaryRepresentation}");
}
case DictionaryRepresentation.ArrayOfDocuments:
{
var keyFieldName = GetKeyFieldName(context, expression, keyExpression, dictionarySerializer.KeySerializer);
var (valueBinding, valueAst) = AstExpression.UseVarIfNotSimple("value", keyFieldName);
ast = AstExpression.Let(
var: valueBinding,
@in: AstExpression.Reduce(
input: dictionaryTranslation.Ast,
initialValue: false,
@in: AstExpression.Cond(
@if: AstExpression.Var("value"),
@then: true,
@else: AstExpression.Eq(AstExpression.GetField(AstExpression.Var("this"), "k"), valueAst))));
break;
}

case DictionaryRepresentation.ArrayOfArrays:
{
var keyFieldName = GetKeyFieldName(context, expression, keyExpression, dictionarySerializer.KeySerializer);
var (valueBinding, valueAst) = AstExpression.UseVarIfNotSimple("value", keyFieldName);
ast = AstExpression.Let(
var: valueBinding,
@in: AstExpression.Reduce(
input: dictionaryTranslation.Ast,
initialValue: false,
@in: AstExpression.Cond(
@if: AstExpression.Var("value"),
@then: true,
@else: AstExpression.Eq(AstExpression.ArrayElemAt(AstExpression.Var("this"), 0), valueAst))));
break;
}

return new TranslatedExpression(expression, ast, BooleanSerializer.Instance);
default:
throw new ExpressionNotSupportedException(expression, because: $"DictionaryRepresentation: {dictionaryRepresentation} is not supported.");
}

throw new ExpressionNotSupportedException(expression);
return new TranslatedExpression(expression, ast, BooleanSerializer.Instance);
}

private static AstExpression GetKeyFieldName(TranslationContext context, Expression expression, Expression keyExpression, IBsonSerializer keySerializer)
Expand Down
Loading
Loading