Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MongoDB 4.2 combine aggregation pipeline update with array filters

Tags:

mongodb

I saw that MongoDB 4.2 introduces aggregation pipeline updates, which allows you to set document fields based on other fields in the document.

Considering the following document

{
    ean: "12345",
    orderedQty: 2,
    fulfilledQty: 1,
    "status": "pending"
}

I could use the following command to increment the fulfilledQty by 1 and if the orderedQty matches the fulfilledQty set the status accordingly:

db.collection.findOneAndUpdate({}, [
  {
    "$set": {
        "orderedQty": {
            "$add": [ "$fulfilledQty", 1 ]
        }
    },  
    "$set": {
      "status": {
        "$cond": {
          "if": { "$eq": ["$orderedQty", "$fulfilledQty"] },
          "then": "fulfilled",
          "else": "pending"
        }
      }
    }
  }
])

My question: How would i perform this on an array. Say I have a document like this:

_id: "test",
items: [
    {ean: "12345", orderedQty: 2, fulfilledQty: 1, "status": "pending"},
    {ean: "67891", orderedQty: 1, fulfilledQty: 1, "status": "fulfilled"}
]

Given I have the params ean = 12345 and an increase value by 1. How could I target the specific array item with EAN 12345, increase the fulfilledQty by 1 and set the status? I want to only chance the status field and fulfilledQty field and leave the rest of the items array as is. So expected outcome would be:

_id: "test",
items: [
    {ean: "12345", orderedQty: 2, fulfilledQty: 2, "status": "fulfilled"},
    {ean: "67891", orderedQty: 1, fulfilledQty: 1, "status": "fulfilled"}
]
like image 845
florian norbert bepunkt Avatar asked Oct 15 '25 14:10

florian norbert bepunkt


1 Answers

I found the following workflow (works only for mongodb 4.2+), but it's amazingly verbose…

Given that there are two variables, an item identifier (called ean) and a quantity that was shipped (called fulfilledQty)

collection.update({}, [
  {
    $set: {
      items: {
        $map: {
          input: "$items",
          as: "item",
          in: {
            $mergeObjects: [
              "$$item",
              {
                fulfilledQty: {
                  $switch: {
                    branches: [
                      {
                        case: {
                          $eq: ["$$item.ean", ean]
                        },
                        then: {
                          $toInt: {
                            $add: ["$$item.fulfilledQty", fulfilledQty]
                          }
                        }
                      }
                    ],
                    default: "$$item.fulfilledQty"
                  }
                }
              }
            ]
          }
        }
      }
    }
  },
  {
    $set: {
      items: {
        $map: {
          input: "$items",
          as: "item",
          in: {
            $mergeObjects: [
              "$$item",
              {
                status: {
                  $cond: {
                    if: {
                      $eq: ["$$item.orderedQty", "$$item.fulfilledQty"]
                    },
                    then: "fulfilled",
                    else: "$$item.status"
                  }
                }
              }
            ]
          }
        }
      }
    }
  }
]);

I used a switch statement since in my use case I have multiple different EANs. Downside is that I had to use a $map operation, so it always iterates over the whole items array.

like image 64
florian norbert bepunkt Avatar answered Oct 18 '25 08:10

florian norbert bepunkt



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!