多个条件条件,如“与”和“或”(而非“与”)

时间:2018-07-30 13:22:49

标签: c# linq azure-cosmosdb

我正在通过LINQ / Iqueryable查询CosmosDB。

我有一个方法,该方法的输入中有一个IEnumerable,可以包含很多元素。

所需查询是

SELECT * FROM table WHERE field LIKE '%input1%' OR field1 LIKE '%input2%'

依次类推,列举了每个IEnumerable元素。

IEnumerable<string> filterElements = GetFiltersExample();

        IQueryable<ResultObject> theQuery = AzureDocumentClient.CreateDocumentQuery<ResultObject>(
               collectionUri, queryOptions);

        if (filterElements.Count() > 0)
            foreach (string filterElement in filterElements)
            {
                theQuery = theQuery.Where(doc => doc.id.Contains(filterElement));
            }

但是它会生成

SELECT * FROM table WHERE (CONTAINS(field, 'input1') AND CONTAINS(field, 'input2'))

如果LINQ不会自动将它们置于AND条件(并且由于里面没有该字符串可能会导致结果返回空,因为它不符合条件),这应该起作用。

如何在“或”条件而非“与”条件下进行操作?

我尝试使用PredicateBuilder,但是它没有用,因为它引发了有关无法启动Invoke方法的异常。

这就是我所做的:

var predicate = PredicateBuilder.True<ReturnObj>();
        if (elements.Count() > 0)
            foreach (string element in elements)
            {
                predicate = predicate.Or(elem => elem.id.Contains(element));
            }

即使使用字符串列表扫描THIS之类的查询元素,我也尝试过各种包含方法

Linq坚持使用AND连接所有条件,因此除非您手动创建LINQ查询,否则它永远不会匹配多个结果:

.Where(condition1 || condition2 || condition3.....)

如何执行此操作?太不可思议了,我对如此缺乏功能的消极方式感到惊讶

3 个答案:

答案 0 :(得分:1)

如果您使用的是LinqKit,则可以在AsExpandable上使用IQueryable,以便将所有表达式的调用扩展为使用表达式的实际主体,而不是Invoke(element) 。例如,如果您具有这样的表达式:

Expresssion<Func<int, bool>> expr = n => n == 1;

以及类似的查询:

var query = (EnumerableQuery<bool>)new[] { 1 }.AsQueryable().Select(n => d.Invoke(n))   

表达式树本身将包含Invoke方法,此LINQ提供程序不支持该方法。但是,当您使用AxExpandable时:

var expQuery = (ExpandableQuery<bool>)new[] { 1 }.AsQueryable().AsExpandable().Select(n => d.Invoke(n))   

表达式树将包含正文n => n == 1,而没有Invoke方法。

所以在您的情况下:

IQueryable<ResultObject> theQuery = AzureDocumentClient
    .CreateDocumentQuery<ResultObject>(collectionUri, queryOptions)
    .AsExpandable()
    .Where(e => predicate.Invoke(e);

应该做到这一点。

或者,如果可能的话,您可以使用不带Invoke的表达式:

.Where(predicate)

因此不需要AsExpandable

答案 1 :(得分:1)

很高兴您通过使用查询字符串找到了解决方法。我只是对您观察到的行为发表一些评论。

  • 第一次尝试,通过语义上附加多个Where子句构建查询时,这意味着组合过滤器或将结果相交。这就是为什么使用AND代替OR的原因。
    • 可能使用多个where然后合并结果,但这远不及使用组合查询字符串的效率。
  • PredicateBuilder抱怨不支持“调用”。这是因为以下原因。在DocumentQuery LinQ表达式中,通常将Where()调用转换为Cosmos DB查询字符串中的WHERE子句,然后再在Cosmos DB后端中发送和执行该子句。如果存在非标准函数调用,则此函数需要序列化。不幸的是,序列化任何函数并不是一件容易的事,因为函数会变得很麻烦。其中包括PredicateBuilder或您可能具有的自定义函数。但是,Cosmos DB确实有替代方法,它是用户定义函数(UDF)。 (UDF)可用于在Cosmos DB后端中执行自定义功能。
  • 要在这种情况下使用UDF,可以使用相应的OR函数的主体创建UDF,然后在Where()调用中使用它。可以在https://docs.microsoft.com/en-us/azure/cosmos-db/programming上找到一些示例。

答案 2 :(得分:0)

我最终使用字符串concat进行了老式处理,因为我在项目中只有一次查询,所以我不想使用外部库:

         StringBuilder query = new StringBuilder("SELECT * From TheCollection WHERE ");
        if (idsToCheck.Count() > 0)
            foreach (string idGuid in idsToCheck)
            {
                query.Append($" CONTAINS(TheCollection.id, '{idGuid}') OR ");
            }
        string builtQuery = query.ToString();
        builtQuery = builtQuery.Substring(0, builtQuery.Length - 4);

        IQueryable<ResultObject> Query = client.CreateDocumentQuery<ResultObject>(
               collectionUri, queryOptions);

        return Query.ToList();

工作得很好;)