How can you combine the output of a $lookup in Mongodb and transform it into a list of objects with corresponding keys?

I possess a classroom document that includes 'modules', the overall structure of the document is as follows:

{
    "_id": "628a7ea21e2a666d7872efbf",
    "name": "Test Class",
    "owner": "60763491b98b9e186ef33137",
    "schoolId": "607dff27c712219af1e65d83",
    "description": "This is a test class.",
    "roster": [],
    "modules": [
        {
            "name": "Test Module 1",
            "id": "62a7082d0bf84c43fdfe95ff",
            "isPublished": false
        },
        {
            "name": "Test Module 2",
            "id": "62a72d6378ce044dca32e1a2",
            "isPublished": false
        }
    ]
}

Furthermore, I own assignment documents like this:

{
    "classroomId": "628a7ea21e2a666d7872efbf",
    "moduleId": "62a7082d0bf84c43fdfe95ff",
    "name": "Assignment 1",
    "description": "Test description",
    "created": 1655120822055,
    "reading": null,
    "questions": [],
    "isPublished": true,
    "_id": "62a723b6683ffc4b11940c7b"
}

The query revolves around how to perform aggregation in such a manner that facilitates grouping the assignment records by moduleId when looking up assignments for the classroom. The desired output would be structured as below:

{
    "_id": "628a7ea21e2a666d7872efbf",
    "name": "Test Class",
    "owner": "60763491b98b9e186ef33137",
    "schoolId": "607dff27c712219af1e65d83",
    "description": "This is a test class.",
    "roster": [],
    "modules": [
        {
            "name": "Test Module 1",
            "id": "62a7082d0bf84c43fdfe95ff",
            "isPublished": false,
            "assignments": [
                {
                    "_id": "62a708ab0bf84c43fdfe9600",
                    "classroomId": "628a7ea21e2a666d7872efbf",
                    "moduleId": "62a7082d0bf84c43fdfe95ff",
                    "name": "Assignment 1",
                    "description": "Test description",
                    "created": 1655113899629,
                    "due": null,
                    "settings": null,
                    "reading": null,
                    "questions": [],
                    "isPublished": true
                },
                {
                    "_id": "62a723b6683ffc4b11940c7b",
                    "classroomId": "628a7ea21e2a666d7872efbf",
                    "moduleId": "62a7082d0bf84c43fdfe95ff",
                    "name": "Assignment 1",
                    "description": "Test description",
                    "created": 1655120822055,
                    "due": null,
                    "settings": null,
                    "reading": null,
                    "questions": [],
                    "isPublished": true
                }
            ]

        },
        {
            "name": "Test Module 2",
            "id": "62a72d6378ce044dca32e1a2",
            "isPublished": false,
            "assignments": [

            ]
        }
    ]
}

Currently only a basic lookup has been implemented resulting in separate assignments field without grouping.

lookup = [
    {
        $lookup: {
            from: "assignments",
            localField: "modules.id",
            foreignField: "moduleId",
            as: "assignments"
        }
    }
];

Answer №1

  1. $lookup

  2. $set - Setting the modules field.

    2.1. $map - Iterating through the modules array and generating a new array.

    2.1.1. $mergeObject - Merging the current iterate module document with the document containing the assignments array from the result 2.1.1.1.

    2.1.1.1. $filter - Filtering the assignments array by matching the moduleId.

  3. $unset - Removing the assignments field.

db.classroom.aggregate([
  {
    $lookup: {
      from: "assignments",
      localField: "modules.id",
      foreignField: "moduleId",
      as: "assignments"
    }
  },
  {
    $set: {
      modules: {
        $map: {
          input: "$modules",
          as: "module",
          in: {
            $mergeObjects: [
              "$$module",
              {
                "assignments": {
                  $filter: {
                    input: "$assignments",
                    as: "asgn",
                    cond: {
                      $eq: [
                        "$$module.id",
                        "$$asgn.moduleId"
                      ]
                    }
                  }
                }
              }
            ]
          }
        }
      }
    }
  },
  {
    $unset: "assignments"
  }
])

Try out this Mongo Playground Example

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

If the body's height is adjusted, the page will automatically scroll to the top

I'm facing an issue where a button on my HTML page triggers a popup box to open (which covers the background). To prevent the background from scrolling, I am using the following CSS: windowHeight = $(window).height(); $("body").css({ "height": wi ...

How can I transition smoothly between two colors in three.js?

I am working with a three.js object that is currently a specific color and I would like to smoothly animate it to a different color. I want the animation to only display a direct transition between the initial and final colors without following a linear pa ...

Trouble with Mocha async hooks execution?

I keep encountering the issue of receiving 'undefined' for the page in this setup. It appears that none of Mocha's hooks are being executed. I've attempted adding async to the describe at the top level, used done statements, and even tr ...

Deleting a product category along with all its products: Understanding the basics of CRUD操作

I am looking for a way to delete a product category along with all the products within it. The Product model has a reference to the category as an object. Is there a straightforward method or a commonly used technique for this? I attempted to use removeAl ...

The switch case functionality refuses to change when I interact with the user interface

Can someone please help me troubleshoot? I'm not receiving any errors in the Chrome console. HTML <div class="wrapper"> <i id="repeat" class="fas fa-stop-circle"></i> </div> Javascript const wrap ...

Expressjs - Error: Headers already sent to the client and cannot be set

After testing various solutions from others, I am still unable to resolve this error. My objective is to iterate through each item in the array sourced below: novel.ts export let indexList = (req: Request, res: Response) => { novel.getAllDocuments ...

Interactive Vue.js canvases that adapt and respond to various

I am currently working on adjusting my canvas to fit within its container in a Vue component. When I call resizeCanvas() in the mounted hook, I notice that the container's height and width are both 0. How can I create a canvas that dynamically fits it ...

Is it possible for me to pass a reference to a specific object's instance function?

Can JavaScript allow you to pass a function reference to a specific object's function, similar to what can be done in Java? Let's take a look at this code snippet: _.every(aS, function (value) { return exp.test(value); }); What if we want ...

I attempted to launch a Node.js application on Heroku, but I'm having trouble sending requests from external routers

After deploying my NodeJS app on Heroku, I encountered a problem. I am able to make a GET request from "/" route that was created in app.js with app.get("/", ...), however I am facing issues with making requests from external routers. The requests seem t ...

Utilizing MongoDb in tandem with FastAPI

Exploring the capabilities of FastAPI has led me to the decision to integrate it with a MongoDB database. In this endeavor, I find myself debating between using motor, an async ODM, or mongoengine. I came across an example of NoSQL implementation here, w ...

Calling this.$refs.upload.submit() is not providing the expected response from Element-UI

Currently working with element-ui and attempting to upload a file using the following code: this.$refs.upload.submit(); Is there a way to retrieve the response from this.$refs.upload.submit();? I have attempted the following: .then(response => { t ...

Obtaining the Div ID After Clicking on a Link

I'm attempting to fade out a box once the X in that box is clicked. I haven't been able to make it work even after searching on Google. I managed to get it working when clicking the div itself using $(this), but my goal is for it to work when the ...

Combining various functions into a single button

I am currently working on creating a full-screen menu that functions like a modal. Everything seems to be working fine, except for the fadeOut animation. Can someone please help me understand what is causing issues with my scripts/codes? I want the content ...

Having issues with the sidebar malfunctioning and unsure of the cause

<html> <head> <title></title> <link rel="stylesheet" type="text/css" href="style.css" /> </head> <body> I've been working on creating a sidebar for my website, but it's not functioning as expect ...

Multer middleware for uploading multiple files is functioning perfectly on a local server, but on the live server, it gets stuck in a pending state or hangs

Encountering an issue with the multer middleware. I have set up the middleware and specified the static express files to be stored in the uploads folder. Included in the Index.js File: import emailroutes from './routes/emailroutes.js' app.use(e ...

Implementing a callback function following the completion of file reading in Phonegap

I have been facing this issue for quite some time now and I need assistance in finding a solution: When it comes to developing an android app, I rely on the phonegap framework. There is an async function called readFromFile() that reads a json file store ...

Is it possible to use the Stop Button on the HTML5 Audio Tag to halt a live MP3 stream

Is there a way to add a stop button as well? Currently, I have play and pause buttons, but the stop function doesn't truly clear the music buffer in the browser; it just stops playback without resetting to the beginning. This is fine for MP3 files but ...

Tips for displaying average progress using a progress bar

I'm in the process of creating a website and I would like to incorporate a progress bar that reflects the average of all the input numbers obtained from an HTML form. Although I have some experience with JavaScript, I am currently in need of the Java ...

issue with splice function

I have a JavaScript function that is supposed to collect all input values from text boxes and store them in an array. However, I want to remove any input value with the type "button" from the array before proceeding. Here is the code snippet: <!-- lang ...

Ajax: If a GET request is made, the page will automatically refresh

EcmaScript 6, jQuery 3.1.0 Fiddle: https://jsfiddle.net/Kifsif/k6gw1gnw/10/ Heroku: A plus sign can be found on the page which is supposed to add a form just above it when clicked. The issue arises when adding a form via AJAX as it ends up being displa ...