假设我们在数据库中有以下结构的记录:
{
"_id": 1234,
"tags" : [ "t1", "t2", "t3" ]
}
现在,我想检查数据库是否包含在数组tagsArray which is [ "t3", "t4", "t5" ]
我知道$in
操作符,但我不仅想知道数据库中的任何记录是否有tagsArray中指定的任何标签,我还想知道数据库中记录的哪个标签与tagsArray中指定的任何标签匹配。(即t3在上述记录的情况下)
也就是说,我想比较两个数组(一个是记录,另一个是我给出的)并找出公共元素。
我需要在查询中有这个表达式以及许多表达式,因此像$,$ eleatch等投影运算符不会有太大用处。(或者有没有一种方法可以使用它而不必遍历所有记录?)
我认为我可以使用$where
操作符,但我不认为这是最好的方法。如何解决这个问题?
有几种方法可以做你想做的事情,这取决于你的MongoDB版本。只是提交shell响应。内容基本上是JSON表示,不难翻译成Java中的DBObject实体,或者在服务器上执行的JavaScript,所以这真的不会改变。
第一个也是最快的方法是MongoDB 2.6和更高版本,在那里你可以获得新的set操作: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或更高版本,如果你还没有最好的结果。