I'm currently using aggregate operator to return documents that has an array of embedded (sub) documents. I want to rename the field name for the array and also rename fields names in the embedded documents of the array.
As an example, for projection I want to rename the array from "friends" to "buddies" and I also want to rename fields in the embedded document from "name" to "nickName". Can I do this within an aggregate operation and if so how?
Here's an example of the source document:
[
{
_id: ObjectID,
name: 'Matthew',
friends: [
{name: 'Slim', age: '32'},
{name: 'buba', age: '36'}
]
}
]
Here's what the results should look like:
[
{
_id: ObjectID,
name: 'Matthew',
buddies: [
{nickName: 'Chris', age: '32'},
{nickName: 'Jim', age: '36'}
]
}
]
Thanks for the help in advance.
There are a couple of approaches to this, but it largely depends on your MongoDB version. More recent versions from 2.6 and upwards support the $map
operator which you can use in $project
to do what you want:
db.friend.aggregate([
{ "$project": {
"name": 1,
"buddies": {
"$map": {
"input": "$friends",
"as": "el",
"in": {
"nickName": "$$el.name",
"age": "$$el.age"
}
}
}
}}
])
In prior versions you would use $unwind
to work with the array elements and re-construct via $group
:
db.collection.aggregate([
{ "$unwind": "$friends" },
{ "$group": {
"_id": "$_id",
"name": { "$first": "$name" },
"buddies": {
"$push": {
"nickName": "$friends.name",
"age": "$friends.age"
}
}
}}
])
With the first form being a little more efficient as you are not de-normalizing the array content and producing more documents in the pipeline to process.
I think I figured it out the following aggregate query. I would be interested in if there is a better way of doing this.
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"}
}}
]);
Below I describe the response of each operator ($unwind, $project, $group)
The $unwind would return something like this. It breaks out each Embedded Document into its own document. So where we had 1 document with 2 friends sub document, now we have two document. Both documents will have the same id and name fields but the friends sub document will be different:
{
"_id" : ObjectId("539c80d43cb9fe99d183a5f7"),
"name" : "Matthew",
"friends" : {"name" : "slim", "age" : "32"}
}, {
"_id" : : ObjectId("539c80d43cb9fe99d183a5f7"),,
"name" : "Matthew",
"friends" : {"name" : "buba", "age" : "36"}
}
The $project would return something like this. Here I'm creating a new Embedded Document called buddy and notice that I assigning "$friends.name" to "nickName":
{
"_id" : ObjectId("539c80d43cb9fe99d183a5f7"),
"name" : "Matthew",
"buddy" : {"nickName" : "slim","age" : "32"}
}, {
"_id" : : ObjectId("539c80d43cb9fe99d183a5f7"),,
"name" : "Matthew",
"friends" : {"nickName" : "buba","age" : "36"}
}
And then the $group would do something like this. Here I'm just group everything back together, but setting the array name to "buddies":
{
"_id" : ObjectId("539c80d43cb9fe99d183a5f7"),
"name" : "Matthew",
"buddies" : [
{"nickName" : "slim","age" : "32"},
{"nickName" : "buba","age" : "36"}
]
}
This seems to work, but if the friends array had duplicates, this process would remove the duplicates. This can be solved if there is a unique id for each friend.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With