c# - DocumentDB LINQ 匹配多个子对象

标签 c# linq azure-cosmosdb

我有一个简单的文档。

{
    Name: "Foo",
    Tags: [
        { Name: "Type", Value: "One" },
        { Name: "Category", Value: "A" },
        { Name: "Source", Value: "Example" },
    ]
}

我想创建一个 LINQ 查询,通过匹配多个 Tags 来找到这些文档。

即不是 SQL 查询,除非没有其他选择。

例如

var tagsToMatch = new List<Tag>()
{
    new Tag("Type", "One"),
    new Tag("Category", "A")
};

var query = client
    .CreateDocumentQuery<T>(documentCollectionUri)
    .Where(d => tagsToMatch.All(tagToMatch => d.Tags.Any(tag => tag == tagToMatch)));

这给我错误 Method 'All' is not supported.

我找到了匹配子对象上的单个属性的示例:LINQ Query Issue with using Any on DocumentDB for child collection

var singleTagToMatch = tagsToMatch.First();

var query = client
    .CreateDocumentQuery<T>(documentCollectionUri)
    .SelectMany
    (
        d => d.Tags
            .Where(t => t.Name == singleTagToMatch.Name && t.Value == singleTagToMatch.Value)
            .Select(t => d)
    );

但如何扩展该方法以支持匹配多个子对象并不明显。

我发现有一个名为 ARRAY_CONTAINS 的函数可以使用:Azure DocumentDB ARRAY_CONTAINS on nested documents

但我遇到的所有示例都使用 SQL 查询。

thread表示 LINQ 支持在 2015 年“即将推出”,但从未跟进,所以我认为它没有被添加。

我没有在 LINQ 中找到有关 ARRAY_CONTAINS 的任何文档,仅在 SQL 中.

我尝试了以下 SQL 查询以查看它是否执行我想要的操作,但它没有返回任何结果:

SELECT  Document
FROM    Document
WHERE   ARRAY_CONTAINS(Document.Tags, { Name: "Type", Value: "One" })
AND     ARRAY_CONTAINS(Document.Tags, { Name: "Category", Value: "A" })

根据comments on this answer , ARRAY_CONTAINS 仅适用于基元数组,不适用于对象。所以它似乎不适合我想要实现的目标。

似乎对该答案的评论是错误的,而且我的查询中存在语法错误。我需要在属性名称周围添加双引号。

运行这个查询确实返回了我想要的结果:

SELECT  Document
FROM    Document
WHERE   ARRAY_CONTAINS(Document.Tags, { "Name": "Type", "Value": "One" })
AND     ARRAY_CONTAINS(Document.Tags, { "Name": "Category", "Value": "A" })

所以 ARRAY_CONTAINS 似乎确实实现了我想要的,所以我正在寻找如何通过 LINQ 语法使用它。

最佳答案

在 LINQ 查询中使用 .Contains 将生成使用 ARRAY_CONTAINS 的 SQL。

所以:

var tagsToMatch = new List<Tag>()
{
    new Tag("Type", "One"),
    new Tag("Category", "A")
};

var singleTagToMatch = tagsToMatch.First();

var query = client
    .CreateDocumentQuery<T>(documentCollectionUri)
    .Where(d => d.Tags.Contains(singleTagToMatch));

将成为:

SELECT * FROM root WHERE ARRAY_CONTAINS(root["Tags"], {"Name":"Type","Value":"One"})

您可以链接 .Where 调用以创建 AND 谓词链。

所以:

var query = client.CreateDocumentQuery<T>(documentCollectionUri)

foreach (var tagToMatch in tagsToMatch)
{
    query = query.Where(s => s.Tags.Contains(tagToMatch));
}

将成为:

SELECT * FROM root WHERE ARRAY_CONTAINS(root["Tags"], {"Name":"Type","Value":"One"}) AND ARRAY_CONTAINS(root["Tags"], {"Name":"Category","Value":"A"})

如果您需要使用 OR 链接谓词,那么您将需要一些表达式谓词生成器库。

关于c# - DocumentDB LINQ 匹配多个子对象,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44777244/

相关文章:

c# - WPF转换器在文本更改时实时更新文本框的背景颜色

C# utf8_解码

c# - C# 中的局部函数 - 向下传递参数时捕获还是不捕获?

c# - LINQ查询参数可以为空

linq - Silverlight 中不存在 List<T>.RemoveAll 吗?

c# - 如何按顺序检查字符串中的单词

c# - 如何使用 linq 使用 hibernate queryover 连接两列

azure - 以编程方式验证是否在 Cosmos DB 集合上启用了 TTL

azure - 在运行 az cosmosdb create 命令创建 Cosmos DB 帐户时,如何指定 MongoDB API 版本?

azure-cosmosdb - 在文档反序列化时禁用 DateParseHandling