按与MongoDB的相关性排序

时间:2012-10-07 16:32:04

标签: mongodb sorting

我有以下表格的文件集合:

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

查询应查找包含任何这些标记的所有文档。我目前使用此查询:

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

它有效;所有标记为“bar”或“hello”的文件都将被退回。

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

3 个答案:

答案 0 :(得分:10)

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名。

答案 1 :(得分:1)

你可能会使用MapReduce这样的东西。您将在“地图”步骤中处理每个文档,确定与查询匹配的标记数量,并指定分数。然后你可以根据那个分数进行排序。

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

答案 2 :(得分: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;
    });
});