【问题标题】:MongoDB - Query on the last element of an array?MongoDB - 查询数组的最后一个元素?
【发布时间】:2015-04-25 03:51:52
【问题描述】:

我知道 MongoDB 支持语法find{array.0.field:"value"},但我特别想对数组中的最后一个元素执行此操作,这意味着我不知道索引。是否有某种运算符可以解决这个问题,还是我不走运?

编辑:澄清一下,我希望 find() 仅返回数组最后一个元素中的字段与特定值匹配的文档。

【问题讨论】:

  • 您的文档是什么样的?
  • 好吧,我尝试测试的数组实际上嵌套在另一个数组中,但我认为这不会有任何影响。基本上我想做的是,在我的 find 选择器中,只返回数组最后一个元素中的特定字段与某个值匹配的文档。
  • 你应该看看aggregation,然后玩$unwind$project$match$group
  • 使用最新的 MongoDB,您可以这样做:find({"array.-1.field":"value"})
  • @MarsLee 在 4.2.0 版上对我不起作用 - 你有更多相关信息吗?

标签: arrays mongodb mongoose mongodb-query spring-data-mongodb


【解决方案1】:

您可以使用聚合解决此问题。

model.aggregate([
    {
      $addFields: {
        lastArrayElement: {
          $slice: ["$array", -1],
        },
      },
    },
    {
      $match: {
        "lastArrayElement.field": value,
      },
    },
  ]);

快速解释。 aggregate 创建一个动作管道,按顺序执行,这就是为什么它需要一个数组作为参数。首先我们使用$addFields 管道阶段。这是 3.4 版的新功能,基本上意味着:保留文档的所有现有字段,但还要添加以下内容。在我们的例子中,我们添加lastArrayElement 并将其定义为数组中名为array 的最后一个元素。接下来我们执行$match 管道阶段。对此的输入是前一阶段的输出,其中包括我们的新lastArrayElement 字段。这里我们说我们只包括其字段field 具有值value 的文档。

请注意,生成的匹配文档将包括lastArrayElement。如果出于某种原因你真的不想要这个,你可以在$match 之后添加一个$project 管道阶段来删除它。

【讨论】:

    【解决方案2】:

    您可以使用$expr(3.6 mongo 版本运算符)在常规查询中使用聚合函数。

    比较 query operatorsaggregation comparison operators

    对于标量数组

    db.col.find({$expr: {$gt: [{$arrayElemAt: ["$array", -1]}, value]}})
    

    对于嵌入式数组 - 使用带有点符号的 $arrayElemAt 表达式来投影最后一个元素。

    db.col.find({$expr: {$gt: [{"$arrayElemAt": ["$array.field", -1]}, value]}})
    

    春天@Query代码

    @Query("{$expr:{$gt:[{$arrayElemAt:[\"$array\", -1]}, ?0]}}")
    ReturnType MethodName(ArgType arg);
    

    【讨论】:

    • 对于嵌入式数组,为什么使用 0 而不是 -1?
    • 这个解决方案对我有用。我使用嵌入式数组来检查数组本身内的日期是否小于当前日期。如果它小于我更新文档。 const query = { $expr: { $lt: [{ $arrayElemAt: ["$lessons.studentData.dueDate", -1] }, new Date()] } }; const update = { completed: true }; Class.updateMany(query, update)
    • db.col.find({$expr: {$gt: [{$arrayElemAt: ["array", -1]}, value]}}) 为我抛出了一个错误。 --> db.col.find({$expr: {$gt: [{$arrayElemAt: ["$array", -1]}, value]}})
    【解决方案3】:

    Mongo 4.4开始,聚合运算符$last可用于访问数组的最后一个元素:


    例如,在find 查询中:

    // { "myArray": ["A", "B", "C"] }
    // { "myArray": ["D"] }
    db.collection.find({ $expr: { $eq: [{ $last: "$myArray" }, "C"] } })
    // { "myArray": ["A", "B", "C"] }
    

    或在aggregation 查询中:

    db.collection.aggregate([
      { $addFields: { last: { $last: "$myArray" } } },
      { $match: { last: "C" } }
    ])
    

    【讨论】:

      【解决方案4】:

      对于答案,请使用$arrayElemAt,如果我想要 orderNumber:"12345" 并且最后一个元素的值 $gt 而不是 "value"?如何制作 $expr?谢谢!

      对于嵌入式数组 - 使用带有点符号的 $arrayElemAt 表达式来投影最后一个元素。

       db.col.find({$expr: {$gt: [{"$arrayElemAt": ["$array.field", -1]}, value]}})
      

      【讨论】:

      • 感谢编辑;我试了几次,我想我解决了:``` db.col.find({$expr:{$and:[{$gt: [{"$arrayElemAt": ["$array.field", -1]}, 值]},{$eq:[{"$orderNumber"},"12345"]} ] }}) ```
      【解决方案5】:

      3.6 版使用聚合来实现相同的功能。

      db.getCollection('deviceTrackerHistory').aggregate([
         {
           $match:{clientId:"12"}
         },
         {
           $project:
            {
               deviceId:1,
               recent: { $arrayElemAt: [ "$history", -1 ] }
            }
         }
      ])
      

      【讨论】:

        【解决方案6】:

        使用 $slice

        db.collection.find( {}, { array_field: { $slice: -1 } } )
        

        编辑: 你可以利用 { <field>: { $elemMatch: { <query1>, <query2>, ... } } } 查找匹配项。

        但它不会准确地给出你正在寻找的东西。我认为这在 mongoDB 中是不可能的。

        【讨论】:

        • 这只是返回最后一个元素,对吧?这不是我想要做的。我希望我的 find 选择器只返回数组的最后一个元素与某个值匹配的文档。
        • 请记住,'$slice' 返回一个数组,而 '$arrayElemAt' 返回一个文档(相同的 sintax)。
        【解决方案7】:

        在 3.2 中这是可能的。第一个项目,让 myField 只包含最后一个元素,然后在 myField 上匹配。

        db.collection.aggregate([
           { $project: { id: 1, myField: { $slice: [ "$myField", -1 ] } } },
           { $match: { myField: "myValue" } }
        ]);
        

        【讨论】:

        • 在 3.2 中有 $arrayElemAt 运算符,它适用于负索引。所以这可以改为{ $project: { id: 1, myField: { $arrayElemAt: [ "$myField", -1 ] } } }。我不知道只有一个元素的切片是否同样有效
        • 这会准确输出问题想要的内容。
        • 从 MongoDB v3.4 开始,还有$addFields operator,这意味着您不再需要投影单个字段。
        【解决方案8】:

        不确定性能,但这对我很有效:

        db.getCollection('test').find(
          {
            $where: "this.someArray[this.someArray.length - 1] === 'pattern'"
          }
        )
        

        【讨论】:

        • $where 很慢,因为它运行的是javascript
        • 非常感谢!可能不是最高效的解决方案,但对我来说就像一个魅力。对于我的数据来说并没有那么慢,也许这种性能差异只有在大型数据集中才能察觉到。
        • 不好的做法@eagleeye。
        【解决方案9】:

        您可以在$push 时使用$position: 0,然后始终查询array.0 以获取最近添加的元素。当然,您将无法获得新的“最后”元素。

        【讨论】:

        • 我想过这样做,但是我的应用程序使用 Spring Data 访问 Mongo,当时不支持推送到数组的前面。不知道现在有没有。
        【解决方案10】:

        我在 Mongo Google 官方群 here 上发帖,并得到了他们工作人员的答复。看来我正在寻找的东西是不可能的。我将只使用不同的架构方法。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2018-05-04
          • 2020-02-22
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2016-12-25
          相关资源
          最近更新 更多