How to use MongoDBs aggregate `$lookup` as `findOne()`

MongodbMongodb QueryAggregation Framework

Mongodb 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

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionFizzixView Question on Stackoverflow
Solution 1 - MongodbstyvaneView Answer on Stackoverflow
Solution 2 - MongodbRosView Answer on Stackoverflow
Solution 3 - Mongodband.hView Answer on Stackoverflow
Solution 4 - MongodbPrakash sView Answer on Stackoverflow