【问题标题】:How to find into mongodb to the last item of an array?如何在mongodb中找到数组的最后一项?
【发布时间】:2012-05-19 12:07:41
【问题描述】:

我想查找数组中最后一个元素等于某个值的文档。 数组元素可以通过特定的数组位置访问:

// i.e. comments[0].by == "Abe"
db.example.find( { "comments.0.by" : "Abe" } )

但我如何使用最后一项作为条件进行搜索? 即

db.example.find( { "comments.last.by" : "Abe" } )

顺便说一下,我用的是php

【问题讨论】:

  • 您要查找数组中最后一个元素等于某个值的文档吗?
  • 那你就不行了。除非您将数组长度存储在单独的字段中。但这仍然需要两个查询。如果有人证明我错了,我会很高兴:)

标签: php mongodb mongodb-query aggregation-framework


【解决方案1】:

我知道这个问题很老,但我在回答similar new question 后在谷歌上找到了它。所以我认为这应该得到同样的待遇。

您可以改用aggregate 来避免$where 对性能的影响:

db.example.aggregate([

    // Use an index, which $where cannot to narrow down
    {$match: { "comments.by": "Abe" }},

    // De-normalize the Array
    {$unwind: "$comments"},

    // The order of the array is maintained, so just look for the $last by _id
    {$group: { _id: "$_id", comments: {$last: "$comment"} }},

    // Match only where that $last comment by `by.Abe`
    {$match: { "comments.by": "Abe" }},

    // Retain the original _id order
    {$sort: { _id: 1 }}

])

这应该围绕 $where 运行,因为我们能够首先缩小“Abe”评论的文档的范围。正如警告的那样,$where 将测试集合中的每个文档,并且永远不会使用索引,即使有要使用的索引。

当然,您也可以使用technique described here 维护原始文档,因此一切都可以像find() 一样工作。

对于任何发现这个的人来说都是值得深思的。


现代 MongoDB 版本更新

现代版本添加了 $redact 管道表达式以及 $arrayElemAt(后者是 3.2 的版本,所以这将是这里的最小版本),它们结合起来将允许逻辑表达式检查最后一个元素未处理 $unwind 阶段的数组:

db.example.aggregate([
  { "$match": { "comments.by": "Abe" }},
  { "$redact": {
    "$cond": {
      "if": {
        "$eq": [
          { "$arrayElemAt": [ "$comments.by", -1 ] },
          "Abe"
        ]
      },
      "then": "$$KEEP",
      "else": "$$PRUNE"
    }
  }}
])

这里的逻辑是通过比较$arrayElemAt 获取数组-1 的最后一个索引来完成的,该索引通过$map 转换为"by" 属性中的值的数组。这允许将单个值与所需参数 "Abe" 进行比较。

在 MongoDB 3.6 及更高版本中使用 $expr 甚至更现代一点:

db.example.find({
  "comments.by": "Abe",
  "$expr": {
    "$eq": [
      { "$arrayElemAt": [ "$comments.by", -1 ] },
      "Abe"
    ]
  }
})

这将是迄今为止匹配数组中最后一个元素的最高效的解决方案,并且实际上有望在大多数情况下取代 $where 的使用,尤其是在这里。

【讨论】:

    【解决方案2】:

    您无法通过这种架构设计一次性完成此操作。您可以存储长度并执行两个查询,或者将最后一条评论另外存储在另一个字段中:

    {
        '_id': 'foo';
        'comments' [
            { 'value': 'comment #1', 'by': 'Ford' },
            { 'value': 'comment #2', 'by': 'Arthur' },
            { 'value': 'comment #3', 'by': 'Zaphod' }
        ],
        'last_comment': {
            'value': 'comment #3', 'by': 'Zaphod'
        }
    }
    

    当然,您会复制一些数据,但至少您可以使用$set$pushcomment 设置这些数据。

    $comment = array(
        'value' => 'comment #3',
        'by' => 'Zaphod',
    );
    
    $collection->update(
        array( '_id' => 'foo' ),
        array(
            '$set' => array( 'last_comment' => $comment ),
            '$push' => array( 'comments' => $comment )
        )
    );
    

    现在很容易找到最后一个!

    【讨论】:

      【解决方案3】:

      您可以使用$where 运算符来做到这一点:

      db.example.find({ $where: 
          'this.comments.length && this.comments[this.comments.length-1].by === "Abe"' 
      })
      

      适用于$where 的常见缓慢性能警告。但是,您可以通过在查询中包含 "comments.by": "Abe" 来帮助解决此问题:

      db.example.find({
          "comments.by": "Abe",
          $where: 'this.comments.length && this.comments[this.comments.length-1].by === "Abe"' 
      })
      

      这样,$where 只需要针对包含 Abe 的 cmets 的文档进行评估,新术语就可以使用 "comments.by" 上的索引。

      【讨论】:

      • @fnaquira 我知道这是旧的,但在找到它时,我认为这值得一个(和更好的)答案。我已经提供了。
      【解决方案4】:

      我只是在做:

      db.products.find({'statusHistory.status':'AVAILABLE'},{'statusHistory': {$slice: -1}})
      

      这让我得到products,其中数组中的最后一个statusHistory 项包含属性status='AVAILABLE'

      【讨论】:

        【解决方案5】:

        我不知道为什么我上面的答案被删除了。我正在重新发布它。我很确定在不更改架构的情况下,您应该可以这样做。

        db.example.find({ "comments:{$slice:-1}.by" : "Abe" } 
        

        // ... 或

        db.example.find({ "comments.by" : "Abe" }
        

        这默认取数组中的最后一个元素。

        【讨论】:

        • 这两个都是错误的。 $slice 是投影算子,不是查询算子,第二个查询查看所有 cmets,而不仅仅是最后一个。
        • 不知何故,ashwin 的答案对我有所帮助,因为 $slice 确实可以在查询中使用,根据 MongoDB 文档:docs.mongodb.org/manual/reference/operator/projection/slice
        猜你喜欢
        • 1970-01-01
        • 2019-08-17
        • 1970-01-01
        • 2020-04-24
        • 1970-01-01
        • 2013-06-14
        • 1970-01-01
        • 1970-01-01
        • 2016-12-26
        相关资源
        最近更新 更多