【问题标题】:Check last element in array matches a condition检查数组中的最后一个元素是否匹配条件
【发布时间】:2015-10-31 11:43:36
【问题描述】:

我的 mongodb 文档中有一个数字数组,需要检查该数组中的最后一个数字是否符合我的条件。

我的文档是这样存储的:

  {
    name: String,
    data: {
      dates: Array,
      numbers: Array
    }
  }

我需要检查numbers 中的最后一个数字是否“位于”另外两个数字之间。

任何关于如何做到这一点的建议将不胜感激。

【问题讨论】:

    标签: mongodb mongoose mongodb-query aggregation-framework


    【解决方案1】:

    目前最有效的方法是使用 $where 的 JavaScript 评估,因为您可以简单地找到最后一个数组元素的值并以编程方式对其进行测试。

    附样本文件:

    { "a": [1,2,3] },
    { "a": [1,2,4] },
    { "a": [1,2,5] }
    

    查询:

    db.collection.find(function() { var a = this.a.pop(); return ( a > 2 ) & ( a < 5 ) })
    

    或者简单地用$where作为一个字符串来进行评估:

    Model.find(
        { 
            "$where": "var a = this.a.pop(); return ( a > 2 ) && ( a < 5 )"
        },
        function(err,results) {
            // handling here
        }
    );
    

    这是一种非常简单的方法,并且在为“非规范化”和处理数组而创建的聚合框架中没有“开销”,例如 $unwind。那里效率不高。

    然而,在“未来”中,它将会。正如目前在开发版本中可用的那样,聚合框架有一个 $slice 运算符。此运算符将允许轻松访问“最后一个”数组元素以进行测试。

    由于聚合框架运算符是在“本机代码”中而不是 JavaScript 来解释的,因此单个管道阶段会变得比 JavaScript 形式更有效。虽然这个清单在提交时看起来更长:

    db.collection.aggregate([
      { "$redact": {
        "$cond": {
          "if": {
            "$anyElementTrue": {
              "$map": {
                "input": { "$slice": ["$a",-1] },
                "as": "el",
                "in":{
                  "$and": [
                    { "$gt": [ "$$el", 2 ] },
                    { "$lt": [ "$$el", 5 ] }
                  ]
                }
              }
            }
          },
          "then": "$$KEEP",
          "else": "$$PRUNE"
        }
      }}
    ])
    

    已经存在的$redact操作符在这里用比较表达式来“逻辑过滤”。根据 true/false 匹配条件,它分别从结果中“保留”或“修剪”文档。

    $slice 运算符本身在它的聚合框架形式中仍然会不合时宜地返回一个数组,尽管在这种情况下是一个单元素数组。这就是为什么使用$map 将每个元素“转换”为true/false 条件,而$anyElementTrue 运算符将“数组”简化为$cond 所表示的单一响应。

    所以当它发布时,这将是最有效的方法。但在那之前,请坚持使用 JavaScript,因为它是目前进行此评估的最快方式。

    两个查询表单都只返回示例的前两个文档:

    { "a": [1,2,3] },
    { "a": [1,2,4] }
    

    【讨论】:

    • 我的例子的等价物是“this.data.numbers.pop()”吗?
    • @martin 对不起,是的。因为它只是一个嵌套对象而不是数组本身,所以这很好。但请确保您的结构是一致的,否则会显着改变响应。
    • 如果你能展示如何在 Mongoose 中使用这个查询,那就太完美了,我一直在尝试实现它,但它只返回一个循环结构。
    • @martin 没有区别。没有猫鼬查询结构,只有MongoDB。 $where 运算符是一个 JavaScript 条件,表示为发送到服务器的字符串。还是添加了示例。
    【解决方案2】:

    MongoDB aggregate 可能是一个可行的方法。假设文档中的 name 字段是唯一的。

    如果您有示例文档。

       {
         name: "allen",
         data: {
          dates: ["2015-08-08"],
          numbers: [20, 21, 22, 23]
         }
       }
    

    以下代码用于进行检查。由于 db.collection.aggregate() 方法返回一个游标,然后我们可以使用游标的hasNext 来判断最后一个数字是否介于给定的两个数字之间。

    var result = db.last_one.aggregate(
     [
       {
          // deconstruct the array field numbers
          $unwind: "$data.numbers"
       },
       { 
         $group: { 
          _id: "$name", 
          // lastNumber is 23 in this case 
          lastNumber: { $last: "$data.numbers" }
         } 
       },
       {
        $match: {
           lastNumber: { $gt: num1, $lt: num2 }
        }
       }
     ]  
    ).hasNext()
    
    if (result) print("matched"); else print("not matched")
    

    例如num1为22,num2为24,则结果匹配;如果 num1 为 21,num2 为 22,则结果不匹配。

    但实际上,按名称分组并不是一个好主意。如果您的文档有一个唯一的 ObjectId 会更好,然后我们可以根据该 _id 进行分组。

    【讨论】:

    • 如果我有一个名为 Company 的猫鼬模型,你如何结合 find() 语句使用它?很抱歉,如果这很明显,但我从未在 mongoDB 中进行过高级查询。
    • 嘿,你不用find,不好意思我对mongoose不熟悉。您可以查看mongoose-aggregate-and-sort 的帖子,了解如何将聚合与 mongoose 一起使用。
    猜你喜欢
    • 2014-06-28
    • 1970-01-01
    • 2017-02-10
    • 1970-01-01
    • 2020-02-23
    • 1970-01-01
    • 2019-04-08
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多