Search code examples
node.jsmongodbmongoosenext.js

ref and Populate in mongoose to a specific field


I want to be able to create a product and in the package I of that product I wan to be able to put an id of one of the skus that I have in my storage model and when I GET the product I want it to show the info about that sku I have search for an answer for this for days and I can not find a solution

const ProductSchema = new mongoose.Schema({
    name:{type:String, required:true, unique:true},
    stores:[{
        name:String,
        package:[{
            name:String,
            sku: // i want here to pass one of the skus id and when i populate the product i want it to return that sku info
        }]
    }],

},{timestamps : true})
export default mongoose.models.Product || mongoose.model("Product", ProductSchema);


const storageSchema = new mongoose.Schema({
    name:String,
    region:[{
        name:String,
        skus:[{
            name:String,
            variant:[{
                name:String,
            }]
        }]
}]
},{timestamps: true})
export default mongoose.models.Storage || mongoose.model("Storage", storageSchema);

//example of the resposnse i want 
{
    "_id": "6402ec724db3304dfc134069",
    "name": "product Name",
    "stores": [
        {
            "name": "Store Name",
            "package": [
                {
                    "name": "package name",
                    "sku" : [{
                        "name":"sku name",
                        "variant":[{
                            "name":"variant name"
                        },
                        {
                            "name":"variant name"
                        },
                        {
                            "name":"variant name"
                        },
                        {
                            "name":"variant name"
                        },]
                    }]
                    "_id": "6402ee69c737a9d40948d522"
                }
            ],
            "_id": "6402ede4c737a9d40948d4f9"
        }
    ],
}

//example of the storage data 
{
    "_id": "63f43de1b8604ed5c5c99f02",
    "StorageName": "Storage Name",
    "region": [
        {
            "name": "region name",
            "_id": "63f43e9eb8604ed5c5c99f48",
            "skus": [
                {
                    "name": "first sku",
                    "variant":[{
                        "name":"variant name"
                    },
                    {
                        "name":"variant name"
                    },
                    {
                        "name":"variant name"
                    },
                    {
                        "name":"variant name"
                    },],
                    "_id": "63f4c734b8604ed5c5c99f99"
                },
                {
                    "name": "sec sku",
                    "variant":[{
                        "name":"variant name"
                    },
                    {
                        "name":"variant name"
                    },
                    {
                        "name":"variant name"
                    },
                    {
                        "name":"variant name"
                    },],
                    "_id": "63f4c73ab8604ed5c5c99fa7"
                },
                {
                    "name": "third sku",
                    "_id": "6402baac4db3304dfc133db7"
                }
            ]
        },
        {
            "name": "region name",
            "_id": "63f43ea9b8604ed5c5c99f5b",
            "skus": [
                {
                    "name": "first sku",
                    "_id": "63f4db42cfb1ebe9fafc1ff0"
                },
                {
                    "name": "sec sku",
                    "_id": "63f4db4dcfb1ebe9fafc200a"
                },
                {
                    "name": "third sku",
                    "_id": "63f4db5ccfb1ebe9fafc2027"
                }
            ]
        }
    ],
}

Solution

  • following the model storegas, you dont need to store name in fields stores and package of model Product because we can get them from storage

    it would be like :

    //product model
    {
        name:{type:String, required:true, unique:true},
        stores:[
            {
                storeId:String,
                package:[{
                    skuId: string
                }]
            }
        ]
    }
    

    except you have another purpose, we can do it with lookup and pipeline

    and i believe you also have collections for region and skus, you should post them as well because it would be easier to join with those collections rather than with only product and storage

    Noted:

    • do filter as much as you can in $match for better performance
    • if your data have a large size of the documents, use pagination because we are doing recurring query

    to approach your requirements to search for sku of product, it would be like below

    db.products.aggregate([
      {
        "$match": {
          "_id": "6402ec724db3304dfc134069",
    
          // i suggest to filter with storege id
          //"stores._id": "63f43de1b8604ed5c5c99f02"
          
        }
      },
      {
        "$unwind": "$stores"
      },
      {
        $lookup: {
          from: "storages",
          let: {
            storeID: {
              "$toString": "$stores._id"
            }
          },
          pipeline: [
            {
              $match: {
                $expr: {
                  $eq: [
                    "$_id",
                    "$$storeID"
                  ]
                }
              }
            },
            {
              $project: {
                "name": "$StorageName",
                "package": {
                  "$filter": {
                    "input": {
                      "$map": {
                        "input": "$region",
                        "as": "region",
                        "in": {
                          "_id": "$$region._id",
                          "name": "$$region.name",
                          "sku": {
                            "$filter": {
                              "input": "$$region.skus",
                              "as": "sku",
                              "cond": {
                                $eq: [
                                  "$$sku._id",
                                  "63f4c734b8604ed5c5c99f99", //filter the id of sku
                                  
                                ]
                              }
                            }
                          }
                        }
                      }
                    },
                    "as": "region",
                    "cond": {
                      "$ne": [
                        "$$region.sku",
                        []
                      ]
                    }
                  }
                },
                
              },
              
            }
          ],
          as: "stores"
        }
      },
    ])
    

    try it in MONGO-PLAYGROUND