2012-04-13 60 views
3

子集的至少一個元素查找對象我有簡單類型Question其中包含使用RavenDB和LINQ

public class Question 
{ 
    public Question(string id) 
    { 
     Id = id; 
     Tags = new List<string>(); 
    } 

    public string Id { get; private set; } 
    public IList<string> Tags { get; set; }    
} 

我已經定義了這樣的問題,標本採集:

var q1 = new Question("q1") { Tags = new List<string>() {"a"} }; 
var q2 = new Question("q2") { Tags = new List<string>() {"b"} }; 
var q3 = new Question("q3") { Tags = new List<string>() {"a", "b", "c"} }; 
var q4 = new Question("q4") { Tags = new List<string>() {"a", "b"} }; 
var q5 = new Question("q5") { Tags = new List<string>() {"z"} }; 
var q6 = new Question("q6"); 
var questions = new List<Question>() {q1, q2, q3, q4, q5, q6}; 

現在我需要找到所有問題,其中至少包含一個標記,來自給定子集。子集定義如下:

string[] tags = new[] {"a", "b"}; 

我希望Q1Q2Q3Q4歸還。我用它來獲得期望的結果的查詢是:

var questions = DocumentSession.Query<Question>().AsQueryable(); 
questions = GetQuestionsToContainingAtLeastOneTagFromSubset(questions, tags) 
// some other query restrictions 
var result = questions.ToList(); // (**) 

其假設強加給我的集合限制的功能如下:

private IQueryable<Question> GetQuestionsToContainingAtLeastOneTagFromSubset(IQueryable<Question> questions, IEnumerable<string> tags) 
{ 
    var result = new List<Question>(); 
    foreach (var tag in tags) 
    { 
     var currentTag = tag; 
     var resultForTag = questions.Where(x => x.Tags.Any(xTag => xTag == currentTag)); 
     result = result.Concat(resultForTag).ToList(); 
    } 
    return result.GroupBy(x => x.Id).Select(grp => grp.First()).AsQueryable(); 
} 

我覺得這是非常低效的。我想避免在提供的功能中使用.ToList()。據我所知這.ToList()表達式查詢RavenDB並返回部分結果(順便說一句:我是吧?)。這不是有效的。我只想對所提供的功能施加限制,並在施加所有限制後執行查詢。 (**)的地方對我來說是很好的把批處理髮送到RavenDB檢索查詢結果。

如何改善?

回答

0

可以查詢Lucene來獲得與匹配侑標籤阿雷陣列像這樣標籤的問題:

string[] tags = new[] { "a", "b" }; 
string queryRange = "(" + string.Join(" OR ", tags) + ")"; 

var res = session.Advanced.LuceneQuery<Question>() 
       .Where("Tags:" + queryRange).ToList(); 

注意,這個查詢整個索引的問題,而不是一個子集。但是我認爲你可以在查詢字符串表達式中附加-I GUESS。 見Lucene的文檔瞭解更多關於這些類型的查詢: http://lucene.apache.org/core/old_versioned_docs/versions/3_0_0/queryparsersyntax.html

+0

感謝您的提示。我改變了我的問題中的數據,只是因爲我想向您顯示這不符合預期。調試器中的Lucene查詢轉換爲{Tags:a或b},並且由於某種原因它僅返回q1,q3和q4。它假設也返回q2,但它不。此外,我想在這個特定的例子中避免使用LuceneQuery。 – jwaliszko 2012-04-13 13:48:28

+0

試試這個查詢範圍: string queryRange =「(」+ string.Join(「OR」,tags)+「)」;我更新了我的答案。至於沒有使用明確的lucene查詢...我更喜歡linq查詢,但如果lucene提供和ravendb公開,爲什麼不使用它? :)這很簡單 – 2012-04-13 14:45:55

+0

謝謝,它現在有效。這看起來確實很簡單。我想避免Lucene查詢,因​​爲我考慮過構建連續的限制。雖然我已經添加了一些限制的IQueryable集合,但我無法進一步應用Lucene限制。從Raven.Client.IDocumentSession獲得構建Lucene查詢的權限,我很樂意應用相同的restristions,但在Linq中使用IQueryable。 – jwaliszko 2012-04-13 16:13:04

0

試試這個代碼:

var q1 = new Question("q1") { Tags = new List<string>() {"aa", "bb"} }; 
var q2 = new Question("q2") { Tags = new List<string>() {"aa"} }; 
var q3 = new Question("q3") { Tags = new List<string>() {"aa", "bb", "cc"} }; 
var q4 = new Question("q4"); 
var questions = new List<Question>() {q1, q2, q3, q4}; 
string[] tags = new[] { "bb", "cc" }; 

var res = (from p in questions where 
      (from q in tags where p.Tags.Contains(q) == true select p).ToArray().Count() >= 1 
      select p).Distinct(); 
//If you want more speed you can try to add .AsParallel() after .Distinct() 
+0

你的第一個where子句可以簡化爲(在標籤從q其中p.Tags.Contains(Q)的選擇P)。任何()選擇P)但無論如何,RavenDB拋出一個異常:System.InvalidOperationException:無法理解如何轉換值.... tags.Where(q => p.Tags.Contains(q))。Select(q => p) – jwaliszko 2012-04-13 09:55:09

+0

Try .ToArray () - – innovia 2012-04-13 11:37:06

+0

使用linq-to-objects可以工作,但Raven驅動程序有一些問題處理它並拋出相同類型的異常。 – jwaliszko 2012-04-13 13:57:15