protected virtual Expression VisitProjection(ProjectionExpression projection)
 {
     var source = (SelectExpression)Visit(projection.Source);
     var projector = Visit(projection.Projector);
     if (source != projection.Source || projector != projection.Projector)
         return new ProjectionExpression(source, projector, projection.Aggregator);
     return projection;
 }
        protected override Expression VisitProjection(ProjectionExpression projection)
        {
            var queryObject = new MongoQueryObjectBuilder().Build(projection);
            queryObject.Projector = new ProjectionBuilder().Build(projection.Projector, queryObject.DocumentType, "document", queryObject.IsMapReduce);
            queryObject.Aggregator = (LambdaExpression)Visit(projection.Aggregator);

            Expression result = Expression.Call(
                _provider,
                "ExecuteQueryObject",
                Type.EmptyTypes,
                Expression.Constant(queryObject, typeof(MongoQueryObject)));

            if (queryObject.Aggregator != null)
                result = Expression.Convert(result, queryObject.Aggregator.Body.Type);
            else
                result = Expression.Convert(result, typeof(IEnumerable<>).MakeGenericType(queryObject.Projector.Body.Type));

            return result;
        }
        protected virtual Expression BindGroupBy(Expression source, LambdaExpression keySelector, LambdaExpression elementSelector, LambdaExpression resultSelector)
        {
            var projection = VisitSequence(source);

            _map[keySelector.Parameters[0]] = projection.Projector;
            var keyExpression = Visit(keySelector.Body);

            var elementExpression = projection.Projector;
            if (elementSelector != null)
            {
                _map[elementSelector.Parameters[0]] = projection.Projector;
                elementExpression = Visit(elementSelector.Body);
            }

            var subqueryBasis = VisitSequence(source);
            _map[keySelector.Parameters[0]] = subqueryBasis.Projector;
            var subqueryKeyExpression = Visit(keySelector.Body);

            var subqueryCorrelation = Expression.Equal(keyExpression, subqueryKeyExpression);

            var subqueryElementExpression = subqueryBasis.Projector;
            if (elementSelector != null)
            {
                _map[elementSelector.Parameters[0]] = subqueryBasis.Projector;
                subqueryElementExpression = Visit(elementSelector.Body);
            }

            var elementAlias = new Alias();
            var elementProjection = _projector.ProjectFields(subqueryElementExpression, elementAlias, subqueryBasis.Source.Alias);
            var elementSubquery =
                new ProjectionExpression(
                    new SelectExpression(elementAlias, elementProjection.Fields, subqueryBasis.Source, subqueryCorrelation),
                    elementProjection.Projector);

            var alias = new Alias();

            var info = new GroupByInfo(alias, elementExpression);
            _groupByMap[elementSubquery] = info;

            Expression resultExpression;
            if (resultSelector != null)
            {
                var saveGroupElement = _currentGroupElement;
                _currentGroupElement = elementSubquery;

                _map[resultSelector.Parameters[0]] = keyExpression;
                _map[resultSelector.Parameters[1]] = elementSubquery;
                resultExpression = Visit(resultSelector.Body);
                _currentGroupElement = saveGroupElement;
            }
            else
            {
                resultExpression = Expression.New(
                    typeof(Grouping<,>).MakeGenericType(keyExpression.Type, subqueryElementExpression.Type).GetConstructors()[0],
                    new[] { keyExpression, elementSubquery });
            }

            var fieldProjection = _projector.ProjectFields(resultExpression, alias, projection.Source.Alias);

            var projectedElementSubquery = ((NewExpression)fieldProjection.Projector).Arguments[1];
            _groupByMap[projectedElementSubquery] = info;

            return new ProjectionExpression(
                new SelectExpression(alias, new FieldDeclaration[0], projection.Source, null, null, keyExpression, false, null, null),
                fieldProjection.Projector);
        }
        private Expression BindFirstOrSingle(Expression source, LambdaExpression predicate, string kind, bool isRoot)
        {
            var projection = VisitSequence(source);
            Expression where = null;
            if (predicate != null)
            {
                _map[predicate.Parameters[0]] = projection.Projector;
                where = Visit(predicate.Body);
            }

            Expression take = kind.StartsWith("First") ? Expression.Constant(1) : null;
            if (take == null & kind.StartsWith("Single"))
                take = Expression.Constant(2);

            if (take != null || where != null)
            {
                var alias = new Alias();
                var fieldProjection = _projector.ProjectFields(projection.Projector, alias, projection.Source.Alias);
                projection = new ProjectionExpression(
                    new SelectExpression(alias, fieldProjection.Fields, projection.Source, where, null, null, false, null, take),
                    fieldProjection.Projector);
            }
            if (isRoot)
            {
                var elementType = projection.Projector.Type;
                var p = Expression.Parameter(typeof(IEnumerable<>).MakeGenericType(elementType), "p");
                var lambda = Expression.Lambda(Expression.Call(typeof(Enumerable), kind, new[] { elementType }, p), p);
                return new ProjectionExpression(projection.Source, projection.Projector, lambda);
            }
            return projection;
        }
        protected virtual bool CompareProjection(ProjectionExpression a, ProjectionExpression b)
        {
            if (!Compare(a.Source, b.Source))
                return false;

            var save = _aliasScope;
            try
            {
                _aliasScope = new ScopedDictionary<Alias, Alias>(_aliasScope);
                _aliasScope.Add(a.Source.Alias, b.Source.Alias);

                return Compare(a.Projector, b.Projector)
                    && Compare(a.Aggregator, b.Aggregator)
                    && a.IsSingleton == b.IsSingleton;
            }
            finally
            {
                _aliasScope = save;
            }
        }