Example #1
0
        /// <summary>
        /// Filters a query based upon the predicate provided.
        /// </summary>
        /// <typeparam name="TSource">The type of AVObject being queried for.</typeparam>
        /// <param name="source">The base <see cref="AVQuery{TSource}"/> to which
        /// the predicate will be added.</param>
        /// <param name="predicate">A function to test each AVObject for a condition.
        /// The predicate must be able to be represented by one of the standard Where
        /// functions on AVQuery</param>
        /// <returns>A new AVQuery whose results will match the given predicate as
        /// well as the Source's filters.</returns>
        public static AVQuery <TSource> Where <TSource>(
            this AVQuery <TSource> source, Expression <Func <TSource, bool> > predicate)
            where TSource : AVObject
        {
            // Handle top-level logic operators && and ||
            var binaryExpression = predicate.Body as BinaryExpression;

            if (binaryExpression != null)
            {
                if (binaryExpression.NodeType == ExpressionType.AndAlso)
                {
                    return(source
                           .Where(Expression.Lambda <Func <TSource, bool> >(
                                      binaryExpression.Left, predicate.Parameters))
                           .Where(Expression.Lambda <Func <TSource, bool> >(
                                      binaryExpression.Right, predicate.Parameters)));
                }
                if (binaryExpression.NodeType == ExpressionType.OrElse)
                {
                    var left = source.Where(Expression.Lambda <Func <TSource, bool> >(
                                                binaryExpression.Left, predicate.Parameters));
                    var right = source.Where(Expression.Lambda <Func <TSource, bool> >(
                                                 binaryExpression.Right, predicate.Parameters));
                    return(left.Or(right));
                }
            }

            var normalized = new WhereNormalizer().Visit(predicate.Body);

            var methodCallExpr = normalized as MethodCallExpression;

            if (methodCallExpr != null)
            {
                return(source.WhereMethodCall(predicate, methodCallExpr));
            }

            var binaryExpr = normalized as BinaryExpression;

            if (binaryExpr != null)
            {
                return(source.WhereBinaryExpression(predicate, binaryExpr));
            }

            var unaryExpr = normalized as UnaryExpression;

            if (unaryExpr != null && unaryExpr.NodeType == ExpressionType.Not)
            {
                var node = unaryExpr.Operand as MethodCallExpression;
                if (IsParseObjectGet(node) && (node.Type == typeof(bool) || node.Type == typeof(bool?)))
                {
                    // This is a raw boolean field access like 'where !obj.Get<bool>("foo")'
                    return(source.WhereNotEqualTo(GetValue(node.Arguments[0]) as string, true));
                }
            }

            throw new InvalidOperationException(
                      "Encountered an unsupported expression for ParseQueries.");
        }
Example #2
0
        /// <summary>
        /// Converts a normalized binary expression into the appropriate AVQuery clause.
        /// </summary>
        private static AVQuery <T> WhereBinaryExpression <T>(
            this AVQuery <T> source, Expression <Func <T, bool> > expression, BinaryExpression node)
            where T : AVObject
        {
            var leftTransformed = new ObjectNormalizer().Visit(node.Left) as MethodCallExpression;

            if (!(IsParseObjectGet(leftTransformed) &&
                  leftTransformed.Object == expression.Parameters[0]))
            {
                throw new InvalidOperationException(
                          "Where expressions must have one side be a field operation on a AVObject.");
            }

            var fieldPath   = GetValue(leftTransformed.Arguments[0]) as string;
            var filterValue = GetValue(node.Right);

            if (filterValue != null && !AVEncoder.IsValidType(filterValue))
            {
                throw new InvalidOperationException(
                          "Where clauses must use types compatible with AVObjects.");
            }

            switch (node.NodeType)
            {
            case ExpressionType.GreaterThan:
                return(source.WhereGreaterThan(fieldPath, filterValue));

            case ExpressionType.GreaterThanOrEqual:
                return(source.WhereGreaterThanOrEqualTo(fieldPath, filterValue));

            case ExpressionType.LessThan:
                return(source.WhereLessThan(fieldPath, filterValue));

            case ExpressionType.LessThanOrEqual:
                return(source.WhereLessThanOrEqualTo(fieldPath, filterValue));

            case ExpressionType.Equal:
                return(source.WhereEqualTo(fieldPath, filterValue));

            case ExpressionType.NotEqual:
                return(source.WhereNotEqualTo(fieldPath, filterValue));

            default:
                throw new InvalidOperationException(
                          "Where expressions do not support this operator.");
            }
        }
Example #3
0
        /// <summary>
        /// Converts a normalized method call expression into the appropriate AVQuery clause.
        /// </summary>
        private static AVQuery <T> WhereMethodCall <T>(
            this AVQuery <T> source, Expression <Func <T, bool> > expression, MethodCallExpression node)
            where T : AVObject
        {
            if (IsParseObjectGet(node) && (node.Type == typeof(bool) || node.Type == typeof(bool?)))
            {
                // This is a raw boolean field access like 'where obj.Get<bool>("foo")'
                return(source.WhereEqualTo(GetValue(node.Arguments[0]) as string, true));
            }

            MethodInfo translatedMethod;

            if (functionMappings.TryGetValue(node.Method, out translatedMethod))
            {
                var objTransformed = new ObjectNormalizer().Visit(node.Object) as MethodCallExpression;
                if (!(IsParseObjectGet(objTransformed) &&
                      objTransformed.Object == expression.Parameters[0]))
                {
                    throw new InvalidOperationException(
                              "The left-hand side of a supported function call must be a AVObject field access.");
                }
                var fieldPath   = GetValue(objTransformed.Arguments[0]);
                var containedIn = GetValue(node.Arguments[0]);
                var queryType   = translatedMethod.DeclaringType.GetGenericTypeDefinition()
                                  .MakeGenericType(typeof(T));
                translatedMethod = ReflectionHelpers.GetMethod(queryType,
                                                               translatedMethod.Name,
                                                               translatedMethod.GetParameters().Select(p => p.ParameterType).ToArray());
                return(translatedMethod.Invoke(source, new[] { fieldPath, containedIn }) as AVQuery <T>);
            }

            if (node.Arguments[0] == expression.Parameters[0])
            {
                // obj.ContainsKey("foo") --> query.WhereExists("foo")
                if (node.Method == containsKeyMethod)
                {
                    return(source.WhereExists(GetValue(node.Arguments[1]) as string));
                }
                // !obj.ContainsKey("foo") --> query.WhereDoesNotExist("foo")
                if (node.Method == notContainsKeyMethod)
                {
                    return(source.WhereDoesNotExist(GetValue(node.Arguments[1]) as string));
                }
            }

            if (node.Method.IsGenericMethod)
            {
                if (node.Method.GetGenericMethodDefinition() == containsMethod)
                {
                    // obj.Get<IList<T>>("path").Contains(someValue)
                    if (IsParseObjectGet(node.Arguments[0] as MethodCallExpression))
                    {
                        return(source.WhereEqualTo(
                                   GetValue(((MethodCallExpression)node.Arguments[0]).Arguments[0]) as string,
                                   GetValue(node.Arguments[1])));
                    }
                    // someList.Contains(obj.Get<T>("path"))
                    if (IsParseObjectGet(node.Arguments[1] as MethodCallExpression))
                    {
                        var collection = GetValue(node.Arguments[0]) as System.Collections.IEnumerable;
                        return(source.WhereContainedIn(
                                   GetValue(((MethodCallExpression)node.Arguments[1]).Arguments[0]) as string,
                                   collection.Cast <object>()));
                    }
                }

                if (node.Method.GetGenericMethodDefinition() == notContainsMethod)
                {
                    // !obj.Get<IList<T>>("path").Contains(someValue)
                    if (IsParseObjectGet(node.Arguments[0] as MethodCallExpression))
                    {
                        return(source.WhereNotEqualTo(
                                   GetValue(((MethodCallExpression)node.Arguments[0]).Arguments[0]) as string,
                                   GetValue(node.Arguments[1])));
                    }
                    // !someList.Contains(obj.Get<T>("path"))
                    if (IsParseObjectGet(node.Arguments[1] as MethodCallExpression))
                    {
                        var collection = GetValue(node.Arguments[0]) as System.Collections.IEnumerable;
                        return(source.WhereNotContainedIn(
                                   GetValue(((MethodCallExpression)node.Arguments[1]).Arguments[0]) as string,
                                   collection.Cast <object>()));
                    }
                }
            }
            throw new InvalidOperationException(node.Method + " is not a supported method call in a where expression.");
        }