public override Expression Bind(ProjectionExpression projection, ProjectionBindingContext context, MethodCallExpression node, IEnumerable<Expression> arguments) { LambdaExpression aggregator; if (node.Method.Name.EndsWith("Async")) { aggregator = CreateAsyncAggregator(); } else { aggregator = CreateSyncAggregator(); } var source = projection.Source; var argument = arguments.FirstOrDefault(); if (argument != null && ExtensionExpressionVisitor.IsLambda(argument)) { source = BindPredicate(projection, context, source, argument); } source = new TakeExpression(source, 1); var serializer = context.SerializerRegistry.GetSerializer(typeof(int)); var accumulator = new AccumulatorExpression(typeof(int), AccumulatorType.Count, null); var serializationAccumulator = new SerializationExpression( accumulator, new BsonSerializationInfo("__agg0", serializer, serializer.ValueType)); var rootAccumulator = new RootAccumulatorExpression(source, serializationAccumulator); return new ProjectionExpression( rootAccumulator, serializationAccumulator, aggregator); }
protected override Expression VisitNew(NewExpression node) { var newNode = (NewExpression)base.VisitNew(node); if (newNode.Type.IsGenericType && newNode.Type.GetGenericTypeDefinition() == typeof(HashSet<>) && newNode.Arguments.Count == 1 && newNode.Arguments[0] is AccumulatorExpression && ((AccumulatorExpression)newNode.Arguments[0]).AccumulatorType == AccumulatorType.Push) { Guid correlationId = Guid.Empty; if (_groupMap == null || TryGetCorrelatedGroup(node.Arguments[0], out correlationId)) { Expression accumulator = new AccumulatorExpression( newNode.Type, AccumulatorType.AddToSet, ((AccumulatorExpression)newNode.Arguments[0]).Argument); if (_groupMap != null) { accumulator = new CorrelatedAccumulatorExpression( correlationId, (AccumulatorExpression)accumulator); } return accumulator; } } return newNode; }
public CorrelatedAccumulatorExpression Update(AccumulatorExpression accumulator) { if (accumulator != _accumulator) { return(new CorrelatedAccumulatorExpression(_correlationId, accumulator)); } return(this); }
public CorrelatedAccumulatorExpression Update(AccumulatorExpression accumulator) { if (accumulator != _accumulator) { return new CorrelatedAccumulatorExpression(_correlationId, accumulator); } return this; }
public override Expression Bind(Expressions.ProjectionExpression projection, ProjectionBindingContext context, System.Linq.Expressions.MethodCallExpression node, IEnumerable<System.Linq.Expressions.Expression> arguments) { var aggregatorName = "Single"; var returnType = node.Method.ReturnType; if (node.Method.Name.EndsWith("Async")) { aggregatorName += "Async"; returnType = returnType.GetGenericArguments()[0]; } var aggregator = CreateAggregator(aggregatorName, returnType); var source = projection.Source; var argument = arguments.FirstOrDefault(); if (argument != null && ExtensionExpressionVisitor.IsLambda(argument)) { var lambda = ExtensionExpressionVisitor.GetLambda(argument); var binder = new AccumulatorBinder(context.GroupMap, context.SerializerRegistry); binder.RegisterParameterReplacement(lambda.Parameters[0], projection.Projector); argument = binder.Bind(lambda.Body); } else { argument = projection.Projector; var select = source as SelectExpression; if (select != null) { source = select.Source; } } var serializer = context.SerializerRegistry.GetSerializer(returnType); var accumulator = new AccumulatorExpression(returnType, AccumulatorType, argument); var serializationAccumulator = new SerializationExpression( accumulator, new BsonSerializationInfo("__agg0", serializer, serializer.ValueType)); var rootAccumulator = new RootAccumulatorExpression(source, serializationAccumulator); return new ProjectionExpression( rootAccumulator, serializationAccumulator, aggregator); }
protected internal override Expression VisitPipeline(PipelineExpression node) { Guid correlationId; if (TryGetCorrelatedGroup(node.Source, out correlationId)) { AccumulatorType accumulatorType; Expression argument; if (TryGetAccumulatorTypeAndArgument(node, out accumulatorType, out argument)) { var accumulator = new AccumulatorExpression( node.Type, "__agg" + _count++, _bindingContext.GetSerializer(node.Type, argument), accumulatorType, argument); return new CorrelatedExpression( correlationId, accumulator); } } return node; }
protected internal virtual Expression VisitAccumulator(AccumulatorExpression node) { return(node.Update(Visit(node.Argument))); }
public CorrelatedAccumulatorExpression(Guid correlationId, AccumulatorExpression accumulator) { _correlationId = correlationId; _accumulator = Ensure.IsNotNull(accumulator, "accumulator"); }
protected internal virtual Expression VisitAccumulator(AccumulatorExpression node) { return node.Update(Visit(node.Argument)); }
private BsonValue TranslateAccumulator(AccumulatorExpression node) { switch (node.AccumulatorType) { case AccumulatorType.AddToSet: return new BsonDocument("$addToSet", TranslateValue(node.Argument)); case AccumulatorType.Average: return new BsonDocument("$avg", TranslateValue(node.Argument)); case AccumulatorType.First: return new BsonDocument("$first", TranslateValue(node.Argument)); case AccumulatorType.Last: return new BsonDocument("$last", TranslateValue(node.Argument)); case AccumulatorType.Max: return new BsonDocument("$max", TranslateValue(node.Argument)); case AccumulatorType.Min: return new BsonDocument("$min", TranslateValue(node.Argument)); case AccumulatorType.Push: return new BsonDocument("$push", TranslateValue(node.Argument)); case AccumulatorType.StandardDeviationPopulation: return new BsonDocument("$stdDevPop", TranslateValue(node.Argument)); case AccumulatorType.StandardDeviationSample: return new BsonDocument("$stdDevSamp", TranslateValue(node.Argument)); case AccumulatorType.Sum: return new BsonDocument("$sum", TranslateValue(node.Argument)); } // we should never ever get here. var message = string.Format("Unrecognized aggregation type in the expression tree {0}.", node.ToString()); throw new MongoInternalException(message); }
// private methods private bool CompareAccumulator(AccumulatorExpression a, AccumulatorExpression b) { return(a.AccumulatorType == b.AccumulatorType && Compare(a.Argument, b.Argument)); }
protected internal override Expression VisitAccumulator(AccumulatorExpression node) { return node; }
private Expression VisitCorrelatedGroup(CorrelatedExpression node) { var groupExpression = (GroupByExpression)node.Expression; if (_accumulatorLookup != null && _accumulatorLookup.Contains(node.CorrelationId)) { var source = Visit(groupExpression.Source); var accumulators = new List<AccumulatorExpression>(); var fieldExpressions = new List<FieldExpression>(); var comparer = new ExpressionComparer(); foreach (var correlatedAccumulator in _accumulatorLookup[node.CorrelationId]) { var index = accumulators.FindIndex(x => comparer.Compare((Expression)x, correlatedAccumulator.Expression)); FieldExpression fieldExpression; if (index == -1) { var accumulator = (AccumulatorExpression)correlatedAccumulator.Expression; // TODO: might not need to do any renames... accumulator = new AccumulatorExpression( accumulator.Type, "__agg" + accumulators.Count, accumulator.Serializer, accumulator.AccumulatorType, accumulator.Argument); accumulators.Add(accumulator); fieldExpression = new FieldExpression(accumulator.FieldName, accumulator.Serializer); fieldExpressions.Add(fieldExpression); } else { fieldExpression = fieldExpressions[index]; } _accumulatorReplacementMap[correlatedAccumulator] = fieldExpression; } groupExpression = new GroupByExpression( groupExpression.Type, source, groupExpression.KeySelector, accumulators.AsReadOnly()); } return Visit(groupExpression); }
private bool TryBindAccumulatorExpression(MethodCallExpression node, out Expression accumulator) { AccumulatorType accumulatorType; if (TryGetAccumulatorType(node.Method.Name, out accumulatorType)) { Guid correlationId = Guid.Empty; if (_groupMap == null || TryGetCorrelatedGroup(node.Arguments[0], out correlationId)) { accumulator = new AccumulatorExpression(node.Type, accumulatorType, GetAccumulatorArgument(node)); if (_groupMap != null) { accumulator = new CorrelatedAccumulatorExpression( correlationId, (AccumulatorExpression)accumulator); } return true; } } accumulator = null; return false; }