Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Binary and/or filter #210

Closed
frankiDotNet opened this issue Mar 2, 2018 · 7 comments
Closed

Binary and/or filter #210

frankiDotNet opened this issue Mar 2, 2018 · 7 comments

Comments

@frankiDotNet
Copy link

frankiDotNet commented Mar 2, 2018

Is it possible to create a binary and/or filter.

I would sy you could create as operator '&' and '|'.

so that a filter like

dataSource.filter([
[ "value", "&", 3, 1]
]);

dataSource.filter([
[ "value", "|", 1, 1]
]);
the linq expression would be (_ => (_.value | 1) == 1)

@frankiDotNet
Copy link
Author

frankiDotNet commented Mar 2, 2018

I think this should work, testet it with your examples and it returned the right result.
So like described this works: [ "value", "&", 3, 1] => (value & 3) == 1
and this also works: [ "value", "&", 3] => (value & 3) == 3

    Expression CompileBinary(ParameterExpression dataItemExpr, IList criteriaJson) {
            var hasExplicitOperation = criteriaJson.Count > 2;

            var clientAccessor = Convert.ToString(criteriaJson[0]);
            var clientOperation = hasExplicitOperation ? Convert.ToString(criteriaJson[1]).ToLower() : "=";
            var clientValue = criteriaJson[hasExplicitOperation ? 2 : 1];
            var isStringOperation = clientOperation == CONTAINS || clientOperation == NOT_CONTAINS || clientOperation == STARTS_WITH || clientOperation == ENDS_WITH;

            var accessorExpr = CompileAccessorExpression(dataItemExpr, clientAccessor, isStringOperation);

            if(isStringOperation) {
                return CompileStringFunction(accessorExpr, clientOperation, Convert.ToString(clientValue));

            } else {
                var useDynamicBinding = accessorExpr.Type == typeof(Object);
                var expressionType = TranslateBinaryOperation(clientOperation);
                
                if(!useDynamicBinding) {
                    try {
                        clientValue = Utils.ConvertClientValue(clientValue, accessorExpr.Type);
                    } catch {
                        return Expression.Constant(false);
                    }
                }
                
                Expression valueExpr = Expression.Constant(clientValue);

                if(accessorExpr.Type != null && clientValue != null && clientValue.GetType() != accessorExpr.Type)
                    valueExpr = Expression.Convert(valueExpr, accessorExpr.Type);

                if(accessorExpr.Type == typeof(String) && IsInequality(expressionType)) {
                    if(clientValue == null)
                        valueExpr = Expression.Constant(null, typeof(String));

                    var compareMethod = typeof(String).GetMethod(nameof(String.Compare), new[] { typeof(String), typeof(String) });
                    accessorExpr = Expression.Call(null, compareMethod, accessorExpr, valueExpr);
                    valueExpr = Expression.Constant(0);
                } else if(useDynamicBinding) {
                    accessorExpr = Expression.Call(typeof(Utils).GetMethod(nameof(Utils.DynamicCompare)), accessorExpr, valueExpr);
                    valueExpr = Expression.Constant(0);
                }
// This part is new
                if(hasExplicitOperation && (expressionType == ExpressionType.And || expressionType == ExpressionType.Or)) {
                    var resultValue = Utils.ConvertClientValue(criteriaJson[criteriaJson.Count > 3 ? 3 : 2], accessorExpr.Type);
                    Expression resultValueExpression = Expression.Constant(resultValue);

                    switch(expressionType) {
                            case ExpressionType.And:
                                valueExpr = Expression.And(accessorExpr, valueExpr);
                                break;
                        case ExpressionType.Or:
                            valueExpr = Expression.Or(accessorExpr, valueExpr);
                            break;
                    }
                    return Expression.MakeBinary(ExpressionType.Equal, valueExpr, resultValueExpression);
                }

                return Expression.MakeBinary(expressionType, accessorExpr, valueExpr);
            }

        }
    ExpressionType TranslateBinaryOperation(string clientOperation) {
            switch(clientOperation) {
                case "=":
                    return ExpressionType.Equal;

                case "<>":
                    return ExpressionType.NotEqual;

                case ">":
                    return ExpressionType.GreaterThan;

                case ">=":
                    return ExpressionType.GreaterThanOrEqual;

                case "<":
                    return ExpressionType.LessThan;

                case "<=":
                    return ExpressionType.LessThanOrEqual;

                case "&":
                    return ExpressionType.And;

                case "|":
                    return ExpressionType.Or;
            }

            throw new NotSupportedException();
        }

@frankiDotNet frankiDotNet changed the title Binary an/or filter Binary and/or filter Mar 2, 2018
@AlekseyMartynov
Copy link
Contributor

Is it possible, in your case, to use calculated properties instead of complex filters?
For example, if bitwise operators are used to check flags:

class Model {
    public int Flags;

    public bool HasFlag1 {
        get { return (Flags & 1) == 1; }
    }
}

A simple boolean filter can be used then.

In filter expressions, we support filtering functionality of DevExtreme data layer (docs). Technically we can extend the compiler as you suggest but it will be an undocumented extension of the spec.

@frankiDotNet
Copy link
Author

Thanks for the response! I can create a calculated field, but it would be nice to have such functionality.. Maybe you could start a request to DevExtreme..

@AlekseyMartynov
Copy link
Contributor

Maybe you could start a request to DevExtreme..

Actually, we are the same team.
We'll count your feedback.
Thanks.

@frankiDotNet
Copy link
Author

Is there any change to this or will there be an update for binary operations?

@AlekseyMartynov
Copy link
Contributor

Hello @Franki1986

Starting with 2.3.0, it's possible to register custom binary operations.

For &, try this:

CustomFilterCompilers.RegisterBinaryExpressionCompiler(info => {
    if(info.Operation == "&") {
        var value = Convert.ToInt32(info.Value);
        return Expression.Equal(
            Expression.MakeBinary(
                ExpressionType.And,
                Expression.Property(info.DataItemExpression, info.AccessorText),
                Expression.Constant(value)
            ),
            Expression.Constant(value)
        );
    }

    return null;
});

Will translate [ "Member", "&", N ] into .Where(obj => ((obj.Member & N) == N)).

@frankiDotNet
Copy link
Author

You don't know how much this helps!!!! A great THANK YOU!!!
👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants