2017-06-27 34 views
3

我有一個簡單的文檔。DocumentDB LINQ匹配多個子對象

{ 
    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僅適用於基本類型數組,不是對象。所以它似乎不適合我想要實現的目標。

看來這個答案的評論是錯誤的,我在我的查詢語法錯誤。我需要在屬性名稱周圍添加雙引號。

運行此查詢確實回來了我想要的結果:

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

+0

這不是太難。您需要使用類標記來實現比較多個標記的IComparable。 – jdweng

+0

@jdweng DocumentDB LINQ提供程序理解如何將該自定義IComparable轉換爲正確的SQL查詢? – AndyJ

+1

有一些[跡象](https://docs.microsoft.com/en-us/azure/cosmos-db/documentdb-sql-query)支持'Contains',所以我想知道'.Where (d => d.Tags.Contains(singleTagToMatch))'有效嗎?因爲如果這樣做了,我們最終可以動態構建'&&'謂詞表達式,類似於您在SQL查詢中使用的表達式。 –

回答

7

在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,那麼你就需要一些表達謂詞Builder庫需要產業鏈的謂詞。

+1

感謝您的好評,爲我解決了這個問題。我希望你不介意,我已經擴大了答案,以提供更多的細節和例子。 – AndyJ

+1

歡迎您,很高興它幫助:)當然(實際上,謝謝你),解釋不是我最好的力量:) –

+0

那麼,如果你只想要一個元素(名稱或值)匹配,這是如何工作的?我嘗試覆蓋Tag類上的Equals和EqualityComparer 上的一個元素/屬性,這不起作用。 – carens