【问题标题】:DocumentDB LINQ match multiple child objectsDocumentDB LINQ 匹配多个子对象
【发布时间】:2017-06-27 09:51:37
【问题描述】:

我有一个简单的文档。

{
    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 answerARRAY_CONTAINS 仅适用于基元数组,不适用于对象。所以它似乎不适合我想要实现的目标。

似乎那个答案的 cmets 是错误的,我的查询中有语法错误。我需要在属性名称周围添加双引号。

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

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 语法来使用它。

【问题讨论】:

  • 这并不难。您需要类 Tag 来实现比较多个标签的 IComparable。
  • @jdweng DocumentDB LINQ 提供程序了解如何将自定义 IComparable 转换为正确的 SQL 查询?
  • 有一些indication支持Contains,所以我想知道.Where(d =&gt; d.Tags.Contains(singleTagToMatch))是否有效?因为如果是这样,我们最终可以动态构建 &amp;&amp; 谓词表达式,类似于您在 SQL 查询中使用的表达式。
  • @IvanStoev 这是一个非常好的想法,谢谢!我会做一些调查,看看 .Contains 会产生什么。
  • @IvanStoev 调用 query.ToString 将查询转换为将发送到后端的 SQL。看着那个 SQL .Where(d =&gt; d.Tags.Contains(singleTagToMatch)); 产生 WHERE ARRAY_CONTAINS(root["Tags"], {"Name":"Type","Value":"One"}) 这是完美的!我现在正在研究动态构建 LINQ 谓词。

标签: c# linq azure-cosmosdb


【解决方案1】:

在 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 链接谓词,那么您将需要一些表达式谓词构建器库。

【讨论】:

  • 感谢您的出色回答,这为我解决了问题。希望您不要介意,我已经扩展了答案以提供更多详细信息和示例。
  • 不客气,很高兴它有帮助 :) 当然(实际上谢谢你),解释不是我最好的力量:)
  • 那么,如果您只希望其中一个元素(名称或值)匹配,这将如何工作?我尝试在 Tag 类上覆盖 Equals 并在其中一个元素/属性上使用 EqualityComparer ,但这不起作用。
  • @carens 您尝试的方法仅适用于 LINQ to Objects。我现在无法测试,但您可以尝试组合 Select + Contains,例如query.Where(s =&gt; s.Tags.Select(t =&gt; t.Name).Contains(tagToMatch.Name));
  • 我收到一条错误消息,指出不支持 Select 方法。我用 SelectMany 试了一下,但结果重复。
猜你喜欢
  • 1970-01-01
  • 2017-08-13
  • 1970-01-01
  • 1970-01-01
  • 2023-03-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多