MongoDB聚合 $project 获取数组位置元素字段值

4

文件:

{
    "_id" : ObjectId("560dcd15491a065d6ab1085c"),
    "title" : "example title",
    "views" : 1,
    "messages" : [
        {
            "authorId" : ObjectId("560c24b853b558856ef193a3"),
            "authorName" : "Karl Morrison",
            "created" : ISODate("2015-10-02T00:17:25.119Z"),
            "message" : "example message"
        }
    ]
}

项目:

$project: {
    _id: 1,
    title: 1,
    views: 1,
    updated: '$messages[$messages.length-1].created' // <--- ReferenceError: $messages is not defined
}

我正在尝试从文档内部的数组中获取最后一个元素创建的值。我已经阅读了文档,但是这个特定任务似乎不太好实现。

我了解到这与点表示法有关。然而,它没有说明如何获取最后一个元素。

1个回答

5
您不能仅仅通过基本的.find()查询提取属性或更改结果,除了简单的顶级字段选择外,它根本不支持更高级的操作需要使用聚合框架。但是,即使没有使用.aggregate()$slice投影运算符也可以帮助您完成大部分工作:
db.collection.find({},{ "messages": { "$slice": -1 } })

你不能改变结构,但是最后一个数组元素可以轻松获取。
在 MongoDB 发布新版本之前(截至目前),聚合框架仍然需要展开数组才能获得“最后”一个元素,你可以使用 $unwind 来展开数组,并使用 $last 聚合器选择它。
db.collection.aggregate([
    { "$unwind": "$messages" },
    { "$group": {
        "_id": "$_id",
        "title": { "$last": "$title" },
        "views": { "$last": "$views" },
        "created": { "$last": "$messages.created" }
   }}
])

未来版本的聚合操作中将直接支持使用 $slice$arrayElemAt 处理此类情况。但您还需要使用 $let 设置一个变量来处理点符号字段:
    [
        { "$project": {
            "name": 1,
            "views": 1,
            "created": {
                "$let": {
                    "vars": {
                        "message": { 
                            "$arrayElemAt": [
                                { "$slice": [ "$messages", -1 ] },
                                0
                            ]
                        }
                    },
                    "in": "$$message.created"
                }
            }
        }}
    ]

@KarlMorrison 在这里需要明白的是,你的问题要求从数组中获取一个字段属性,并且从数组的最后一个成员那里获取。这就是答案告诉你该怎么做。如果你想保留拷贝,请事先复制数组字段,或者 $push 回内容到数组中。目前没有其他方法可以实现这一点。下一个版本的 MongoDB 将包括一个 $slice 运算符,可用于聚合框架。你只能问你所问的问题,在一个问题中不能问其他问题。 - Blakes Seven
没关系,伙计!我现在正在尝试实现它 :) 但是我遇到了一个错误:MongoError: exception: invalid operator '$arrayElemAt' - basickarl
哈哈,抱歉啊!我会保存代码以便稍后使用。我会检查 unwind 并立即回复你的! - basickarl
确实有效,谢谢 :) 使用其中一个与另一个相比是否有优势/劣势? - basickarl
2
如果您有其他聚合操作,那么您真的没有选择。自然地,处理$unwind并需要将文档$group回来是很大的开销。如果您有更广泛的细节,请提出其他问题 - Blakes Seven
显示剩余6条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接