DEV Community

Saurabh Dutta
Saurabh Dutta

Posted on

6 2

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.

👋 Kindness is contagious

Please leave your appreciation by commenting on this post!

It takes one minute and is worth it for your career.

Get started

Thank you!

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 !

A Workflow Copilot. Tailored to You.

Pieces.app image

Our desktop app, with its intelligent copilot, streamlines coding by generating snippets, extracting code from screenshots, and accelerating problem-solving.

Read the docs

👋 Kindness is contagious

Dive into an ocean of knowledge with this thought-provoking post, revered deeply within the supportive DEV Community. Developers of all levels are welcome to join and enhance our collective intelligence.

Saying a simple "thank you" can brighten someone's day. Share your gratitude in the comments below!

On DEV, sharing ideas eases our path and fortifies our community connections. Found this helpful? Sending a quick thanks to the author can be profoundly valued.

Okay