查找以给定顺序出现的文档数组中的两个元素

问题描述 投票:0回答:1

假设我在数据库中有以下文档结构:

{ name: String,
  subDocs: [{
    index: Number,
    value: Number
  }]
}

所以典型的文档看起来像:

{ name: 'Document 1',
  subDocs: [ 
    { index: 0, value: 30 },
    { index: 1, value: 40 },
    { index: 2, value: 10 },
    { index: 3, value: 20 },
    { index: 4, value: 700 },
    { index: 5, value: 40 }
  ]
}

现在,我想找到包含值为A = 10和B = 40的subDocs的所有文档。但是数组中项目的出现必须满足以下要求A.index <B.index。因此,基本上具有值A的项必须出现在集合中比B更早的位置。因此上面的对象满足要求而不是这个,因为值不会出现在顺序中:

{ name: 'Document 2',
  subDocs: [ 
    { index: 0, value: 40 },
    { index: 1, value: 70 },
    { index: 2, value: 10 },
    { index: 3, value: 20 },
    { index: 4, value: 700 }
  ]
}

我们可以用Mongoose实现它而不牺牲这种查询的性能吗?

mongodb mongoose mongodb-query
1个回答
1
投票

如果你想在查询中使用那种约束,那么你基本上有两个选项,具体取决于你的MongoDB版本支持的内容:

MongoDB 3.6

您最好在任何正常查询条件下“使用”$expr来实际选择有效文档:

var A = 10, B = 40;

Model.find({
  "subDocs.value": { "$all": [A, B] },
  "$expr": {
    "$lt": [
      { "$arrayElemAt": [
        "$subDocs.index",
        { "$indexOfArray": [ "$subDocs.value", A ]}
      ]},
      { "$arrayElemAt": [
        "$subDocs.index",
        { "$indexOfArray": [ "$subDocs.value", B ]}  
      ]}
    ]
  }
})

或匹配“最后”事件:

Model.find({
  "subDocs.value": { "$all": [A, B] },
  "$expr": {
    "$lt": [
      { "$arrayElemAt": [
        "$subDocs.index",
        { "$subtract": [
          { "$subtract": [{ "$size": "$subDocs.value" }, 1 ] },
          { "$indexOfArray": [ { "$reverseArray": "$subDocs.value" }, A ] }
        ]}
      ]},
      { "$arrayElemAt": [
        "$subDocs.index",
        { "$subtract": [
          { "$subtract": [{ "$size": "$subDocs.value" }, 1 ] },
          { "$indexOfArray": [ { "$reverseArray": "$subDocs.value" }, B ] }
        ]}
      ]}
    ]
  }
})

Earlier Versions

同样的事情,但没有本机运算符,您需要使用$where的JavaScript评估:

var A = 10, B = 40;

Model.find({
  "subDocs.value": { "$all": [A, B] },
  "$where": `this.subDocs.find( e => e.value === ${A}).index
      < this.subDocs.find( e => e.value === ${B}).index`
})

或匹配“最后”事件:

Model.find({
  "subDocs.value": { "$all": [10,40] },
  "$where": `let arr = this.subDocs.reverse();
      return arr.find( e => e.value === ${A}).index
        > arr.find( e => e.value === ${B}).index`
})

如果您在聚合管道中需要它,那么您将使用$redact和类似的逻辑来代替第一个示例:

var A = 10, B = 40;

Model.aggregate([
  { "$match": { "subDocs.value": { "$all": [A, B] } } },
  { "$redact": {
    "$cond": {
      "if": {
        "$lt": [
          { "$arrayElemAt": [
            "$subDocs.index",
            { "$indexOfArray": [ "$subDocs.value", A ]}
          ]},
          { "$arrayElemAt": [
            "$subDocs.index",
            { "$indexOfArray": [ "$subDocs.value", B ]}  
          ]}
        ]
      },
      "then": "$$KEEP",
      "else": "$$PRUNE"
    }
  }}
])

可以说“比较逻辑”实际上并不是“查询运算符表达式”本身的原生,因此“最佳”可以应用于索引的唯一部分是在所有情况下使用$all查询运算符。剩下的基本逻辑实际上应用“在”主要表达式被评估之后和“除了”之外,以便没有返回任何结果,除了那些满足$expr$where表达式的结果。

每个的基本逻辑基本上是从“第一个”数组成员中提取"index"属性的值,该成员实际匹配"value"属性中的相应值。如果这是“小于”,那么条件是true,这满足了返回的文件。

因此,请注意,“计算的评估”与查询运算符的效率相匹配,并且不与其他能够访问“索引”的查询运算符条件“组合”使用,则将启动“完整集合扫描”。

但是总体结果肯定比将所有匹配项返回到第一个查询条件然后在“从数据库返回”之后的光标上拒绝它们更有效。


另请参阅$arrayElemAt$indexOfArray$ltArray.find()的JavaScript文档

© www.soinside.com 2019 - 2024. All rights reserved.