2011-12-23 17 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

相关问题