简体   繁体   中英

Mongo Multiple Level Aggregate Lookup Group

I have 3 collections, User, Dispensary and City. I want my result to look like this:

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

However I am getting the city object out, in the first level, and I want to get it as child of the dispensary collection.

This is my 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 }
)

I think you don't need to use $group at all, you can use as: "dispensary.city" in your second $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" // modify here
    },
  },
  {
    "$unwind": {
      path: "$dispensary.city", // modify here
      preserveNullAndEmptyArrays: true
    }
  }
]

You can used another lookup method using pipeline , This allow you to make more condition/sub-query inside of the lookup function. see reference: 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.`

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM