查询以查找所有数组属性元素都在数组中的文档

时间:2018-05-25 16:52:16

标签: mongodb

我有一个包含如下文档的集合:

{
_id : "1",
arrayProperty : ["1","2","3"]
}

我想找到一些包含arrayProperty元素的文档。

假设我有这个集合:

{_id : "1", arrayProperty : ["1", "2"]}
{_id : "2", arrayProperty : ["1", "4"]}
{_id : "3", arrayProperty : ["1", "7", "8"]}
{_id : "4", arrayProperty : ["1", "9"]}

我希望找到包含["1", "2", "3", "4"]

中所有arrayProperty元素的文档

它应该返回:

{_id : "1", arrayProperty : ["1", "2"]}
{_id : "2", arrayProperty : ["1", "4"]}

1 个答案:

答案 0 :(得分:1)

这里的基本概念是查找不在每个数组元素的可能值列表中的内容,然后“排除”该文档。这意味着将$elemMatch$nin用于列表,使用$not来反转逻辑:

db.collection.find({ 
  "arrayProperty": { 
    "$not": { "$elemMatch": { "$nin": ["1", "2", "3", "4"] } } 
  }
})

返回正确的文件:

{ "_id" : "1", "arrayProperty" : [ "1", "2" ] }
{ "_id" : "2", "arrayProperty" : [ "1", "4" ] }

实际上使用native operators in the "query engine"来评估表达式而不是"forced calculation"$expr$where我们将在后面提到。这是正确的结果,但这里唯一的问题是运算符模式实际上否定了任何索引的使用。幸运的是,我们可以做些什么:

db.collection.find({
  "arrayProperty": {
    "$in": ["1", "2", "3", "4"],
    "$not": { "$elemMatch": { "$nin": ["1", "2", "3", "4"] } }
  } 
})

虽然起初看起来有点滑稽,但在这里添加$in是一个有效的条件。它对查询的作用是强制在选择有效文档时实际使用索引。在问题样本中仍然是“所有”文档,但在现实世界中并非所有事物通常都会匹配参数列表。

基本上它会改变解析的查询条件:

"winningPlan" : { "stage" : "COLLSCAN"

对此:

"winningPlan" : { "stage" : "FETCH",

"inputStage" : { "stage" : "IXSCAN",

这使$in成为一个值得添加到表达式的过滤器,“native query operator”表达式是最快的方法。

$expr的问题(除了只能从MongoDB 3.6获得)是因为它意味着需要扫描“整个集合”以应用它包含的“聚合运算符表达式”。当然,我们也刚刚了解了$in添加到查询中的内容

db.collection.find({
   "arrayProperty": { "$in": ["1", "2", "3", "4"] },
   "$expr": { "$setIsSubset": ["$arrayProperty", ["1", "2", "3", "4"]] }
})

这有一个类似的IXSCAN输入,其中由于$in而存在索引,并且仅使用$setIsSubset布尔条件来拒绝索引选择中找到的其他文档。

早期MongoDB版本的早期使用形式不太理想:

db.collection.aggregate([
  { "$match": { "$in": ["1", "2", "3", "4"] } },
  { "$redact": {
    "if": { "$setIsSubset": ["$arrayProperty", ["1", "2", "3", "4"]] },
    "then": "$$KEEP",
    "else": "$$PRUNE"
  }}
])

或使用$where

db.collection.find({
  "arrayProperty": { "$in": ["1", "2", "3", "4"] },
  "$where": function() {
    return this.arrayProperty.every(a => ["1", "2", "3", "4"].some(s => a === s))
  }
})

所有实际上所有工作都已完成,但$elemMatch$nin$not的组合,也包括用于索引选择的$in运算符实际上就是你真正的想。它也支持所有版本。