6

我有以下形式的文件集合:

{ _id: ObjectId(...)
, title: "foo"
, tags: ["bar", "baz", "qux"] 
}

查询应该找到所有带有这些标签的文档。我目前使用这个查询:

{ "tags": { "$in": ["bar", "hello"] } }

它有效;返回所有标记为“bar”或“hello”的文档。

但是,我想按相关性排序,即匹配的标签越多,文档应该出现在结果中的时间越早。例如,["bar", "hello", "baz"]在结果中标记的文档应该高于["bar", "baz", "boo"]为查询标记的文档["bar", "hello"]。我怎样才能做到这一点?

4

3 回答 3

12

MapReduce 并在客户端执行它会太慢 - 您应该使用聚合框架(MongoDB 2.2 中的新功能)。

它可能看起来像这样:

db.collection.aggregate([
   { $match : { "tags": { "$in": ["bar", "hello"] } } },
   { $unwind : "$tags" },
   { $match : { "tags": { "$in": ["bar", "hello"] } } },
   { $group : { _id: "$title", numRelTags: { $sum:1 } } },
   { $sort : { numRelTags : -1 } }
   //  optionally
   , { $limit : 10 }
])

请注意,第一个和第三个管道成员看起来相同,这是有意和需要的。以下是这些步骤的作用:

  1. 仅传递其中包含标签“bar”或“hello”的文档。
  2. 展开标签数组(意味着每个标签元素拆分为一个文档
  3. 只传递“bar”或“hello”的标签(即丢弃其余的标签)
  4. 按标题分组(也可以是“$_id”或原始文档的任何其他组合加起来它有多少标签(“bar”和“hello”)
  5. 按相关标签数量降序排序
  6. (可选)将返回的集合限制为前 10 个。
于 2012-10-07T21:28:57.903 回答
1

你可能会使用 MapReduce 来做类似的事情。您将在 Map 步骤中处理每个文档,确定有多少标签与查询匹配,并分配一个分数。然后您可以根据该分数进行排序。

http://www.mongodb.org/display/DOCS/MapReduce

于 2012-10-07T19:02:25.343 回答
0

查询后应该做一些复杂的事情。通过 db.eval 的服务器端(如果您的客户端支持此)或只是客户端。这是您正在寻找的示例。

它将检索具有您指定的标签的所有帖子,然后根据匹配的数量对它们进行排序。

删除 db.eva( 部分并将其翻译成您的客户端用来查询以获得客户端效果的语言(

db.eval(function () {
    var tags = ["a","b","c"];
    return db.posts.find({tags:{$in:tags}}).toArray().sort(function(a,b){

        var matches_a = 0;
        var matches_b = 0;
        a.tags.forEach(function (tag) {
            for (t in tags) {
                if (tag == t) {
                    matches_a++;
                } else {
                    matches_b++;
                }
            }
        });

        b.tags.forEach(function(tag) {
            for (t in tags) {
                if (tag == t) {
                    matches_b++;
                } else {
                    matches_a++;
                }
            }
        });
        return matches_a - matches_b;
    });
});
于 2012-10-07T19:40:59.390 回答