Search code examples
mongodbmongooseaggregation-frameworknosql-aggregation

How to lookup a specific field of every object of an array using mongodb aggregation


I'm trying to lookup every rentalContractId from rentalcontractdetails table. The product document looks something like this:

{
  "productPrice": [
    {
      "rentalContractId": ObjectId("64018dfc8a7d37f378d6c427"),
      "price": 20
    },
    {
      "rentalContractId": ObjectId("64018dfc8a7d37f378d6c426"),
      "price": 10
    }
  ]
}

I just need to lookup rentalContractId. So was doing the below query in aggregation to get that output,

db.products.aggregate([
{
      $lookup: {
        from: "rentalcontractdetails",
        localField: "productPrice.rentalContractId",
        foreignField: "_id",
        as: "rentalContract",
      },
    }, { $set: {
      "productPrice.rentalContract": "$rentalContract"
    }}])

but this is producing output like this. "productPrice":

[
                    {
                        "rentalContractId": "64018dfc8a7d37f378d6c427",
                        "price": 20,
                        "rentalContract": [
                            {
                                "_id": "64018dfc8a7d37f378d6c427",
                                "contractType": "8 - Days Rental",
                                "contractDaysInNumber": 8,
                                "createdAt": "2023-03-03T06:04:44.531Z",
                                "isDeleted": false,
                                "isArchived": false
                            },
                            {
                                "_id": "64018dfc8a7d37f378d6c426",
                                "contractType": "4 - Days Rental",
                                "contractDaysInNumber": 4,
                                "createdAt": "2023-03-03T06:04:44.531Z",
                                "isDeleted": false,
                                "isArchived": false
                            }
                        ]
                    },
                    {
                        "rentalContractId": "64018dfc8a7d37f378d6c426",
                        "price": 10,
                        "rentalContract": [
                            {
                                "_id": "64018dfc8a7d37f378d6c427",
                                "contractType": "8 - Days Rental",
                                "contractDaysInNumber": 8,
                                "createdAt": "2023-03-03T06:04:44.531Z",
                                "isDeleted": false,
                                "isArchived": false
                            },
                            {
                                "_id": "64018dfc8a7d37f378d6c426",
                                "contractType": "4 - Days Rental",
                                "contractDaysInNumber": 4,
                                "createdAt": "2023-03-03T06:04:44.531Z",
                                "isDeleted": false,
                                "isArchived": false
                            }
                        ]
                    }
                ]

expected output:

{
  "productPrice": [
    {
      "rentalContractId": {
          "_id": "64018dfc8a7d37f378d6c427",
          "contractType": "8 - Days Rental",
        },
      "price": 20
    },
    {
      "rentalContractId": {
          "_id": "64018dfc8a7d37f378d6c426",
          "contractType": "4 - Days Rental",
        }
      "price": 10
    }
  ]
}

Thanks in advance.


Solution

  • One option is to use $lookup with pipeline, and update your $set step to use $mergeObjects inside $map:

    db.products.aggregate([
      {$lookup: {
          from: "rentalcontractdetails",
          localField: "productPrice.rentalContractId",
          foreignField: "_id",
          as: "rentalContract",
          pipeline: [{$project: {contractType: 1}}]
      }},
      {$project: {
          _id: 0,
          productPrice: {$map: {
              input: "$rentalContract",
              in: {$mergeObjects: [
                  {rentalContractId: "$$this"},
                  {price: {$arrayElemAt: [
                        "$productPrice.price",
                        {$indexOfArray: ["$productPrice.rentalContractId", "$$this._id"]}
                  ]}}
              ]}
          }}
      }}
    ])
    

    See how it works on the playground example

    • mongodb $lookup and mongoose populate are two different things, working with two different mechanisms. The original question was mixing the two, but used $lookup in the code, so I guess this was the intention.