linq - LINQ to CosmosDB 中数组的交集

标签 linq azure azure-cosmosdb

我正在尝试查找数据库中的所有项目,这些项目在数组中至少有一个值与我在代码中拥有的数组中的任何值相匹配(两个数组的交集不应为空)。

基本上,我正在努力实现这一目标:

public List<Book> ListBooks(string partitionKey, List<string> categories)
{
    return _client.CreateDocumentQuery<Book>(GetCollectionUri(), new FeedOptions
    {
        PartitionKey = new PartitionKey(partitionKey)
    })
    .Where(b => b.Categories.Any(c => categories.Contains(c))
    .ToList();
}

Book 类如下所示:

public class Book
{
    public string id {get;set;}
    public string Title {get;set;}
    public string AuthorName {get;set;}
    public List<string> Categories {get;set;}
}

但是,执行此代码时,SDK 会抛出异常,表示不支持方法“Any”。 这也不起作用:

return _client.CreateDocumentQuery<Book>(GetCollectionUri(), new FeedOptions
{
    PartitionKey = new PartitionKey(partitionKey)
})
.Where(b => categories.Any(c => b.Categories.Contains(c))
.ToList();

以下代码有效,因为只有一个类别可供查找:

public List<Book> ListBooksAsync(string category)
{
    return _client.CreateDocumentQuery<Book>(GetCollectionUri())
    .Where(b => b.Categories.Contains(category))
    .ToList();
}

在普通 SQL 中,我可以将多个 ARRAY_CONTAINS 与多个 OR 一起排队,查询将正确执行。

SELECT * FROM root 
WHERE ARRAY_CONTAINS(root["Categories"], 'Humor')
   OR ARRAY_CONTAINS(root["Categories"], 'Fantasy')
   OR ARRAY_CONTAINS(root["Categories"], 'Legend')

我正在尝试找到使用 LINQ 实现此目的的最佳方法,但我什至不确定这是否可行。

最佳答案

在这种情况下,我使用了一个辅助方法来组合表达式,其计算结果为 SQL,如最后一个示例中所示。下面的辅助方法“MakeOrExpression”允许您传递多个谓词(在您的情况下,单独检查 b.Categories.Contains(category))并生成一个表达式,您可以将其放入 .Where(expression) 的参数中文档查询。

class Program
{
    private class Book
    {
        public string id { get; set; }
        public string Title { get; set; }
        public string AuthorName { get; set; }
        public List<string> Categories { get; set; }
    }

    static void Main(string[] args)
    {
        var comparison = new[] { "a", "b", "c" };

        var target = new Book[] {
            new Book { id = "book1", Categories = new List<string> { "b", "z" } },
            new Book { id = "book2", Categories = new List<string> { "s", "t" } },
            new Book { id = "book3", Categories = new List<string> { "z", "a" } } };

        var results = target.AsQueryable()
            .Where(MakeOrExpression(comparison.Select(x => (Expression<Func<Book, bool>>)(y => y.Categories.Contains(x))).ToArray()));

        foreach (var result in results)
        {
            // Should be book1 and book3
            Console.WriteLine(result.id);
        }

        Console.ReadLine();
    }

    private static Expression<Func<T,bool>> MakeOrExpression<T>(params Expression<Func<T,bool>>[] inputExpressions)
    {
        var combinedExpression = inputExpressions.Skip(1).Aggregate(
            inputExpressions[0].Body, 
            (agg, x) => Expression.OrElse(agg, x.Body));

        var parameterExpression = Expression.Parameter(typeof(T));

        var replaceParameterVisitor = new ReplaceParameterVisitor(parameterExpression, 
            Enumerable.SelectMany(inputExpressions, ((Expression<Func<T, bool>> x) => x.Parameters)));

        var mergedExpression = replaceParameterVisitor.Visit(combinedExpression);

        var result = Expression.Lambda<Func<T, bool>>(mergedExpression, parameterExpression);
        return result;
    }

    private class ReplaceParameterVisitor : ExpressionVisitor
    {
        private readonly IEnumerable<ParameterExpression> targetParameterExpressions;
        private readonly ParameterExpression parameterExpression;

        public ReplaceParameterVisitor(ParameterExpression parameterExpressionParam, IEnumerable<ParameterExpression> targetParameterExpressionsParam)
        {
            this.parameterExpression = parameterExpressionParam;
            this.targetParameterExpressions = targetParameterExpressionsParam;
        }

        public override Expression Visit(Expression node)
            => targetParameterExpressions.Contains(node) ? this.parameterExpression : base.Visit(node);
    }
}

关于linq - LINQ to CosmosDB 中数组的交集,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48870626/

相关文章:

c# - 由于缺少 SSL 加密,连接到 SQL Azure 数据库失败

asp.net-mvc - 我可以在自己的登录/注册页面 (MVC) 中使用 Azure AD B2C 吗?

c# - 当webrole通过自动缩放删除时如何取消订阅?

c# - 使用 Cosmos DB 配置 EF Core 以存储字符串的 ICollection

c# - 为什么这个 Linq 不工作

c# - ADO.NET Entity Framework : Converting String to Int in Where/OrderBY

azure-sql-database - Azure SQL 成本与 DocumentDB/CosmosDB 成本的比较

azure - 如何将更改源监视集合名称获取到我的 ChangesHander - Containers.ChangesHander<T> (Azure Cosmos v3)

c# - 将新列表发送到 C# 列表中的 LINQ 查询结果

c# - 对象数组上的 Linq 外连接