我有以下表格的文件集合:
{ _id: ObjectId(...)
, title: "foo"
, tags: ["bar", "baz", "qux"]
}
查询应查找包含任何这些标记的所有文档。我目前使用此查询:
{ "tags": { "$in": ["bar", "hello"] } }
它有效;所有标记为“bar”或“hello”的文件都将被退回。
但是,我想按相关性进行排序,即文档在结果中出现的越早,匹配标记越多。例如,标记为["bar", "hello", "baz"]
的文档在结果中应高于标记为["bar", "baz", "boo"]
的查询["bar", "hello"]
的文档。我怎样才能做到这一点?
答案 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 :(得分:1)
你可能会使用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;
});
});