给出以下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)
在关系模式中,标签将是一个单独的表,您可以加入该表,计算匹配,并按计数排序。
但是,在Mongo ......?
似乎这种方法可行,
但我不清楚如何将其作为Mongo查询实现,或者这是否是最有效的方法。
答案 0 :(得分:8)
我在In MongoDB search in an array and sort by number of matches
回答可以使用聚合框架。
<强>假设强>
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
}
答案 1 :(得分:5)
现在,除非您使用MapReduce,否则不可能。 MapReduce的唯一问题是它很慢(与普通查询相比)。
聚合框架定于2.2(因此应该在2.1开发版中提供)并且如果没有MapReduce,应该更容易做到这一点。
就个人而言,我认为使用M / R并不是一种有效的方式。我宁愿查询所有文档并在应用程序端进行这些计算。扩展应用服务器比扩展数据库服务器更容易,也更便宜,所以让应用服务器进行数字运算。其中,考虑到您的数据访问模式和要求,这种方法可能对您不起作用。
更简单的方法可能是在每个标记对象中包含count
属性,每当$push
新数据添加到数组时,您$inc
count
{{1}} 1}}属性。这是MongoDB世界中的一种常见模式,至少在聚合框架之前。
答案 2 :(得分: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();