'Merge two array objects together in Mongodb from $Lookup
I have two Mongodb 3.6 collections where one references another as an example data below where certs.$Entities.Id: entities._id
:
certs: {
_id: ObjectId('xxx'),
Category: "Certification",
Entities: [{Id: ObjectId('abc'), prev: true, Own_type: "Contact"}
{Id: ObjectId('def), prev: false, Own_Type: "Operator"}]
...bunch more fields
}
And the other:
entities: {_id: ObjectId('abc'),
FName: 'Bob',
LName: 'Smith',
...bunch more fields},
{_id: ObjectId('def'),
FName: 'Bill',
LName: 'Jones',
...bunch more fields}
I want to use a simple lookup to get the information from the entities into certs and would typically do that, but it produces two objects or overwrites cert.Entities
. This issue is that I need to be able to preserve the data within the cert.Entities
and merge that with entities
.
The desired result would be:
certs: {
_id: ObjectId('xxx'),
Category: "Certification",
Entities: [{Id: ObjectId('abc'),
prev: true,
Own_type: "Contact",
FName: 'Bob',
LName: 'Smith',
...bunch more fields}
{Id: ObjectId('def),
prev: false,
Own_Type: "Operator",
FName: 'Bill',
LName: 'Jones',
...bunch more fields
}]
...bunch more fields
}
I have accomplished this by doing the following aggregation, but it doesn't look right as I have to use the $first accumulator on several fields I need to have in the result.
certs.aggregate([{
$unwind: {
path: '$Entities',
preserveNullAndEmptyArrays: false
}
},
{
$lookup: {
from: 'entities',
localField: 'Entities.Id',
foreignField: '_id',
as: 'Ent'
}
},
{
$unwind: {
path: '$Ent',
preserveNullAndEmptyArrays: false
}
},
{
$addFields: {
Ent2: {
$mergeObjects: ['$Ent', '$Entities']
}
}
},
{
$group: {
_id: '$_id',
Entities: {
$push: '$Ent2'
},
Cert_ID: {
$first: '$Cert_ID'
},
Cert_Details: {
$first: '$Cert_Details'
},
Allocations: {
$first: '$Allocations'
},
Flowmeters: {
$first: '$Flowmeters'
},
Category: {
$first: '$Category'
},
Water_Sources: {
$first: '$Water_Sources'
},
Active: {
$first: '$Active'
},
County: {
$first: '$County'
},
Legal: {
$first: '$Legal'
},
GWMA: {
$first: '$GWMA'
},
Alias: {
$first: '$Alias'
},
Usage: {
$first: '$Usage'
}
}
}])
Can someone give me some advice to improve on this aggregate or give me an alternate pipeline?
Solution 1:[1]
you need to $map
and $filter
to replace joined entities after $lookup
db.certs.aggregate([
{$match : {_id : "xxx"}},
{$lookup : {from:"entities", localField:"Entities",foreignField:"_id", as: "joins"}},
{$addFields : {Entities:
{$map : {
input : "$Entities",
as : "e",
in : {$arrayElemAt :[{
$filter : {
input : "$joins",
as : "j",
cond : {$eq :["$$e", "$$j._id"]}
}},0]}
}}
}},
{$project : {joins:0}}
]).pretty()
EDIT-1
to preserve fields from both the array elements
db.certs.aggregate([
{$match : {_id : "xxx"}},
{$lookup : {from:"entities", localField:"Entities._id",foreignField:"_id", as: "joins"}},
{$addFields : {Entities:
{$map : {
input : "$Entities",
as : "e",
in : {$mergeObjects: [
"$$e",
{$arrayElemAt :[{$filter : {input : "$joins",as : "j", cond : {$eq :["$$e", "$$j._id"]}}},0]}
]
}}}
}},
{$project : {joins:0}}
]).pretty()
Solution 2:[2]
The example on top worked for me. It solved the problem!
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | |
Solution 2 | iglizar |