MongoDb:从查询中的两个数组中查找公共元素

时间:2014-06-25 13:57:04

标签: mongodb mapreduce aggregation-framework

我们假设我们在数据库中记录了以下结构。

{
  "_id": 1234,
  "tags" : [ "t1", "t2", "t3" ]
}

现在,我想检查数据库是否包含带有数组tagsArray which is [ "t3", "t4", "t5" ]中指定的任何标记的记录

我知道$in运算符,但我不仅想知道数据库中的任何记录是否有tagsArray中指定的任何标记,我还想知道数据库中哪个记录的标记与任何标记匹配tagsArray中指定的标记。 (即上述记录的情况下为t3)

也就是说,我想比较两个数组(记录之一和我给出的其他数组)并找出共同的元素。

我需要在查询中使用此表达式以及许多表达式,因此像$,$ elematch等投影运算符不会有多大用处。 (或者有没有办法可以在不必迭代所有记录的情况下使用它?)

我认为我可以使用$where运算符,但我认为这不是最好的方法。 如何解决这个问题?

1 个答案:

答案 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或更高版本,如果你还没有获得最佳效果的话。