Skip to content
Merged
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
31 changes: 31 additions & 0 deletions src/MongoDB.Bson/Serialization/BsonSerializer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -778,6 +778,37 @@ internal static void EnsureKnownTypesAreRegistered(Type nominalType)
}
}

// internal static methods
internal static BsonValue[] GetDiscriminatorsForTypeAndSubTypes(Type type)
{
// note: EnsureKnownTypesAreRegistered handles its own locking so call from outside any lock
EnsureKnownTypesAreRegistered(type);

var discriminators = new List<BsonValue>();

__configLock.EnterReadLock();
try
{
foreach (var entry in __discriminators)
{
var discriminator = entry.Key;
var actualTypes = entry.Value;

var matchingType = actualTypes.SingleOrDefault(t => t == type || t.IsSubclassOf(type));
if (matchingType != null)
{
discriminators.Add(discriminator);
}
}
}
finally
{
__configLock.ExitReadLock();
}

return discriminators.OrderBy(x => x).ToArray();
}

// private static methods
private static void CreateSerializerRegistry()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,17 @@
*/

using System;
using System.Collections.Concurrent;

namespace MongoDB.Bson.Serialization.Conventions
{
/// <summary>
/// Represents a discriminator convention where the discriminator is provided by the class map of the actual type.
/// </summary>
public class ScalarDiscriminatorConvention : StandardDiscriminatorConvention
public class ScalarDiscriminatorConvention : StandardDiscriminatorConvention, IScalarDiscriminatorConvention
{
private readonly ConcurrentDictionary<Type, BsonValue[]> _cachedTypeAndSubTypeDiscriminators = new();

// constructors
/// <summary>
/// Initializes a new instance of the ScalarDiscriminatorConvention class.
Expand Down Expand Up @@ -52,5 +55,11 @@ public override BsonValue GetDiscriminator(Type nominalType, Type actualType)
return null;
}
}

/// <inheritdoc/>
public BsonValue[] GetDiscriminatorsForTypeAndSubTypes(Type type)
{
return _cachedTypeAndSubTypeDiscriminators.GetOrAdd(type, BsonSerializer.GetDiscriminatorsForTypeAndSubTypes);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@
using MongoDB.Bson;
using MongoDB.Driver.Linq.Linq3Implementation.Ast.Expressions;
using MongoDB.Driver.Linq.Linq3Implementation.Ast.Filters;
using MongoDB.Driver.Linq.Linq3Implementation.Ast.Stages;
using MongoDB.Driver.Linq.Linq3Implementation.Ast.Visitors;
using MongoDB.Driver.Linq.Linq3Implementation.Misc;

namespace MongoDB.Driver.Linq.Linq3Implementation.Ast.Optimizers
{
Expand Down Expand Up @@ -351,6 +353,33 @@ elemMatchOperation.Filter is AstFieldOperationFilter elemFilter &&
}
}

public override AstNode VisitFilterExpression(AstFilterExpression node)
{
var inputExpression = VisitAndConvert(node.Input);
var condExpression = VisitAndConvert(node.Cond);
var limitExpression = VisitAndConvert(node.Limit);

if (condExpression is AstConstantExpression condConstantExpression &&
condConstantExpression.Value is BsonBoolean condBsonBoolean)
{
if (condBsonBoolean.Value)
{
// { $filter : { input : <input>, as : "x", cond : true } } => <input>
if (limitExpression == null)
{
return inputExpression;
}
}
else
{
// { $filter : { input : <input>, as : "x", cond : false, optional-limit } } => []
return AstExpression.Constant(new BsonArray());
}
}

return node.Update(inputExpression, condExpression, limitExpression);
}

public override AstNode VisitGetFieldExpression(AstGetFieldExpression node)
{
if (TrySimplifyAsFieldPath(node, out var simplified))
Expand Down Expand Up @@ -448,6 +477,26 @@ public override AstNode VisitNotFilterOperation(AstNotFilterOperation node)
return base.VisitNotFilterOperation(node);
}

public override AstNode VisitPipeline(AstPipeline node)
{
var stages = VisitAndConvert(node.Stages);

// { $match : { } } => remove redundant stage
if (stages.Any(stage => IsMatchEverythingStage(stage)))
{
stages = stages.Where(stage => !IsMatchEverythingStage(stage)).AsReadOnlyList();
}

return node.Update(stages);

static bool IsMatchEverythingStage(AstStage stage)
{
return
stage is AstMatchStage matchStage &&
matchStage.Filter is AstMatchesEverythingFilter;
}
}

public override AstNode VisitSliceExpression(AstSliceExpression node)
{
node = (AstSliceExpression)base.VisitSliceExpression(node);
Expand Down Expand Up @@ -498,15 +547,34 @@ arrayConstant.Value is BsonArray bsonArrayConstant &&

public override AstNode VisitUnaryExpression(AstUnaryExpression node)
{
var arg = VisitAndConvert(node.Arg);

// { $first : <arg> } => { $arrayElemAt : [<arg>, 0] } (or -1 for $last)
if (node.Operator == AstUnaryOperator.First || node.Operator == AstUnaryOperator.Last)
{
var simplifiedArg = VisitAndConvert(node.Arg);
var index = node.Operator == AstUnaryOperator.First ? 0 : -1;
return AstExpression.ArrayElemAt(simplifiedArg, index);
return AstExpression.ArrayElemAt(arg, index);
}

// { $not : booleanConstant } => !booleanConstant
if (node.Operator is AstUnaryOperator.Not &&
arg is AstConstantExpression argConstantExpression &&
argConstantExpression.Value is BsonBoolean argBsonBoolean)
{
return AstExpression.Constant(!argBsonBoolean.Value);
}

// { $not : { $eq : [expr1, expr2] } } => { $ne : [expr1, expr2] }
// { $not : { $ne : [expr1, expr2] } } => { $eq : [expr1, expr2] }
if (node.Operator is AstUnaryOperator.Not &&
arg is AstBinaryExpression argBinaryExpression &&
argBinaryExpression.Operator is AstBinaryOperator.Eq or AstBinaryOperator.Ne)
{
var oppositeComparisonOperator = argBinaryExpression.Operator == AstBinaryOperator.Eq ? AstBinaryOperator.Ne : AstBinaryOperator.Eq;
return AstExpression.Binary(oppositeComparisonOperator, argBinaryExpression.Arg1, argBinaryExpression.Arg2);
}

return base.VisitUnaryExpression(node);
return node.Update(arg);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
/* 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.Linq;
using System.Linq.Expressions;
using System.Reflection;
using MongoDB.Bson.Serialization;
using MongoDB.Bson.Serialization.Conventions;
using MongoDB.Driver.Linq.Linq3Implementation.Ast.Expressions;
using MongoDB.Driver.Linq.Linq3Implementation.Misc;
using MongoDB.Driver.Linq.Linq3Implementation.Reflection;
using MongoDB.Driver.Linq.Linq3Implementation.Serializers;

namespace MongoDB.Driver.Linq.Linq3Implementation.Translators.ExpressionToAggregationExpressionTranslators.MethodTranslators
{
internal static class OfTypeMethodToAggregationExpressionTranslator
{
private static MethodInfo[] __ofTypeMethods =
{
EnumerableMethod.OfType,
QueryableMethod.OfType
};

public static TranslatedExpression Translate(TranslationContext context, MethodCallExpression expression)
{
var method = expression.Method;
var arguments = expression.Arguments;

if (method.IsOneOf(__ofTypeMethods))
{
var sourceExpression = arguments[0];
var sourceTranslation = ExpressionToAggregationExpressionTranslator.TranslateEnumerable(context, sourceExpression);
NestedAsQueryableHelper.EnsureQueryableMethodHasNestedAsQueryableSource(expression, sourceTranslation);

var sourceAst = sourceTranslation.Ast;
var sourceSerializer = sourceTranslation.Serializer;
if (sourceSerializer is IWrappedValueSerializer wrappedValueSerializer)
{
sourceAst = AstExpression.GetField(sourceAst, wrappedValueSerializer.FieldName);
sourceSerializer = wrappedValueSerializer.ValueSerializer;
}
var itemSerializer = ArraySerializerHelper.GetItemSerializer(sourceSerializer);

var nominalType = itemSerializer.ValueType;
var nominalTypeSerializer = itemSerializer;
var actualType = method.GetGenericArguments().Single();
var actualTypeSerializer = BsonSerializer.LookupSerializer(actualType);

AstExpression ast;
if (nominalType == actualType)
{
ast = sourceAst;
}
else
{
var discriminatorConvention = nominalTypeSerializer.GetDiscriminatorConvention();
var itemVar = AstExpression.Var("item");
var discriminatorField = AstExpression.GetField(itemVar, discriminatorConvention.ElementName);

var ofTypeExpression = discriminatorConvention switch
{
IHierarchicalDiscriminatorConvention hierarchicalDiscriminatorConvention => DiscriminatorAstExpression.TypeIs(discriminatorField, hierarchicalDiscriminatorConvention, nominalType, actualType),
IScalarDiscriminatorConvention scalarDiscriminatorConvention => DiscriminatorAstExpression.TypeIs(discriminatorField, scalarDiscriminatorConvention, nominalType, actualType),
_ => throw new ExpressionNotSupportedException(expression, because: "OfType is not supported with the configured discriminator convention")
};

ast = AstExpression.Filter(
input: sourceAst,
cond: ofTypeExpression,
@as: "item");
}

var resultSerializer = NestedAsQueryableSerializer.CreateIEnumerableOrNestedAsQueryableSerializer(expression.Type, actualTypeSerializer);
return new TranslatedExpression(expression, ast, resultSerializer);
}

throw new ExpressionNotSupportedException(expression);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,15 @@ public static TranslatedExpression Translate(TranslationContext context, MethodC
var sourceExpression = arguments[0];
var sourceTranslation = ExpressionToAggregationExpressionTranslator.TranslateEnumerable(context, sourceExpression);
NestedAsQueryableHelper.EnsureQueryableMethodHasNestedAsQueryableSource(expression, sourceTranslation);
var itemSerializer = ArraySerializerHelper.GetItemSerializer(sourceTranslation.Serializer);

var sourceAst = sourceTranslation.Ast;
var sourceSerializer = sourceTranslation.Serializer;
if (sourceSerializer is IWrappedValueSerializer wrappedValueSerializer)
{
sourceAst = AstExpression.GetField(sourceAst, wrappedValueSerializer.FieldName);
sourceSerializer = wrappedValueSerializer.ValueSerializer;
}
var itemSerializer = ArraySerializerHelper.GetItemSerializer(sourceSerializer);

var predicateLambda = ExpressionHelper.UnquoteLambdaIfQueryableMethod(method, arguments[1]);
var predicateParameter = predicateLambda.Parameters[0];
Expand All @@ -57,7 +65,7 @@ public static TranslatedExpression Translate(TranslationContext context, MethodC
}

var ast = AstExpression.Filter(
sourceTranslation.Ast,
sourceAst,
predicateTranslation.Ast,
@as: predicateSymbol.Var.Name,
limitTranslation?.Ast);
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,7 @@
* limitations under the License.
*/

using System.Linq;
using System.Linq.Expressions;
using MongoDB.Bson;
using MongoDB.Bson.Serialization;
using MongoDB.Bson.Serialization.Conventions;
using MongoDB.Bson.Serialization.Serializers;
Expand Down
Loading