Stathis Ntonas
Stathis Ntonas

Reputation: 1262

Mongo $lookup nested array 3 levels deep using mongoose

I have this inside outsideServices model:

name: String,
salary: Number,
services: [
      {
        category: {
          ref: 'ServiceCategories',
          type: Schema.Types.ObjectId
        },
        types: [
          {
            ref: 'ServiceType',
            type: Schema.Types.ObjectId
          }
        ]
      }
    ]

I have this inside incidents model:

  outsideServices: {
      ref: 'OutsideService',
      type: Schema.Types.ObjectId
    }

so when requesting incidents from api the response would be like:

[
  {
    category: '5cf655135a1b72106777c238',
    types: ['5cf655a85a1b72106777c239']
  },
  {
    category: '5cf656525a1b72106777c23b',
    types: ['5cf656835a1b72106777c23c']
  }
];

How it's possible to lookup both categories and types ref and keep the structure above in my response?

Here's what I've tried so far but I can't get the desired results:

aggregate.lookup({
      from: 'outsideservices',
      let: { serviceId: '$outsideServices' },
      pipeline: [
        { $match: { $expr: { $eq: ['$_id', '$$serviceId'] } } },
        {
          $lookup: {
            from: 'servicecategories',
            let: { services: '$services.category' },
            pipeline: [{ $match: { $expr: { $in: ['$_id', '$$services'] } } }],
            as: 'category'
          }
        },
        {
          $lookup: {
            from: 'servicetypes',
            let: { services: '$services.types' },
            pipeline: [
              { $match: { $expr: { $in: ['$_id', '$$services'] } } }
            ],
            as: 'types'
          }
        }
      ],
      as: 'outsideService'
    });

The wanted result/outcome is like the response above but with populated documents and not ObjectIds

MongoDB version 4.0.10

Upvotes: 4

Views: 1901

Answers (2)

s7vr
s7vr

Reputation: 75934

You can try below aggregation. Use $unwind to deconstruct the array followed by $lookup to pull in the categories and types and finally $group to go back to original structure.

{
  "from":"outsideservices",
  "let":{"serviceId":"$outsideServices"},
  "pipeline":[
    {"$match":{"$expr":{"$eq":["$_id","$$serviceId"]}}},
    {"$unwind":"$services"},
    {"$lookup":{
    "from":"servicecategories",
    "localField":"services.category",
    "foreignField":"_id",
    "as":"services.category"
    }},
    {"$lookup":{
    "from":"servicetypes",
    "localField":"services.types",
    "foreignField":"_id",
    "as":"services.types"
    }},
    {"$group":{
    "_id":null,
    "services":{"$push":"$services"}
    }}
  ],
  "as":"outsideService"
}

Upvotes: 1

Akrion
Akrion

Reputation: 18515

So you are using mongoose schema but then going down to mongoDB for the aggregations.

You need to use populate and it's match stage from mongoose.

populate is what would tie in the refs for you etc.

Upvotes: 1

Related Questions