Azure Cosmos DB-Where查询中的动态OR子句

问题描述

在我的宇宙存储中,我的物品有一组关键字。 我想进行查询,以过滤出列表中至少有一个关键字的项目。

我尝试了Intersect方法

query = query.Where(x => x.Keywords.Intersect(research.Kewords).Any())

但是我得到了一个异常“不支持方法'Intersect'”

所以我尝试了PredicateBuilder方法

var pred = PredicateBuilder.False<Item>();
foreach (var k in research.Keywords)
   pred = pred.Or(x => x.Keywords.Contains(k));
 query = query.Where(pred);

但是我得到了一个异常“不支持使用NodeType'Invoke'的表达式”

这是我的predicateBuilder:

public static Expression<Func<T,bool>> False<T>() { return f => false; }
public static Expression<Func<T,bool>> Or<T>(this Expression<Func<T,bool>> expr1,Expression<Func<T,bool>> expr2)
{
    var invokedExpr = Expression.Invoke(expr2,expr1.Parameters.Cast<Expression>());
    return Expression.Lambda<Func<T,bool>>
          (Expression.OrElse(expr1.Body,invokedExpr),expr1.Parameters);
}

在Cosmos中是否有办法实现这一目标?还是我必须在查询结果之后过滤?

解决方法

我过去使用过类似的PredicateBuilder类,其实现与您展示的有所不同。我对此解释得不多,因为它可能是SO的缩写,而我却忽略了文档:)。

有了它,您应该能够使用显示的表单:

expression = PredicateBuilder.Or(expression,x.Keywords.Contains(k));

实施

public static class PredicateBuilder
{
    public static Expression<Func<T,bool>> True<T>() { return f => true; }
    public static Expression<Func<T,bool>> False<T>() { return f => false; }

    public static Expression<Func<T,bool>> Or<T>(this Expression<Func<T,bool>> expr1,Expression<Func<T,bool>> expr2)
    {
        var secondBody = expr2.Body.Replace(expr2.Parameters[0],expr1.Parameters[0]);
        return Expression.Lambda<Func<T,bool>>(Expression.OrElse(expr1.Body,secondBody),expr1.Parameters);
    }

    public static Expression<Func<T,bool>> And<T>(this Expression<Func<T,bool>> expr2)
    {
        if (expr2 != null)
        {
            var secondBody = expr2.Body.Replace(expr2.Parameters[0],expr1.Parameters[0]);
            return Expression.Lambda<Func<T,bool>>(Expression.AndAlso(expr1.Body,expr1.Parameters);
        }
        else
        {
            return expr1;
        }
    }

    public static Expression Replace(this Expression expression,Expression searchEx,Expression replaceEx)
    {
        return new ReplaceVisitor(searchEx,replaceEx).Visit(expression);
    }
}

internal class ReplaceVisitor : ExpressionVisitor
{
    private readonly Expression from,to;

    public ReplaceVisitor(Expression from,Expression to)
    {
        this.from = from;
        this.to = to;
    }

    public override Expression Visit(Expression node)
    {
        return node == from ? to : base.Visit(node);
    }
}