2014-09-02 68 views
1

我想做一個簡單的「聊天」,其中有一個帖子和他們的答案(只有1深),我決定去this的方式,所以單個文檔看起來像這樣在mongodb中嵌入評論搜索

{ 
    _id: ObjectId(...), 
    posted: date, 
    author: "name", 
    content: "content", 
    comments: [ 
     { posted: date, 
      author: "name2"}, 
      content: '...' 
     }, ... ] 
} 

我的問題是我該如何在內容中搜索這種方式?我首先需要在「父」內容中查找匹配,然後在評論列表中查找內容。我應該怎麼做?

+0

能否請您提供的搜索用例的詳細信息?你想在一個查詢中搜索嗎? – 2014-09-02 21:26:10

+0

查找內容中的字符串,包括「父」和「子」註釋。如果可以輕鬆完成,單個查詢將非常棒。 – kviktor 2014-09-02 21:30:39

+0

如果您可以搜索正則表達式,可以使用:{$或:[{'content':{$ regex:'您的搜索正則表達式'}},{'comments':{$ elemMatch:{'content':{ $ regex:'您的搜索正則表達式'}}}]}。這會起作用嗎? – 2014-09-02 21:36:16

回答

1

正如在評論前面所述,基本查詢「發現」是用$or在這裏,這也確實短路匹配,其中返回true的首要條件只是一個簡單的事情。只有一個數組此元件,從而無需$elemMatch,只是用「點號」,因爲多場比賽不要求:

db.messages.find({ 
    "$or": [ 
     { "content": { "$regex": ".*Makefile.*" } }, 
     { "comments.content": { "$regex": ".*Makefile.*" } } 
    ] 
}) 

實際匹配的文件能夠滿足這些條件,這就是.find()所做的。然而,你似乎在尋找的東西有點「funkier」你想在「父」結果和「子」結果之間「辨別」。

這有點超出了.find()的範圍,這種操作實際上是MongoDB其他操作的範疇。不幸的是,當您正在查找「字符串的一部分」以匹配您的條件時,在諸如聚合框架之類的東西中不存在與「$regex」操作相當的「邏輯」操作。如果它是最好的選擇,但是沒有這樣的comparison operator,並且邏輯比較就是你想要的。這同樣適用於基於「文本」的搜索,因爲仍然需要從孩子中辨別父母。

不是最理想的方法,因爲它確實涉及JavaScript處理,但這裏的下一個最佳選項是mapReduce()

db.messages.mapReduce(
    function() { 
     // Check parent 
     if (this.content.match(re) != null) 
      emit(
       { "_id": this._id, "type": "P", "index": 0 }, 
       { 
        "posted": this.posted, 
        "author": this.author, 
        "content": this.content 
       } 
      ); 

     var parent = this._id; 
     // Check children 
     this.comments.forEach(function(comment,index) { 
      if (comment.content.match(re) != null) 
       emit(
        { "_id": parent, "type": "C", "index": index }, 
        { 
         "posted": comment.posted, 
         "author": comment.author, 
         "content": comment.content 
        } 
      ); 
     }); 
    }, 
    function() {}, // no reduce as all are unique 
    { 
     "query": { 
      "$or": [ 
       { "content": { "$regex": ".*Makefile.*" } }, 
       { "comments.content": { "$regex": ".*Makefile.*" } } 
      ] 
     }, 
     "scope": { "re": /.*Makefile.*/ }, 
     "out": { "inline": 1 } 
    } 
) 

基本上相同的查詢輸入,因爲這並選擇「文件」你想,真的只用「範圍」這裏是它使得它很容易就能在正則表達式作爲參數傳遞,而不重編寫JavaScript代碼以便每次都包含該值。

這裏的邏輯非常簡單,只需對每個「非規範化」元素進行測試,以查看正則表達式條件是否與該特定元素匹配。結果返回「去歸一化」並辨別匹配的元素是父母還是孩子。

你可以更進一步,不用費心去檢查孩子,如果父母是一個匹配,只需將其移動到else。以同樣的方式,你甚至可以通過某種方式返回「第一」兒童比賽,如果這是你的願望。

無論如何,這應該設置你的最終代碼看起來像什麼路徑。但是,這是實現在服務器上處理這種區別的唯一方法的基本方法,並且客戶端後處理將遵循大致相同的模式。

+0

似乎'評論'可以包含多個評論(@kvictor - 糾正我,如果我錯了) – 2014-09-03 07:25:54

+0

@BaruchOxman你錯了。請參閱文檔中的「點符號」(http://docs.mongodb.org/manual/core/document/#document-dot-notation)。這不是在討論數組中某個元素的位置,而是在討論數組中的「any」元素。僅當需要匹配數組元素的多個字段時才需要'$ elemMatch'。 – 2014-09-03 07:30:33

+0

謝謝澄清,今天學到了新東西! – 2014-09-03 07:39:05

2

如果你可以搜索各個內容中的一個正則表達式,你可以使用: {$or : [ {'content':{$regex:'your search regex'}}, {'comments' : { $elemMatch: { 'content':{$regex:'your search regex'}}}]}

請注意,要麼父母或孩子取的結果時,在一場比賽您將收到整個蒙戈文件,包含父母和孩子。 如果你想避免這種情況(要確定你找到了什麼),你可能首先運行一個正則表達式查詢,而不是單一的查詢。

,詳細瞭解$elemMatch看一看:docs.mongodb.org/manual/reference/operator/query/elemMatch