【问题标题】:MongoDb : Find common element from two arrays within a queryMongoDb:从查询中的两个数组中查找公共元素
【发布时间】:2014-06-25 13:57:03
【问题描述】:

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

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

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

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

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

我需要在查询中包含这个表达式以及许多表达式,这样 $、$elematch 等投影运算符就没有多大用处了。 (或者有没有一种方法可以使用它而不必遍历所有记录?)

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

【问题讨论】:

  • 您是说给定上述示例文档和您的测试列表,您会期望结果数组包含“t3”吗?同样,如果一个文档同时具有“t3”和“t4”,那么这将是该文档的结果吗?否则,如果您只想知道匹配的文档,那么您实际上需要$in。无论如何,$where 不是最好的选择,因为您可能会建议您无法“过滤”。
  • 是的,我想要一个包含公共元素的数组或第一个公共元素。我不想只得到匹配的文件。这就是为什么 $in 没有多大帮助的原因。

标签: mongodb mapreduce aggregation-framework


【解决方案1】:

有几种方法可以做你想做的事,这取决于你的 MongoDB 版本。只需提交外壳响应。内容基本上是 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 或更高版本。

【讨论】:

  • 这看起来很棒!我将很快对此进行测试并考虑接受答案。感谢您的出色帮助。
猜你喜欢
  • 2015-07-20
  • 1970-01-01
  • 1970-01-01
  • 2020-03-26
  • 1970-01-01
  • 1970-01-01
  • 2019-02-11
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多