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

Mongodb 在文档数组中查找按给定顺序出现的两个元素,mongodb,mongoose,mongodb-query,Mongodb,Mongoose,Mongodb Query,假设数据库中有以下文档结构: { 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:

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

{ 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的子文档的所有文档,但数组中项的出现必须满足以下要求A.index
{ 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版本支持的内容,您基本上有两种选择:

MongoDB 3.6 您最好在任何正常查询条件之外使用“添加”,以实际选择有效文档:

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 ] }
        ]}
      ]}
    ]
  }
})
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`
})
早期版本 同样,如果没有本机运算符,则需要使用JavaScript计算:

如果在聚合管道中需要此功能,则应使用与第一个示例类似的逻辑:

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"
    }
  }}
])
只需说“比较逻辑”实际上不是“查询运算符表达式”本身固有的,因此“最佳”应用于索引的唯一部分是在所有情况下都使用查询运算符。基本剩余逻辑实际上应用于主表达式求值的“之后”和“除此之外”,以便不返回除满足或表达式的结果之外的其他结果

每个属性的基本逻辑基本上都是从实际匹配
“value”
属性中相应值的“first”数组成员中提取
“index”
属性的值。如果该值为“小于”,则条件为
true
,且满足返回的文档

因此请注意,“计算出的求值”与查询运算符的效率匹配,并且在不“组合”使用能够访问“索引”的其他查询运算符条件的情况下,将启动“完整集合扫描”

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



另请参见文档和JavaScript

为什么不在获得文档后对其进行排序?嗨@GeorgeBailey。你能详细说明一下这个想法吗?我认为在这种情况下,排序没有帮助。我还想让MongoDB引擎完成所有的工作,这样我就不需要在我的后端代码中对其进行额外的筛选。您提到的对象包含10,20以外的值?你的问题有点不清楚抱歉:如果问题不清楚,请回答。字段“value”是一个整数,因此它可以保存任何有效的整数(如:),其中包含值为A=10和B=40的子文档,那么,这是如何预期的输出呢<代码>{name:'Some name',子文档:[{index:0,value:30},{index:1,value:40},{index:2,value:10},{index:3,value:20},{index:4,value:700},{index:5,value:40}]}非常感谢@Neil Lunn。我想我唯一需要的是返回我的B匹配的最后一个索引的东西。否则,如果A=10和B=40,我有一个子文档集合,比如:[40,10,40],它将与文档不匹配。这是因为它会发现10是A,第一个(而不是最后一个)40是B,索引将是A.index>B.index。。。所以没有匹配。不幸的是,我在猫鼬中没有看到类似的东西。也许我们可以在应用
$indexOfArray
操作符(用于B值)之前,以某种方式反转查询中的集合?@ukaszzkup您注意到我故意在答案主体中突出显示“first”。这不是你问题中的细节,你真的不应该在提问时做“lilke”…但是如果我也需要这个怎么办。是的,我们可以
“$reverse”
阵列。它在MongoDB和JavaScript中都是一个运算符。@Ukaszzkup不知道我为什么这么做,但最后添加了一些示例。不要再要求改动了。如果你有一个新问题,那么请改为。嗨@Neil Lunn,“但是”陈述不是额外的细节。这是我最初的问题自然产生的。我在后面的评论中提到了这一点,因为您最初的查询无法捕获集合为[40,10,40]的项目,其中a=10和B=40,即使它应该根据我最初问题中的条件进行匹配。因此,我只需要在查询的后半部分反转数组,我的意思是对于B部分,因为我们需要A的第一个索引和B的最后一个索引。无论如何,非常感谢您的时间和解决我的问题:)
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"
    }
  }}
])