For example, if I have a document like this
{
a: 1,
subdoc: {
b: 2,
c: 3
}
}
How can I convert it into a format like this? (without using project
)
{
a: 1,
b: 2,
c: 3
}
For example, if I have a document like this
{
a: 1,
subdoc: {
b: 2,
c: 3
}
}
How can I convert it into a format like this? (without using project
)
{
a: 1,
b: 2,
c: 3
}
You can use MongoDB projection i.e $project
aggregation framework pipeline operators as well. (recommended way). If you don't want to use project
check this link
db.collection.aggregation([{$project{ . . }}]);
Below is the example for your case:
db.collectionName.aggregate
([
{
$project: {
a: 1,
b: '$subdoc.b',
c: '$subdoc.c'
}
}
]);
Gives you the output as you expected i.e.
{
"a" : 1,
"b" : 2,
"c" : 3
}
You can use $replaceRoot
with a $addFields
stage as follows:
db.collection.aggregate([
{ "$addFields": { "subdoc.a": "$a" } },
{ "$replaceRoot": { "newRoot": "$subdoc" } }
])
We can do this with $replaceWith
an alias for $replaceRoot
and the $mergeObjects
operator.
let pipeline = [
{
"$replaceWith": {
"$mergeObjects": [ { "a": "$a" }, "$subdoc" ]
}
}
];
or
let pipeline = [
{
"$replaceRoot": {
"newRoot": {
"$mergeObjects": [{ "a": "$a" }, "$subdoc" ]
}
}
}
];
db.collection.aggregate(pipeline)
You can also use $$ROOT
in $mergeObjects
{ $replaceWith: {
$mergeObjects: [ "$$ROOT", "$subdoc" ]
} }
Starting Mongo 4.2
, the $replaceWith
aggregation operator can be used to replace a document by another (in our case by a sub-document) as syntaxic sugar for the $replaceRoot
mentioned by @chridam.
We can thus first include within the sub-document the root field to keep using the $set
operator (also introduced in Mongo 4.2
as an alias for $addFields
) and then replace the whole document by the sub-document using $replaceWith
:
// { a: 1, subdoc: { b: 2, c: 3 } }
db.collection.aggregate([
{ $set: { "subdoc.a": "$a" } },
{ $replaceWith: "$subdoc" }
])
// { b: 2, c: 3, a: 1 }
If you want to query nested array element and display it at root level You should use $unwind twice if you have 2 nested array
db.mortgageRequests.aggregate( [
{ $unwind: "$arrayLevel1s" },
{ $unwind: "$arrayLevel1s.arrayLevel2s" },
{ $match: { "arrayLevel1s.arrayLevel2s.documentid" : { $eq: "6034bceead4ed2ce3951f38e" } } },
{ $replaceRoot: { newRoot: "$arrayLevel1s.arrayLevel2s" } }
] )
I guess the easiest way is altering the result after it returns, using map
.
collection.mapFunction = function(el) {
el.b = el.subdoc.b;
el.c = el.subdoc.c
delete(el.subdoc);
return el;
}
...
var result = await collection.aggregate(...).toArray();
result = result.map(collection.mapFunction);
You can do this with the use of $set
and $unset
db.collection.update({},{ $set: { "b": 2, "c": 3 }, $unset: { "subdoc": 1 } })
Of course if there are lots of documents in the collection, then much as above you need to loop the collection documents in order to get the values. MongoDB has no way of referring to a value of a field in an update operation alone:
db.collection.find({ "subdoc": {"$exists": 1 } }).forEach(function(doc) {
var setDoc = {};
for ( var k in doc.subdoc ) {
setDoc[k] = doc.subdoc[k];
}
db.collection.update(
{ "_id": doc._id },
{ "$set": setDoc, "$unset": "subdoc" }
);
})
That also employs some safe usage of $exists
in order to make sure you are selecting documents where the "subdoc" key is actually there.