2011-12-23 15 views
8

的總數量排序:獲取的文件在列表的標籤,由下式給出的文件下面的MongoDB集合匹配

{ 
title : 'shirt one' 
tags : [ 
    'shirt', 
    'cotton', 
    't-shirt', 
    'black' 
] 
}, 
{ 
title : 'shirt two' 
tags : [ 
    'shirt', 
    'white', 
    'button down collar' 
] 
}, 
{ 
title : 'shirt three' 
tags : [ 
    'shirt', 
    'cotton', 
    'red' 
] 
}, 
... 

你如何檢索匹配的標籤列表項的列表,按總數排序匹配標籤?例如,給定的標籤這個列表作爲輸入:

['shirt', 'cotton', 'black'] 

我想通過檢索匹配標籤的總數在遞減順序排列的項目:

item   total matches 
--------  -------------- 
Shirt One  3 (matched shirt + cotton + black) 
Shirt Three 2 (matched shirt + cotton) 
Shirt Two  1 (matched shirt) 

在關係模式中,標籤將成爲一張單獨的桌子,您可以加入這張桌子,統計比賽次數,並按次數排序。

但是,在Mongo ...?

似乎這種方法可以工作,

  • 斷裂輸入標記成多個「IN」的語句
  • 查詢的項目「或」「荷蘭國際集團共同標籤輸入
    • 即其中(」 ('cotton'IN items.tags)
    • 這會返回,例如,「Shirt One」的三個實例,「Shirt Three」的兩個實例等
  • map/reduce that output
    • map:emit(this._id,{...});
    • 減少:數_id
    • 敲定總出現:排序計總

但我不能就如何落實這是一個蒙戈查詢清楚,或者如果這是連最有效的方法。

+0

好像簡單M/R作業。 – 2011-12-23 14:48:54

+1

由於當前的實現缺乏適當的並行性,因此沒有M/R在生產代碼中很簡單。事實上,在高吞吐量情況下可以完全避免m/r。 – 2011-12-23 18:27:16

回答

5

現在,除非您使用MapReduce,否則不可能這樣做。 MapReduce的唯一問題是速度慢(與普通查詢相比)。

聚合框架定義爲2.2(所以應該在2.1 dev版本中可用),並且應該使這種事情在沒有MapReduce的情況下更容易完成。

個人而言,我不認爲使用M/R是一種有效的方法。我寧願查詢所有文檔,並在應用程序方面進行這些計算。擴展應用程序服務器比擴展數據庫服務器更容易,更便宜,因此讓應用程序服務器執行數字處理。其中,根據您的數據訪問模式和要求,此方法可能無法爲您工作。

一個更簡單的方法可能是隻包括在每個標籤對象的count財產,只要你$push一個新的標籤數組,你也$inccount財產。這是MongoDB世界中的一種常見模式,至少在聚合框架之前。

+1

當$ push'ing一個新的標籤到數組時,包括一個count屬性不會幫助解決這個問題,因爲wount可以簡單地指示總標籤(而不是匹配輸入的總標籤)。 – Matt 2011-12-23 15:17:28

+0

對啊,我在那裏領先了。 – 2011-12-23 15:20:08

1

我會第二@Bryan說MapReduce是目前唯一可能的方式(而且它遠非完美)。但是,如果你迫切需要它,在這裏你去:-)

var m = function() { 
     var searchTerms = ['shirt', 'cotton', 'black']; 
     var me = this; 
     this.tags.forEach(function(t) { 
      searchTerms.forEach(function(st) { 
       if(t == st) { 
        emit(me._id, {matches : 1}); 
       } 
      }) 
     }) 
    }; 

    var r = function(k, vals) { 
     var result = {matches : 0}; 
     vals.forEach(function(v) { 
      result.matches += v.matches; 
     }) 
     return result; 
    }; 

    db.shirts.mapReduce(m, r, {out: 'found01'}); 

    db.found01.find(); 
+0

謝謝,這是一個好的開始。但是,不是在集合中的* all *項上運行map/reduce,通過將輸入標籤組合在一起做初始查找不會更快嗎?這將減少在m()中處理的集合的大小,並且r()可以簡單地返回vals.length作爲總匹配? – Matt 2011-12-23 15:24:01

7

正如我在In MongoDB search in an array and sort by number of matches

它使用聚​​合框架是可能的回答。

假設

  • tags屬性是一組(不重複的元素)

查詢

這種方法迫使你放鬆的結果,並重新評估匹配謂詞結果放鬆,所以它真的效率低下。

db.test_col.aggregate(
    {$match: {tags: {$in: ["shirt","cotton","black"]}}}, 
    {$unwind: "$tags"}, 
    {$match: {tags: {$in: ["shirt","cotton","black"]}}}, 
    {$group: { 
     _id:{"_id":1}, 
     matches:{$sum:1} 
    }}, 
    {$sort:{matches:-1}} 
); 

預期結果

{ 
    "result" : [ 
     { 
      "_id" : { 
       "_id" : ObjectId("5051f1786a64bd2c54918b26") 
      }, 
      "matches" : 3 
     }, 
     { 
      "_id" : { 
       "_id" : ObjectId("5051f1726a64bd2c54918b24") 
      }, 
      "matches" : 2 
     }, 
     { 
      "_id" : { 
       "_id" : ObjectId("5051f1756a64bd2c54918b25") 
      }, 
      "matches" : 1 
     } 
    ], 
    "ok" : 1 
} 
+0

Samuel答案是正確的。我只是在質疑效率低下的額外信息。爲了匹配某人將不得不解開標籤反正在聚合管道中執行此任務可能是adhoc查詢的最快方法 – rat 2016-02-04 14:44:46

+0

這個答案對我很好,但是我必須對'$ group'對象做一些小改動在Mongo 3.0中開展這項工作。並將其用於ID'_id:{「_ id」:「$ _ id」}' – Binarytales 2016-02-12 11:24:35

+0

是的,確實如此。分組_id格式在版本3.0中已更改,現在您可以使用該格式或嵌套格式,但也可以使用$符號。 – 2016-02-12 11:33:32

相關問題