Mongodb 获取列表中带有标记的文档,按匹配总数排序

Mongodb 获取列表中带有标记的文档,按匹配总数排序,mongodb,aggregation-framework,Mongodb,Aggregation Framework,考虑到以下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', 're

考虑到以下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']
我想检索按匹配标记总数按desc顺序排列的项目:

item          total matches
--------      --------------
Shirt One     3 (matched shirt + cotton + black)
Shirt Three   2 (matched shirt + cotton)
Shirt Two     1 (matched shirt)
在关系模式中,标记将是一个单独的表,您可以对该表进行连接、计算匹配项并按计数排序

但是,在蒙戈

看来这个办法行得通,

  • 将输入标记拆分为多个“IN”语句
  • 通过将标记输入“或”组合在一起查询项目
    • i、 e.其中('shirt'在items.tags中)或('cotton'在items.tags中)
    • 例如,这将返回“Shirt One”的三个实例,以及“Shirt three”的两个实例,等等
  • 映射/减少该输出
    • 映射:emit(this.\u id,{…})
    • 减少:统计_id的总出现次数
    • 最终确定:按计数总数排序

但我不清楚如何将其实现为Mongo查询,或者这是否是最有效的方法。

现在,除非使用MapReduce,否则无法实现。MapReduce唯一的问题是速度慢(与普通查询相比)

聚合框架计划在2.2版本中使用(因此应该在2.1开发版本中提供),并且应该在没有MapReduce的情况下使这类事情更容易完成

就我个人而言,我不认为使用M/R是一种有效的方法。我宁愿查询所有文档并在应用程序端进行这些计算。与扩展数据库服务器相比,扩展应用服务器更容易、更便宜,所以让应用服务器来处理数据。其中,考虑到您的数据访问模式和需求,这种方法可能不适合您


一种更简单的方法可能是只在每个标记对象中包含一个
count
属性,并且每当
$push
一个新标记到数组中时,您也可以
$inc
使用
count
属性。这是MongoDB世界中的一种常见模式,至少在聚合框架出现之前是如此。

我支持@Bryan的说法,即MapReduce是目前唯一可行的方法(而且还远远不够完美)。但是,如果你非常需要它,就给你:-)

正如我回答的那样

使用聚合框架是可能的

假设

{
    "result" : [
        {
            "_id" : {
                "_id" : ObjectId("5051f1786a64bd2c54918b26")
            },
            "matches" : 3
        },
        {
            "_id" : {
                "_id" : ObjectId("5051f1726a64bd2c54918b24")
            },
            "matches" : 2
        },
        {
            "_id" : {
                "_id" : ObjectId("5051f1756a64bd2c54918b25")
            },
            "matches" : 1
        }
    ],
    "ok" : 1
}
  • 标记
    属性是一个集合(无重复元素)
查询

这种方法迫使您展开结果,并使用未展开的结果重新评估匹配谓词,因此其效率非常低。

预期结果

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

考虑到这个问题,在$push'将新标记推送到数组时包含count属性是没有帮助的,因为不能简单地指示标记总数(而不是与输入匹配的标记总数)。谢谢,这是一个好的开始。但是,与其在集合中的所有项目上运行map/reduce,不如通过或将输入标记组合在一起进行初始查找,这样会更快吗?这将减少在m()中处理的集合的大小,并且r()可以简单地返回vals.length作为总匹配项?在生产代码中没有简单的m/r,因为当前的实现缺乏适当的并行性。事实上,在高吞吐量情况下,完全避免m/r是一个很好的例子。Samuel的回答是正确的。我只是对附加信息表示怀疑,认为它效率低下。为了匹配,无论如何都有人必须解开标签在聚合管道中执行此任务可能是临时查询的最快方法此答案对我来说非常有效,但是我必须在
$group
对象中做一些小更改,才能在Mongo 3.0中实现此功能。并将其用于ID
\u ID:{“\u ID”:“$\u ID”}
确实如此。分组id格式在3.0版中已更改,现在您可以使用该格式或嵌套格式,但也可以使用$symbol。
{
    "result" : [
        {
            "_id" : {
                "_id" : ObjectId("5051f1786a64bd2c54918b26")
            },
            "matches" : 3
        },
        {
            "_id" : {
                "_id" : ObjectId("5051f1726a64bd2c54918b24")
            },
            "matches" : 2
        },
        {
            "_id" : {
                "_id" : ObjectId("5051f1756a64bd2c54918b25")
            },
            "matches" : 1
        }
    ],
    "ok" : 1
}