"MongoDB Aggregating Data with Nested Lookup and Grouping

I have 3 collections named User, Dispensary, and City. My desired result structure is as follows:

{
    _id: ,
    email: ,
    birthdate: ,
    type: ,
    dispensary: {
      _id: ,
      schedule: ,
      name: ,
      address: ,
      phone: ,
      user:,
      city: {
         name:,
        },
    },
  }

However, the city object is currently being fetched at the first level, but I want it to be a child of the dispensary collection.

This is the current pipeline I'm using:

User.aggregate
              ([
                {
                  $match: { "_id": id } 
                },
                {
                  $lookup:
                  {
                    from: Dispensary.collection.name,
                    localField: "dispensary",
                    foreignField: "_id",
                    as: "dispensary"
                  },
                },
                {"$unwind": {path:"$dispensary",preserveNullAndEmptyArrays: true} ,},
                {
                  $lookup:
                  {
                    from: City.collection.name,
                    localField: "dispensary.city",
                    foreignField: "_id",
                    as: "city"
                  },
                },
                {"$unwind": {path:"$city",preserveNullAndEmptyArrays: true}} ,
                {
                  "$group": {
                  _id: "$_id", 
                  email : { $first: '$email' },
                  birthdate : { $first: '$birthdate' },
                  type : { $first: '$type' },
                  dispensary: { $push:  "$dispensary" }, 
                  city: { $push:  "$city" }, 
                  },
                },
                {"$unwind": {path:"$dispensary",preserveNullAndEmptyArrays: true}} ,
                {"$unwind": {path:"$city",preserveNullAndEmptyArrays: true}} ,

              ], (aggErr, aggResult) => {
                (aggErr)  ? console.log(aggResult)
                          : console.log(aggResult)
              })

SCHEMAS:

const CitySchema = new Schema({
    name: { type: String, required: true, unique:true },
    zip: { type: String, required: true },
});

const DispensarySchema = new Schema({
    name: { type: String, required: true },
    address: { type: String, required: true },
    longitude: { type: String, required: true },
    latitude: { type: String, required: true },
    phone: { type: String, required: true },
    user: {type: mongoose.Schema.Types.ObjectId, ref: 'User'},
    schedule: [{type: mongoose.Schema.Types.ObjectId, ref: 'Schedule'}],
    city: {type: mongoose.Schema.Types.ObjectId, ref: 'City'},
})

const UserSchema = new Schema({
    name: { type: String, required: true },
    email: { type: String, required: true, unique: true },
    password: { type:String, required: true },
    birthdate: { type: Date, required: true },
    type: { type: String, enum: ['ADMIN','DISPENSARY','CUSTOMER'], required: true},
    verificationToken: { type: String, required: false },
    resetPasswordToken: { type: String, required: false },
    resetPasswordExpires: { type: String, required: false },
    isVerified: { type: Boolean, required: true },
    isActive: { type: Boolean, required: true },
    last_session: { type: Date },
    last_ip_session: { type:String },
    dispensary: {type: mongoose.Schema.Types.ObjectId, ref: 'Dispensary'},
},
{ timestamps: true }
)

Answer №1

In my opinion, it is not necessary to utilize $group in this scenario. Instead, you could simply include as: "dispensary.city" within your second occurrence of $lookup

[
  {
    "$match": {
      "_id": id
    }
  },
  {
    "$lookup": {
      from: Dispensary.collection.name,
      localField: "dispensary",
      foreignField: "_id",
      as: "dispensary"
    },
  },
  {
    "$unwind": {
      path: "$dispensary",
      preserveNullAndEmptyArrays: true
    },
  },
  {
    "$lookup": {
      from: City.collection.name,
      localField: "dispensary.city",
      foreignField: "_id",
      as: "dispensary.city" // update made here
    },
  },
  {
    "$unwind": {
      path: "$dispensary.city", // updated here
      preserveNullAndEmptyArrays: true
    }
  }
]

Answer №2

Utilize a different lookup method by employing the pipeline. This enables you to incorporate additional conditions or sub-queries within the lookup function. For more information, refer to: aggregate-lookup

    User.aggregate([
  {
    $match: { "_id": id } 
  },
  {
    $lookup: {
      from: Dispensary.collection.name,
      let: {dispensaryId: "$dispensary"},
      pipeline: [
        {
          $match: {
            $expr: {
               $eq: ["$_id", "$$dispensaryId"]
            }
          }
        },
        {
          $lookup:
          {
            from: City.collection.name,
            localField: "city",
            foreignField: "_id",
            as: "city"
          },
        },
        {
          $unwind: {
            path:"$city",
            preserveNullAndEmptyArrays: true
          }
        }
      ],
      as: "dispensary",
    },
  },
  {
     $unwind: {
       path:"$dispensary",
       preserveNullAndEmptyArrays: true
    }
   },
  {
    "$group": {
      _id: : {
        _id: "$_id", 
        email :  '$email' ,
        birthdate : '$birthdate' ,
        type :  '$type' 
        dispensary: "$dispensary"
     }
    }
  }
], (aggErr, aggResult) => {
  (aggErr)  ? console.log(aggResult)
            : console.log(aggResult)
})

Update: Pipeline NOTE: To reference variables in pipeline stages, use the "$$" syntax.

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

What is the process for incorporating multiple HTML pages into an Ionic hybrid mobile application?

Struggling to combine my sign in and sign up pages into a single index.html page. I'm currently working on a project involving Hybrid mobile app development. ...

Searching for items in a list using SwiftUI with data imported from a JSON

My current SearchView functionality involves filtering through an array called names. However, I now need to modify it to search through a JSON file. The JSON file has the following structure: struct UcmData: Codable, Identifiable { let id: Int le ...

What is the process for verifying a particular user in AngularJS?

I'm new to AngularJS and I'm a bit confused about the concepts of GET, PUT requests. I am currently working on an app where I display a list of users on one page, and on another page, I have a form with three buttons. My main focus is on the "Con ...

Error: JSON data abruptly terminated (Node.js)

As a beginner developer diving into the world of API's, I've encountered a recurring error in Node.js that seems to be causing crashes: SyntaxError: Unexpected end of JSON input at JSON.parse (<anonymous>) at IncomingMessage.<anonymo ...

Assistance with utilizing Regular Expressions to extract the className from a React/JSX component

For instance, I have <img className='class' src='somelink' /> and my goal is to extract only the className='class'. I have already attempted using / className='.+'[ |>] while going through files in search of ...

Validating JSON Data: Checking for the Existence of a Value in a Field

Looking for a way to validate a specific field called email in a JSON schema against 4 possible email values: ['test1', 'test2', 'test3', 'test4']. Additionally, some emails may have a \n new line separator that ...

Managing finances in React Application through a secure and advanced wallet system

I'm in the process of building my eCommerce site using React. I am looking to boost sales by incorporating a feature that allows customers to use eWallet from Ilium Software. This way, shoppers can save money for future buys and make withdrawals whene ...

Update to using res.get() instead of res.getHeader()

Looking for assistance with the code snippet below: const compress = require('compression'); export const handleCompression = compress({ filter: function (req: express.Request, res: express.Response) { return (/json|text|javascript|css|fo ...

Invoking a method in Vue.js from another component

I'm just starting out with VUE and I have a unique challenge for my project. I want to have a button on one page that triggers a function on another page. I know some may ask why not keep the button and function on the same page, but I am exploring ho ...

Utilizing the JSON File in Your HTML Webpage: A Comprehensive Guide

I'm currently trying to integrate the following JSON file into my HTML page. <html> <head> <title> testing get</title> <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"> ...

Tips for Avoiding Code Execution in Apollo Server Resolvers When User is Not Authenticated

Currently, I am experiencing a problem with my Express Apollo Server. My goal is to create an API with various resolvers, some of which do not require authentication. Everything runs smoothly when the user is authenticated, but issues arise when authentica ...

Executing Angular CLI tasks using a script file rather than directly from the CLI? Embracing the power of Localization and Internationalization

Our Angular 2 app is gearing up for internationalization/localization, and I am looking to create scripts that can handle tasks such as generating translation source files or building/serving the application with translations in a specific language. Inste ...

Tips on handling jsonp responses in CakePHP without using the .json extension

When working with CakePHP, the framework determines the data type to return by either checking for the presence of the .json extension in the URL or examining the Accepts HTTP header. It becomes a bit trickier when dealing with JSONP, as it doesn't a ...

How can I convert JSON data from an array to an array of objects using JavaScript?

My goal is to load data into an ag-grid. I have obtained the json data in the following format: {"my_data":{"labels":[1,2,3,...], "idx":["idx1", "idx2", ...]}} In order to pass it to the grid, I need to transform it to this format: {"my_data":[{"labels" ...

Dynamic anime-js hover animation flickering at high speeds

I have implemented the anime-js animation library to create an effect where a div grows when hovered over and shrinks when moving the mouse away. You can find the documentation for this library here: The animation works perfectly if you move slowly, allow ...

What are some potential problems that could arise when making a POST request for signing authentication in a MERN stack using JWT?

I'm currently in the process of developing a social media application using the MERN stack. To ensure the functionality of the backend API, I am utilizing POSTMAN. Here is an overview of the dependencies outlined in the package.json file: { "na ...

Utilizing Regular Expressions in JQuery

-Hello, I'm a newcomer to Jquery. I have a question about identifying this symbol [H] in JQuery. Currently, I am able to identify letters. $(document).ready(function () { $(":button#boton").click(function () { if ($(":text#texto").attr( ...

Button within ng-switch statement

Issue with switch button inside switch statement, only functioning correctly outside of the switch statement. See below for my code: <div ng-controller="LoginController as LC"> <div ng-switch on="view.name"> <div ng-switch-de ...

Sort the elements within the *ngFor loop according to the category upon clicking the button in Angular

Currently, I have a collection of items that I am iterating through using *ngFor. Above this list, there are category buttons available as shown in the HTML snippet below. My goal is to enable filtering of the list based on the category of the button click ...

Transforming a JSON object into a case class with a single field: a step-by-step guide

When dealing with Play 2.1, using reads to convert Json into objects is a common practice. However, it becomes tricky when working with case classes that have only one field. The usual method for multiple fields doesn't apply in this scenario as &apos ...