DEV Community

Saurabh Dutta
Saurabh Dutta

Posted on

MongoDB: Using aggregation pipeline to extract DBref using $lookup operator

Problem Statement:

While using aggregation pipeline on collection having DBRef to other collection, using $lookup does not resolve to value due to $id field in the reference.

Imagine we have a collection called posts with the following structure.

{ "_id": ObjectId("5126bbf64aed4daf9e2ab771"), "title": "Once in a Lifetime", "content": "Morbi quis tortor id nulla ultrices aliquet.", "author": { "$ref": "users", "$id": ObjectId("5126bc054aed4daf9e2ab772") } } 

The DBRef in this example points to a document in the users collection that has ObjectId("5126bc054aed4daf9e2ab772") in its _id field.

To resolve,author field we need to use $lookup stage, which has syntax as given below but will not work, due to the presence of $ in the field name.

db.posts.aggregate([ { $lookup: { from: "users", localField: "author['$id']", // or author.$id foreignField: "_id", as: "author" } } ]) 

Solution:

One of the workaround I found to resolve the DBRefs with $lookup operator, was with using $objectToArray, $arrayElemAt and $addFields operator.

Idea behind this is to somehow, extract the $id value in author field and then use $lookup operator to get the desired result.

db.posts.aggregate([ { $addFields: { "author": { $arrayElemAt: [{ $objectToArray: "author" }, 1] } } }, { $addFields: { "author": "author.v" } }, { $lookup: { from: "users", localField: "author", foreignField: "_id", as: "author" } }, { $addFields: { "author": { $arrayElemAt: ["$author", 0] } } } ]) 

will return result:

[ { "_id": ObjectId("5126bbf64aed4daf9e2ab771"), "title": "Once in a Lifetime", "content": "Morbi quis tortor id nulla ultrices aliquet.", "author": { "_id": ObjectId("5126bc054aed4daf9e2ab772"), "name": "Uta Charman", "avatar": "https://robohash.org/porroasperioreshic.png?size=50x50&set=set1", "aboutText": "Vivamus tortor." } } ] 

Explanation:

Stage 1:

{ $addFields: { "author": { $arrayElemAt: [{ $objectToArray: "author" }, 1] } } } 

Converts author object from DBref reference to array of key value pairs, and extracts key-value pair of $id in author field

{ ... author: { k: $id, v: ObjectId("5126bc054aed4daf9e2ab772") } } 

Stage 2:

 { $addFields: { "author": "author.v" } } 

Maps value of $id key to author field.

{ ... author: ObjectId("5126bc054aed4daf9e2ab772") } 

Stage 3:

 { $lookup: { from: "users", localField: "author", foreignField: "_id", as: "author" } } 

Using $lookup operator to fetch for local field author from users collection

{ ... author: [ { "_id": ObjectId("5126bc054aed4daf9e2ab772"), "name": "Uta Charman", "avatar": "https://robohash.org/porroasperioreshic.png?size=50x50&set=set1", "aboutText": "Vivamus tortor." } ] } 

Stage 4:

{ $addFields: { "author": { $arrayElemAt: ["$author", 0] } } } 

Maps author array type field to object field.

{ ... author: { "_id": ObjectId("5126bc054aed4daf9e2ab772"), "name": "Uta Charman", "avatar": "https://robohash.org/porroasperioreshic.png?size=50x50&set=set1", "aboutText": "Vivamus tortor." } } 

Note:

As per Mongo DB's Documentation Database References — MongoDB Manual

Unless you have a compelling reason to use DBRefs, use manual references instead.

Top comments (4)

Collapse
 
faizaans profile image
Faizaan Shaikh

Very helpful article!

There is a missing $ from the snippet below. It took me hours to figure it out.
{
$addFields: {
"author": {
$arrayElemAt: [{ $objectToArray: "$author" }, 1]
}
}
},
{
$addFields: {
"author": "$author.v"
}
}

Collapse
 
djnitehawk profile image
Dĵ ΝιΓΞΗΛψΚ

yeah i avoid dbref like the plague 🤪

Collapse
 
sllayan profile image
Shayan

Useful! You Save my time
Thank you

Collapse
 
sriramsridharanvr profile image
Sriram Sridharan

Very helpful. The explanation was spot on and brilliant. Thanks !