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)

在关系模式中,标签将是一个单独的表,您可以连接该表,计算匹配项,并按计数排序。

但是,在蒙古...?

似乎这种方法可以工作,

  • 将输入标签分成多个“IN”语句
  • 通过将标签输入“或”在一起来查询项目
    • 即 where ('shirt' IN items.tags ) OR ('cotton' IN items.tags )
    • 例如,这将返回三个“衬衫一号”实例、两个“衬衫三号”实例等
  • 映射/减少该输出
    • 地图:发射(this._id,{...});
    • 减少:计算_id的总出现次数
    • finalize:按总数排序

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

4

3 回答 3

9

正如我在 MongoDB 中回答的那样,在数组中搜索并按匹配数排序

可以使用聚合框架。

假设

  • 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
}
于 2012-09-13T15:23:12.353 回答
5

现在,除非您使用 MapReduce,否则这是不可能的。MapReduce 的唯一问题是它很慢(与普通查询相比)。

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

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

一种更简单的方法可能是count在每个标签对象中包含一个属性,并且每当您$push向数组添加新标签时,您也可以添加$inccount属性。这是 MongoDB 世界中的常见模式,至少在聚合框架之前是这样。

于 2011-12-23T14:58:35.210 回答
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();
于 2011-12-23T15:08:57.743 回答