如果数组 属性 包含给定列表中的任何项目,如何查询 Cosmos DB?

How to query Cosmos DB if array property contains any item from a given list?

如果你有一个像这样的实体class,它存储在你的 cosmos 数据库中

public class SomeItem
{
    public Guid Id { get; set; }
    public IReadOnlyList<string> AffectedUsers { get; set; }
}

在您的代码中,您会收到一个用户列表,您必须查询其中列出的所有项目:

var cosmosClient = new CosmosClient("https://test.local/", "Tm9LZXk=");
var container = cosmosClient.GetContainer("123", "456");

// A list of users where we like to get all items, where any of them is listed in affected users.
var someUsers = Enumerable.Range(1, 5).Select(i => $"User {i}").ToList();

// The query that should check if any user of the above list is within affectedUsers
var query = container.GetItemLinqQueryable<SomeItem>()
    // How to check for any element in someUsers is within affectedUsers dynamically?
    .Where(item => item.AffectedUsers.Contains(someUsers[0]));

var definition = query.ToQueryDefinition();
var text = definition.QueryText;

Console.WriteLine(text);

要解决此问题,需要一些基于 LinqKit 的魔术代码:

public static class QueryableExtensions
{
    public static IQueryable<T> WhereAny<T, U>(this IQueryable<T> source, IEnumerable<U> items, Expression<Func<T, U, bool>> expression)
    {
        var predicate = PredicateBuilder.New<T>();

        foreach (var item in items)
        {
            Expression<Func<T, bool>> comparison = p => expression.Invoke(p, item);
            predicate = predicate.Or(comparison.Expand());
        }

        return source.Where(predicate);
    }
}

在您的工具箱中使用此方法,您可以按如下方式编写查询:

var query = container.GetItemLinqQueryable<SomeItem>()
    .WhereAny(someUsers, (item, user) => item.AffectedUsers.Contains(user));

生成的 cosmos 查询将是:

SELECT VALUE root FROM root WHERE ((((ARRAY_CONTAINS(root["AffectedUsers"], "User 1")
    OR ARRAY_CONTAINS(root["AffectedUsers"], "User 2"))
    OR ARRAY_CONTAINS(root["AffectedUsers"], "User 3"))
    OR ARRAY_CONTAINS(root["AffectedUsers"], "User 4"))
    OR ARRAY_CONTAINS(root["AffectedUsers"], "User 5"))