Reputation: 447
I have 2 collections in MongoDB :
Collection1 :
{
_id:1,
Field1: "Some info",
Field2: "Some other info",
Elements: [
{
id: 0,
Enabled: false
},
{
id: 1,
Enabled: false
},
{
id: 2,
Enabled: false
}
]
}
Collection2 :
{
Identifier: "identifier",
ElementsOverride: [
{
id: 0,
Enabled: true
},
{
id: 1,
Enabled: false
},
{
id: 2,
Enabled: true
}
]
}
What I would like to do is perform an operation which flattens "Element" collection and returns Collection1 with the flattened Element collection (basically the Enabled field from collection 2 overwrites the enabled field of Collection 1.
Is there a way to achieve this in Mongodb?
Adding more clarification for what the output should be like:
Essentially what I'm trying to do is merge the document identified by _id:1
in collection 1 (document1), with the document identified by Identifier: "identifier" in collection 2 (document 2) such that:
id: 0
) will overwrite the values in document1Required Output :
{
_id:1,
Identifier: "identifier",
Field1: "Some info",
Field2: "Some other info",
Elements: [
{
id: 0,
Enabled: true
},
{
id: 1,
Enabled: false
},
{
id: 2,
Enabled: true
}
]
}
Upvotes: 1
Views: 1072
Reputation: 14287
I am not sure how you want the output.
flattens "Element" collection
generally means the array Element
is unwound. Please correct my interpretation, in case I have misunderstood.
But, the following steps in Mongo Shell will get the result:
arr1 = db.c1.aggregate( [ { $unwind: "$Elements" }, { $sort: { "Elements.id": 1 } ] ).toArray()
arr2 = db.c2.aggregate( [ { $unwind: "$ElementsOverride" }, { $sort: { "ElementsOverride.id": 1 } ] ).toArray()
for (let i=0; i < arr1.length; i++) {
updated = Object.assign(arr1[i].Elements, arr2[i].ElementsOverride);
arr1[i].Elements = updated
}
The variable arr1
will have:
[
{
"_id" : 1,
"Field1" : "Some info",
"Field2" : "Some other info",
"Elements" : {
"id" : 0,
"Enabled" : true
}
},
{
"_id" : 1,
"Field1" : "Some info",
"Field2" : "Some other info",
"Elements" : {
"id" : 1,
"Enabled" : false
}
},
{
"_id" : 1,
"Field1" : "Some info",
"Field2" : "Some other info",
"Elements" : {
"id" : 2,
"Enabled" : true
}
}
]
Updated to reflect the required output:
arr2 = db.c2.aggregate( [
{ $unwind: "$ElementsOverride" },
{ $replaceRoot: { newRoot: "$ElementsOverride" } }
] ).toArray()
db.c1.aggregate( [
{ $unwind: "$Elements" },
{ $addFields: {
"Elements.Enabled": {
$filter: {
input: arr2,
cond: { $eq: [ "$$this.ElementsOverride.id", "$Elements.id" ] }
}
}
} },
{ $group: {
_id: "$_id",
doc: { $first: "$$ROOT"},
Identifier: { $first: "$Elements.Enabled.Identifier"},
Elements: { $push: { $arrayElemAt: [ "$Elements.Enabled", 0 ] } }
} },
{ $addFields: {
"doc.Elements": "$Elements.ElementsOverride",
"doc.Identifier": { $arrayElemAt: [ "$Identifier", 0 ] }
} },
{ $replaceRoot: { newRoot: "$doc" } }
] )
[ EDIT ADD 2 ]
Here is another way of merging the documents:
doc1 = db.c1.findOne()
arr2 = db.c2.aggregate( [ { $unwind: "$ElementsOverride" } ] ).toArray()
for (let e2 of arr2) {
for (i = 0; i < doc1.Elements.length; i++) {
if (doc1.Elements[i].id == e2.ElementsOverride.id) {
doc1.Elements[i].Enabled = e2.ElementsOverride.Enabled
doc1.Identifier = e2.Identifier
}
}
}
The output is the doc1
document.
Upvotes: 0
Reputation: 17915
You can try below query :
db.Collection1.aggregate([
/** get only one required doc from Collection1 */
{ $match: { _id: 1 } },
/** Join relative doc from Collection2 */
{
$lookup:
{
from: "Collection2",
pipeline: [
{
$match:
{
$expr:
{ $eq: ["$Identifier", "identifier"] }
}
}
],
as: "data"
}
},
/** As lookup will default to an array of objects getting an object out of array */
{ $unwind: '$data' },
/** Replacing existing elements field of Collection1 & adding Identifier field to existing doc */
{
$addFields: {
Identifier: '$data.Identifier', Elements:
{
$reduce: {
input: { $reverseArray: { $setUnion: ["$Elements", "$data.ElementsOverride"] } },
initialValue: [],
in: { $concatArrays: ["$$value", { $cond: [{ $in: ['$$this.id', '$$value.id'] }, [], ['$$this']] }] }
}
}
}
},
/** removing unnecessary field created at lookup stage */
{ $project: { data: 0 } }
])
Test : MongoDB-Playground
Upvotes: 1