Using the aggregate pipeline, I am trying to project an embedded document to the root level WITHOUT projecting each field individually.
For example, I want to project name
from this collection to the root level:
[
{
_id: "1",
name: {
firstName: "John",
lastname: "Peters"
}
},
{
_id: "2",
name: {
firstName: "Mary",
lastname: "Jones"
}
}
]
This is what I am looking for:
[
{
firstName: "John",
lastname: "Peters"
},
{
firstName: "Mary",
lastname: "Jones"
}
]
Is there a way to do this without projecting each field individually? I don't want to have to do this:
db.collection.aggregate(
[
{
$project : {
"_id" : 0,
"firstName" : "$name.firstName",
"lastName" : "$name.lastName"
}
}
]
Using the aggregate pipeline, I am trying to project an embedded document to the root level WITHOUT projecting each field individually.
For example, I want to project name
from this collection to the root level:
[
{
_id: "1",
name: {
firstName: "John",
lastname: "Peters"
}
},
{
_id: "2",
name: {
firstName: "Mary",
lastname: "Jones"
}
}
]
This is what I am looking for:
[
{
firstName: "John",
lastname: "Peters"
},
{
firstName: "Mary",
lastname: "Jones"
}
]
Is there a way to do this without projecting each field individually? I don't want to have to do this:
db.collection.aggregate(
[
{
$project : {
"_id" : 0,
"firstName" : "$name.firstName",
"lastName" : "$name.lastName"
}
}
]
Share
Improve this question
edited Feb 3, 2016 at 18:25
gnerkus
12k7 gold badges53 silver badges74 bronze badges
asked Feb 3, 2016 at 17:11
Dave NewDave New
40k62 gold badges232 silver badges405 bronze badges
2
|
5 Answers
Reset to default 13MongoDB 3.4 has the new stage in aggregation pipeline - $replaceRoot
, which does exactly what was asked.
https://docs.mongodb.com/manual/reference/operator/aggregation/replaceRoot/
Here is the solution which uses JavaScript variable.
# Set Object for what to project
var projectWhat = {'_id' : 0};
# Fill Object with keys
Object.keys(db.coll.findOne().name).forEach(function(x){
projectWhat[x] = "$name." + x;
});
# Do Aggregate
db.coll.aggregate([{$project : projectWhat}])
And the output will be
{ "firstName" : "John", "lastname" : "Peters" }
{ "firstName" : "Mary", "lastname" : "Jones" }
Hope this helps.
You can use $replaceRoot
like this:
db.collection.aggregate(
[
{
$replaceRoot : {
newRoot: {"$name"}
}
}
]
)
Also if you have a field in the root document you want to retain you can use a $mergeObjects
to combine it with your embedded object:
db.collection.aggregate(
[
{
$replaceRoot : {
newRoot: {
$mergeObjects: [
{"_id": "$_id"},
"$name"
]
}
}
}
]
)
This may be achieved by using $set
to update all documents with the values in the name
sub-document:
db.collection.find({ "name": {"$exists": 1 } }).forEach(function(doc) {
var setName = {};
for ( var k in doc.name ) {
setName[k] = doc.name[k];
}
db.collection.update(
{ "_id": doc._id },
{ "$set": setName, "$unset": "name" }
);
})
While I'll recommend you use $project
because it would be more performant than this solution, I can understand why you wouldn't want to use $project
.
Starting Mongo 4.2
, the $replaceWith
aggregation operator can be used to replace a document by another (in our case by a sub-document):
// { _id: "1", name: { firstName: "John", lastname: "Peters" } }
// { _id: "2", name: { firstName: "Mary", lastname: "Jones" } }
db.collection.aggregate({ $replaceWith: "$name" })
// { firstName: "John", lastname: "Peters" }
// { firstName: "Mary", lastname: "Jones" }
$$ROOT
, you won't get it in the top most level, but all the variables can be contained under a single field. – BatScream Commented Feb 3, 2016 at 21:21