Home > Blockchain >  MongoDB aggregation multiple operation
MongoDB aggregation multiple operation

Time:03-27

I have an items collection:

[
   {_id: '1', name: 'A', description: 'Some description 1'}, 
   {_id: '2', name: 'B', description: 'Some description 2'}, 
   {_id: '3', name: 'C', description: 'Some description 3'}, 
   {_id: '4', name: 'D', description: 'Some description 4'}, 
]

I have another collection cItems:

[
    {_id: '1', itemId: '1', items:[ {itemId: '2', qty: 4}, {itemId: '3', qty: 5}  ]},
    {_id: '2', itemId: '4', items:[ {itemId: '2', qty: 1}, {itemId: '3', qty: 1}  ]},
]

I am trying to get the details of the item based on itemId in CItems collection using aggregate.

cItems.aggregate([
      { $match: { _id: { $eq: categoryId } } },
      { $unwind: "$items" },
      {
        $lookup: {
          from: "items",
          let: {
            itemId: "$items.itemId",
            items: "$items",
          },
          pipeline: [
            { $match: { $expr: { $eq: ["$_id", "$$itemId"] } } },
            {
              $replaceRoot: {
                newRoot: { $mergeObjects: ["$$items", "$$ROOT"] },
              },
            },
            {
              $project: {
                qty: 1,
                name: 1,
                description: 1,
              },
            },
          ],
          as: "items",
        },
      },
      {
        $group: {
          _id: "$_id",
          items: { $push: { $first: "$items" } },
          name: { $first: "$name" },
          description: { $first: "$description" },
        },
      },
    ]);

Results I am getting is:

{
    "_id": "1",
    "items": [
    {
      "qty": 4,
      "_id": "2",
      "name": "A",
      "description": "Some description 2"
    },
    {
      "qty": 5,
      "_id": "3",
      "name": "C",
      "description": "Some description 3"
    }
  ],
    "name": null,
    "description": null
}

The problem is it is showing null for name & description for itemId that is not in the array.

Expected output:

{
    "_id": "1",
    "items": [
    {
      "qty": 4,
      "_id": "2",
      "name": "A",
      "description": "Some description 2"
    },
    {
      "qty": 5,
      "_id": "3",
      "name": "C",
      "description": "Some description 3"
    }
  ],
    "name": 'A',
    "description": "Some description 1"
}

I am using the latest version of MongoDB.

CodePudding user response:

You miss out on the $lookup stage to join with items for the parent item (itemId) [2nd stage].

And to get the parent item's name & description, you need $arrayElemAt to get the first item of parentItem [last stage].

Solution 1

db.cItems.aggregate([
  {
    $match: {
      _id: {
        $eq: categoryId
      }
    }
  },
  {
    $lookup: {
      from: "items",
      localField: "itemId",
      foreignField: "_id",
      as: "parentItem"
    }
  },
  {
    "$unwind": "$items"
  },
  {
    $lookup: {
      from: "items",
      let: {
        itemId: "$items.itemId",
        items: "$items"
      },
      pipeline: [
        {
          $match: {
            $expr: {
              $eq: [
                "$_id",
                "$$itemId"
              ]
            }
          }
        },
        {
          $replaceRoot: {
            newRoot: {
              $mergeObjects: [
                "$$items",
                "$$ROOT"
              ]
            }
          }
        },
        {
          $project: {
            qty: 1,
            name: 1,
            description: 1
          }
        }
      ],
      as: "items"
    }
  },
  {
    $group: {
      _id: "$_id",
      items: {
        $push: {
          $first: "$items"
        }
      },
      name: {
        $first: {
          "$arrayElemAt": [
            "$parentItem.name",
            0
          ]
        }
      },
      description: {
        $first: {
          "$arrayElemAt": [
            "$parentItem.description",
            0
          ]
        }
      }
    }
  }
])

Sample Mongo Playground (Solution 1)


Solution 2

db.cItems.aggregate([
  {
    $match: {
      _id: {
        $eq: categoryId
      }
    }
  },
  {
    $lookup: {
      from: "items",
      localField: "itemId",
      foreignField: "_id",
      as: "parentItem"
    }
  },
  {
    $lookup: {
      from: "items",
      let: {
        items: "$items"
      },
      pipeline: [
        {
          $match: {
            $expr: {
              $in: [
                "$_id",
                "$$items.itemId"
              ]
            }
          }
        },
        {
          "$replaceRoot": {
            "newRoot": {
              "$mergeObjects": [
                {
                  $arrayElemAt: [
                    "$$items",
                    0
                  ]
                },
                "$$ROOT"
              ]
            }
          }
        }
      ],
      as: "items"
    }
  },
  {
    $project: {
      _id: 1,
      itemId: 1,
      items: 1,
      name: {
        $arrayElemAt: [
          "$parentItem.name",
          0
        ]
      },
      description: {
        $arrayElemAt: [
          "$parentItem.description",
          0
        ]
      }
    }
  }
])

Sample Mongo Playground (Solution 2)

  • Related