MongoDB聚合查询-重命名嵌入文档中返回的字段

7
我目前正在使用聚合操作符来返回包含嵌套(子)文档数组的文档。我想重命名数组的字段名称,并且还想重命名数组中嵌套文档的字段名称。
例如,对于投影,我想将数组从“friends”重命名为“buddies”,并且我还想将嵌套文档中的字段从“name”重命名为“nickName”。我可以在聚合操作中完成这个操作吗?如果可以,如何?
下面是源文档的示例:
[
    {
        _id: ObjectID,
        name: 'Matthew',
        friends: [
            {name: 'Slim', age: '32'},
            {name: 'buba', age: '36'}
        ]
    }
]

这是结果应该看起来的样子:
[
    {
        _id: ObjectID,
        name: 'Matthew',
        buddies: [
            {nickName: 'Chris', age: '32'},
            {nickName: 'Jim', age: '36'}
        ]
    }
]

提前感谢您的帮助。
2个回答

10

有几种方法可以实现这个,但主要取决于您的MongoDB版本。从2.6及以上的最新版本支持$map运算符,您可以在$project中使用它来完成您想要的操作:

db.friend.aggregate([
    { "$project": {
        "name": 1,
        "buddies": {
            "$map": {
                "input": "$friends",
                "as": "el",
                "in": {
                    "nickName": "$$el.name",
                    "age": "$$el.age"
                }
            }
        }
    }}
])

在之前的版本中,您可以使用$unwind操作符来处理数组元素,并通过$group操作符重新构建:

db.collection.aggregate([
    { "$unwind": "$friends" },
    { "$group": {
        "_id": "$_id",
        "name": { "$first": "$name" },
        "buddies": {
            "$push": {
                "nickName": "$friends.name",
                "age": "$friends.age"
            }
        }
    }}
])

第一种方法更高效,因为它不需要对数组内容进行非正常化处理并生成更多需要处理的文档。


我提交了答案后才注意到你的回答。我目前使用的是2.4版本,但真的很喜欢2.6的$map运算符。你用$unwind和$group的答案比我的更简单,因为它不包括$project。为了确认给其他人,我刚刚尝试了你的查询,使用$unwind和$group可以正常工作。 - Mike Barlow - BarDev
1
@BarDev 更快,因为您实质上是对文档进行了另一次遍历。此外请注意,当您使用 $project 对字段进行“重新命名”并包含现有字段时,不管您指定的顺序如何,现有字段始终会首先列出。这是管道执行的一般优化,通过“复制”现有内容来实现的。然而,$group 将保留您指定的顺序。 - Neil Lunn
非常酷,也很有用。就 MongoDB 而言,我肯定是一个新手,还有很多要学习的。感谢提供额外信息;这使事物运作方式更加清晰。 - Mike Barlow - BarDev

1

我想我已经弄清楚了以下聚合查询。如果有更好的方法,请告诉我。

db.friends.aggregate([
    { $unwind: '$friends'},
    { $project: {
        _id: 1,
        name: 1,
        buddy: {
            nickName: '$friends.name',
            age: '$friends.age',
        }
    }},
    { $group: {        
        _id: '$_id',
        name: {'$first': '$name'},
        buddies: {"$push": "$buddy"}
    }}
]);

以下是每个操作符($unwind,$project,$group)的响应描述。 $unwind将返回这样的结果。它会将每个嵌套文档拆分成自己的文档。因此,我们原本有一个包含2个朋友子文档的文档,现在变成了两个文档。这两个文档都将具有相同的id和name字段,但friends子文档将不同:
{
    "_id" : ObjectId("539c80d43cb9fe99d183a5f7"),
    "name" : "Matthew",
    "friends" : {"name" : "slim", "age" : "32"}
}, {
    "_id" : : ObjectId("539c80d43cb9fe99d183a5f7"),,
    "name" : "Matthew",
    "friends" : {"name" : "buba", "age" : "36"}
}
$project会返回类似于这样的内容。这里我正在创建一个名为buddy的新嵌入式文档,并注意将"$friends.name"分配给"nickName":
{
    "_id" : ObjectId("539c80d43cb9fe99d183a5f7"),
    "name" : "Matthew",
    "buddy" : {"nickName" : "slim","age" : "32"}
}, {
    "_id" : : ObjectId("539c80d43cb9fe99d183a5f7"),,
    "name" : "Matthew",
    "friends" : {"nickName" : "buba","age" : "36"}
}

然后 $group 会做类似这样的事情。这里我只是将所有内容重新分组,但将数组名称设置为“buddies”:

{
    "_id" : ObjectId("539c80d43cb9fe99d183a5f7"),
    "name" : "Matthew",
    "buddies" : [
        {"nickName" : "slim","age" : "32"},
        {"nickName" : "buba","age" : "36"}
    ]
}

这似乎可行,但如果朋友数组中有重复项,则此过程将删除重复项。如果每个朋友都有唯一的ID,则可以解决此问题。

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