I have a piece of code to join collection A (sample) to collection B (locators). I've tried the $unwind
, $group
and $push
syntax the only problem is that I can't return the field locator
and record
.
data = db.sample.aggregate([
{'$lookup': {
'from': 'locators',
'localField': "locator",
'foreignField': "_id",
'as': "metalocator"}}])
print(list(data))
Which returns
[
{
'_id': '599A65E1A80541BA',
'locator': 'ABC',
'record': 'Nicaragua',
'metalocator': [{'_id': 'ABC', 'group': 'Location', 'section': 'Geo', 'cabinet': 'Country', 'record': 'string', 'status': 'integer'}]
},
{
'_id': '428E970995AE8C76',
'locator': 'CDE',
'record': 'Nigeria',
'metalocator': [{'_id': 'CDE', 'group': 'Location', 'section': 'Geo', 'cabinet': 'Country', 'record': 'string', 'status': 'integer'}]
}
]
data = db.sample.aggregate([
{"$lookup": {"from": "locators",
"localField": "locator",
"foreignField": "_id",
"as": "metalocator"}},
{"$unwind": '$metalocator'},
{"$group": {"_id": "$_id",
"metalocator": {"$push": {
"section": "$metalocator.section",
"cabinet": "$metalocator.cabinet"}}}}
])
print(list(data))
Which returns:
[
{
'_id': '1835853D2982AAEF',
'metalocator': [{'section': 'Geo', 'cabinet': 'Country'}]
},
{
'_id': '428E970995AE8C76',
'metalocator': [{'section': 'Geo', 'cabinet': 'Country'}]
}
]
[
{
'_id': '1835853D2982AAEF',
'locator': 'ABC',
'record': 'Nicaragua',
'metalocator': [{'section': 'Geo', 'cabinet': 'Country'}]
},
{
'_id': '428E970995AE8C76',
'locator': 'CDE',
'record': 'Nigeria',
'metalocator': [{'section': 'Geo', 'cabinet': 'Country'}]
}
]
You want $map
:
db.sample.aggregate([
{'$lookup': {
'from': 'locators',
'localField': "locator",
'foreignField': "_id",
'as': "metalocator"
}},
{ '$addFields': {
'metalocator': {
'$map': {
'input': '$metalocator',
'in': {
'section': '$$this.section',
'cabinet': '$$this.cabinet'
}
}
}
}}
])
That is what you use to "re-map" the contents of an array, which is really what you are asking. It's usage is much the same as the operator of much the same name in python as well as many other languages.
If you have MongoDB 3.6, you alternately can use a different $lookup
syntax where you can actually "choose" which fields to return from there:
db.sample.aggregate([
{'$lookup': {
'from': 'locators',
'let': { 'locator': '$locator' },
'pipeline': [
{ '$match': {
'$expr': { '$eq': [ '$_id', '$$locator' ] }
}},
{ '$project': { 'section': 1, 'cabinet': 1, '_id': 0 } }
],
'as': "metalocator"
}}
])
That would actually be more efficient where you can do it, since the data is simply not even returned into the target array and you don't need to "re-map" the array in order to discard the other fields.
For the record, what you "missed" was the $first
operator:
db.sample.aggregate([
{ "$lookup": {
"from": "locators",
"localField": "locator",
"foreignField": "_id",
"as": "metalocator"
}},
{ "$unwind": '$metalocator'},
{ "$group": {
"_id": "$_id",
"locator": { "$first": "$locator" },
"record": { "$first": "$record" },
"metalocator": {
"$push": {
"section": "$metalocator.section",
"cabinet": "$metalocator.cabinet"
}
}
}}
])
But using $unwind
and $group
is not necessary here as the other methods shown above are far more efficient.
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