How to use MongoDBs aggregate `$lookup` as `findOne()`
MongodbMongodb QueryAggregation FrameworkMongodb Problem Overview
So as you all know, find()
returns an array of results, with findOne()
returning just a simply object.
With Angular, this makes a huge difference. Instead of going {{myresult[0].name}}
, I can simply just write {{myresult.name}}
.
I have found that the $lookup
method in the aggregate pipeline returns an array of results instead of just a single object.
For example, I have two colletions:
users
collection:
[{ "firstName": "John", "lastName": "Smith", "country": 123}, { "firstName": "Luke", "lastName": "Jones", "country": 321}]
countries
collection:
[{ "name": "Australia", "code": "AU", "_id": 123}, { "name": "New Zealand", "code": "NZ", "_id": 321}]
My aggregate $lookup
:
db.users.aggregate([{
$project: {
"fullName": {
$concat: ["$firstName", " ", "$lastName"]
},
"country": "$country"
}
}, {
$lookup: {
from: "countries",
localField: "country",
foreignField: "_id",
as: "country"
}
}])
The results from the query:
[{ "fullName": "John Smith", "country": [{ "name": "Australia", "code": "AU", "_id": 123 }]
}, {
"fullName": "Luke Jones",
"country": [{
"name": "New Zealand",
"code": "NZ",
"_id": 321
}]
}]
As you can see by the above results, each country
is an array instead of a single object like "country": {....}
.
How can I have my $lookup
return a single object instead of an array since it will only ever match a single document?
Mongodb Solutions
Solution 1 - Mongodb
You're almost there, you need to add another $project
stage to your pipeline and use the $arrayElemAt
to return the single element in the array.
db.users.aggregate(
[
{ "$project": {
"fullName": {
"$concat": [ "$firstName", " ", "$lastName"]
},
"country": "$country"
}},
{ "$lookup": {
"from": "countries",
"localField": "country",
"foreignField": "_id",
"as": "countryInfo"
}},
{ "$project": {
"fullName": 1,
"country": 1,
"countryInfo": { "$arrayElemAt": [ "$countryInfo", 0 ] }
}}
]
)
Solution 2 - Mongodb
You can also use "preserveNullAndEmptyArrays"
Like so:
db.users.aggregate(
[
{ "$project": {
"fullName": {
"$concat": [ "$firstName", " ", "$lastName"]
},
"country": "$country"
}},
{ "$lookup": {
"from": "countries",
"localField": "country",
"foreignField": "_id",
"as": "countryInfo"
}},
{"$unwind": {
"path": "$countryInfo",
"preserveNullAndEmptyArrays": true
}
},
]
)
Solution 3 - Mongodb
When you don't want to repeat all fields in project, just overwrite the field in question with $addFields:
db.users.aggregate([
{ "$project": {
"fullName": {
"$concat": [ "$firstName", " ", "$lastName"]
},
"country": "$country"
}},
{ "$lookup": {
"from": "countries",
"localField": "country",
"foreignField": "_id",
"as": "countryInfo"
}},
{ "$addFields": {
"countryInfo": {
"$arrayElemAt": [ "$countryInfo", 0 ]
}
}}
])
Solution 4 - Mongodb
db.users.aggregate([
{
$lookup: {
from: 'countries',
localField: 'country',
foreignField: '_id',
as: 'country'
}
},
{
$unwind: '$country'
}
]).pretty()
You can use this mongo query for getting the country object