开发者问题收集

如何比较同一文档中数组中的元素?

2017-11-13
355

我找到了比较同一 MongoDB 集合中不同文档中数组值的方法,但是 如何按索引比较同一文档中同一数组中的元素? 以下是相关条目:

{ "_id" : ObjectId("1"), "arr" : [ { "int" : 100 }, { "int" : 10 } ] }

我有一个包含大量此类条目的集合(当然这里进行了简化),我想查询该集合,检查每个条目,如果 arr[0].int > arr[1].int 则返回这些文档。更好的方法是使用逻辑来确定 索引 1 处的元素与 索引 0 处的元素的百分比差异。因此,在此示例中,例如, 索引 1 比索引 0 小 10 倍

以下方法可用于查询 mongoDoc 中的元素是否大于给定值:

db.collection.find( { "arr.0.int" : { $gt: 10 }})

我试过这个 - 但没有什么用。 由于数据集很大 - 性能考虑会很棒!

谢谢!

1个回答

这里的基本情况是简单地应用 $redact $arrayElemAt 的条件,以便检查每个索引:

db.collection.aggregate([
  { "$redact": {
    "$cond": {
      "if": {
        "$gt": [
          { "$arrayElemAt": ["$arr.int", 0] },
          { "$arrayElemAt": ["$arr.int", 1] }
        ]
      },
      "then": "$$KEEP",
      "else": "$$PRUNE"
    }
  }}
])

这是一个特殊的管道阶段,它使用 $cond 以满足 “if” 条件 “then” 我们 “$$KEEP” 结果中的文档,否则 “else” 我们 “$$PRUNE” 结果中的文档。

这使用本机运算符,并且与依赖于计算的查询一样“高效”。

MongoDB 3.6 允许使用 $expr 对此使用更简短的语法:

db.collection.aggregate([
  { "$match": { 
    "$expr": {
      "$gt": [
        { "$arrayElemAt": ["$arr.int", 0] },
        { "$arrayElemAt": ["$arr.int", 1] }
      ]
    }
  }}
])

甚至可以在 .find() 中使用:

db.collection.find({
  "$expr": {
    "$gt": [
      { "$arrayElemAt": ["$arr.int", 0] },
      { "$arrayElemAt": ["$arr.int", 1] }
    ]
  }
})

并且自发布以来的所有版本都支持通过 $where :

db.collection.find({
  "$where": "return this.arr[0].int > this.arr[1].int"
})

但是,由于这需要对每个文档的 JavaScript 表达式进行评估,因此它的性能不如使用本机运算符。

此外,获取“比率”响应的唯一方法是使用聚合管道,该管道实际上可以“改变”返回的结果,而 .find() 查询无法做到这一点:

db.collection.aggregate([
  { "$redact": {
    "$cond": {
      "if": {
        "$gt": [
          { "$arrayElemAt": ["$arr.int", 0] },
          { "$arrayElemAt": ["$arr.int", 1] }
        ]
      },
      "then": "$$KEEP",
      "else": "$$PRUNE"
    }
  }},
  { "$addFields": {
    "ratio": {
      "$divide": [
        { "$arrayElemAt": ["$arr.int", 1] },
        { "$arrayElemAt": ["$arr.int", 0] }
      ]         
    }
  }}
])

无论如何,“计算作为条件”并没有什么好处,因为除了遍历集合中的每个文档并查看它是否符合条件之外,没有其他选择。

因此,如果这是“常见逻辑”,那么您最好将其存储在文档中。即:

{ "arr": [{ "int": 100 },{ "int": 10 }], "firstIsGreater": true }

然后您实际上 “可以” 以有效的方式使用索引进行选择。因此,当您更改文档中的内容时,编写此条件将取决于您的应用程序逻辑,从而让您不需要这样的计算。

如果您不能以这种方式建模和存储,那么任何一种形式的计算都是您所拥有的。所以通常最好先考虑一下为什么您认为您需要这样一个计算条件。

Neil Lunn
2017-11-13