【问题标题】:Query to find documents where all array property elements are in array查询以查找所有数组属性元素都在数组中的文档
【发布时间】:2018-05-25 16:52:16
【问题描述】:

我有一个像这样的文档的集合:

{
_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"]}

我想找到所有 arrayProperty 元素都包含在["1", "2", "3", "4"]中的文档

它应该返回:

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

【问题讨论】:

    标签: mongodb


    【解决方案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 成为值得添加到表达式的过滤器,而“本机查询 运算符”表达式是执行此操作的最快方法。

    $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 运算符实际上是您真正想要的。所有版本也都支持它。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-12-11
      • 1970-01-01
      • 2013-10-11
      • 2017-09-21
      • 1970-01-01
      • 2021-03-16
      • 2016-04-14
      • 1970-01-01
      相关资源
      最近更新 更多