【问题标题】:Match docs having all array element within $gte $lte匹配具有 $gte $lte 内所有数组元素的文档
【发布时间】:2016-04-20 10:58:33
【问题描述】:

这是我的示例文档:

{
updated: [
    1461062102,
    1461062316
],
name: "test1",
etc: "etc"
}

{
updated: [
    1460965492,
    1461060275
],
name: "test2",
etc: "etc"
}


{
updated: [
    1461084505
],
name: "test3",
etc: "etc"
}

{
updated: [
    1461060430
],
name: "test4",
etc: "etc"
}

{
updated: [
    1460965715,
    1461060998
],
name: "test5",
etc: "etc"
}

$gte$lte 条件内获取与更新日期匹配的所有文档的 find 查询的正确用法是什么?

例如

db.test.find({'updated':{$elemMatch:{$gte:1461013201,$lte:1461099599}}})

我可以使用 $or 并将其设置为 updated.0:{$gte:1461013201,$lte:1461099599}update.1:{$gte:1461013201,$lte:1461099599} 等,但如果我的数组包含更多更新日期怎么办?

据我了解,$elemMatch 不符合我的标准,因为它只匹配数组中的第一次出现。

【问题讨论】:

  • 你用 map reduce 试过了吗?
  • @bartektartanus mapReduce 在几乎所有方面都是聚合框架的“差劲”表亲。如今,它的主要用途是用于糟糕的文档结构,在这种情况下,您别无选择,只能使用 JavaScript 处理进行元素遍历。您几乎总是希望使用原生运算符而不是 JavaScript 处理。
  • 不幸的是,我知道“又差又慢”的 map reduce。但正如你所说 - 有时是唯一的选择。很高兴你找到另一个聚合 :)

标签: mongodb mongodb-query aggregation-framework


【解决方案1】:

好问题。 $elemMatch 是正确的,但这确实需要标准运算符中未涵盖的其他逻辑。

所以你要么用$redact

db.test.aggregate([
  { "$match": {
    'updated': { '$elemMatch':{ '$gte':1461013201, '$lte':1461099599 } }
  }},
  { "$redact": {
    "$cond": {
      "if": { 
        "$allElementsTrue": {
          "$map": {
            "input": "$updated",
            "as": "upd",
            "in": {
              "$and": [
                { "$gte": [ "$$upd", 1461013201 ] },
                { "$lte": [ "$$upd", 1461099599 ] }
              ]
            }
          }
        }
      },
      "then": "$$KEEP",
      "else": "$$PRUNE"
    }
  }}
])

或者在 MongoDB 2.6 之前的版本中,您使用 $where 子句处理:

db.test.find({
  'updated': { '$elemMatch':{ '$gte':1461013201, '$lte':1461099599 } },
  "$where": function() {
    return this.updated.filter(function(el) { 
      return el >= 1461013201 &&  el <= 1461099599;
    }).length == this.updated.length;
  }
})

问题在于,虽然一般的原生“查询”运算符可以告诉您一个数组成员满足条件,但它不能告诉您所有都满足。

因此,可以使用$map$allElementsTrue 测试条件,这两者都可以从MongoDB 2.6 获得。在 MongoDB 3.2 中,$filter$size 相当于下面的 JavaScript 测试。

或者,您也可以使用 $where 的 JavaScript 评估来测试“过滤后的”数组长度与原始数组的长度是否相同。

这是为了查看 all 与提供的范围条件匹配而构建的附加逻辑。聚合方法是本机代码,而不是 JavaScript 解释。相比之下,它的运行速度要快得多

但您仍然希望在所有情况下都保留 $elemMatch

当然,这里是匹配的文件:

{
        "updated" : [
                1461062102,
                1461062316
        ],
        "name" : "test1",
        "etc" : "etc"
}
{
        "updated" : [
                1461084505
        ],
        "name" : "test3",
        "etc" : "etc"
}
{
        "updated" : [
                1461060430
        ],
        "name" : "test4",
        "etc" : "etc"
}

【讨论】:

  • 感谢您的详细解答!这似乎是使用聚合框架的正确方法。但是当我测试它时,它给出了一个错误:“异常:$allElementsTrue 的参数必须是一个数组”
  • @moogeek 您是否“完全”复制了上面的代码?你有哪个 MongoDB 版本?我正在使用您问题中发布的数据在此处运行确切的代码,其中任何一个都没有任何更改。 $map 表达式确实返回了一个数组,所以我猜你实际上改变了一些东西。
  • 是的,抱歉,我没有更新我在生产中使用的真实密钥名称。现在它可以工作了,但没有显示任何结果。
  • @moogeek 这就是为什么当您在这里提问时永远不要抽象问题,因为您不可避免地会错过对翻译至关重要的细节,然后您不知道如何自己做。仔细查看您的真实数据并考虑您需要应用的更改。如果你不能解决,那么最好ask another question这次包含真实的细节。这样我们就可以清楚地看到它需要如何应用。
  • 谢谢,我明白了。不幸的是,我在每个文档中的真实数据都很大,并且可能包含一些不允许我在这里分享的机密细节。我将尝试重现该问题并找出查询出了什么问题
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-07-01
  • 2022-01-17
  • 2020-06-19
  • 1970-01-01
  • 1970-01-01
  • 2017-05-27
  • 1970-01-01
相关资源
最近更新 更多