我们假设我们在数据库中记录了以下结构。
{
"_id": 1234,
"tags" : [ "t1", "t2", "t3" ]
}
现在,我想检查数据库是否包含带有数组tagsArray which is [ "t3", "t4", "t5" ]
中指定的任何标记的记录
我知道$in
运算符,但我不仅想知道数据库中的任何记录是否有tagsArray中指定的任何标记,我还想知道数据库中哪个记录的标记与任何标记匹配tagsArray中指定的标记。 (即上述记录的情况下为t3)
也就是说,我想比较两个数组(记录之一和我给出的其他数组)并找出共同的元素。
我需要在查询中使用此表达式以及许多表达式,因此像$,$ elematch等投影运算符不会有多大用处。 (或者有没有办法可以在不必迭代所有记录的情况下使用它?)
我认为我可以使用$where
运算符,但我认为这不是最好的方法。
如何解决这个问题?
答案 0 :(得分:5)
有几种方法可以做你想要的,它只取决于你的MongoDB版本。只需提交shell响应即可。内容基本上是JSON表示,对于Java中的DBObject实体或者要在服务器上执行的JavaScript来说并不难翻译,因此实际上不会改变。
第一个也是最快的方法是使用MongoDB 2.6及更高版本来获得新的设置操作:
var test = [ "t3", "t4", "t5" ];
db.collection.aggregate([
{ "$match": { "tags": {"$in": test } }},
{ "$project": {
"tagMatch": {
"$setIntersection": [
"$tags",
test
]
},
"sizeMatch": {
"$size": {
"$setIntersection": [
"$tags",
test
]
}
}
}},
{ "$match": { "sizeMatch": { "$gte": 1 } } },
{ "$project": { "tagMatch": 1 } }
])
新的运算符有$setIntersection
正在执行主要工作,而$size
运算符用于测量数组大小并有助于后者过滤。最终作为"集"的基本比较。为了找到相交的项目。
如果你有早期版本的MongoDB,那么这仍然是可能的,但你需要更多的阶段,这可能会影响性能,这取决于你是否有大型数组:
var test = [ "t3", "t4", "t5" ];
db.collection.aggregate([
{ "$match": { "tags": {"$in": test } }},
{ "$project": {
"tags": 1,
"match": { "$const": test }
}},
{ "$unwind": "$tags" },
{ "$unwind": "$match" },
{ "$project": {
"tags": 1,
"matched": { "$eq": [ "$tags", "$match" ] }
}},
{ "$match": { "matched": true }},
{ "$group": {
"_id": "$_id",
"tagMatch": { "$push": "$tags" },
"count": { "$sum": 1 }
}}
{ "$match": { "count": { "$gte": 1 } }},
{ "$project": { "tagMatch": 1 }}
])
或者,如果所有这些似乎都涉及或者您的数组足够大以产生性能差异,那么总是mapReduce:
var test = [ "t3", "t4", "t5" ];
db.collection.mapReduce(
function () {
var intersection = this.tags.filter(function(x){
return ( test.indexOf( x ) != -1 );
});
if ( intersection.length > 0 )
emit ( this._id, intersection );
},
function(){},
{
"query": { "tags": { "$in": test } },
"scope": { "test": test },
"output": { "inline": 1 }
}
)
请注意,在所有情况下,$in
运算符仍可帮助您减少结果,即使它不是完全匹配。另一个常见因素是检查"尺寸"交叉点的结果是减少响应。
所有这些都非常容易编码,说服老板切换到MongoDB 2.6或更高版本,如果你还没有获得最佳效果的话。