MongoDb updateOne to update an array

I have a Stock document.

{
    "_id": {
        "$oid": "5fe45a9de1cccf001a7c6f7f"
    },
    "caseQuantity": 5,
    "unitQuantity": 0,
    "totalQuantity": 2000,
    "currentQuantity": 2000,
    "isClaimActive": "true",
    "claim": 32,
    "status": "Active",
    "purchaseInventoryId": {
        "$oid": "5fe45a9ce1cccf001a7c6f7e"
    },
    "index": "1608800909352",
    "batchNo": 1,
    "unitPrice": 14.19,
    "casePrice": 255.75,
    "product": {
        "$oid": "5f8d9a6184c1d0005814ed61"
    },
    "productName": "Red Cow - Red Cow 18g",
    "type": {
        "$oid": "5f8d931fcc42160023d770e2"
    },
    "units": 400,
    "agency": {
        "$oid": "5f8d6f0acc42160023d770c4"
    },
    "createdBy": {
        "$oid": "5f8d6f2dcc42160023d770c5"
    },
    "__v": 0,
    "reservations": [{
        "loadingsheetId": "5fe45a9ce1cccf001a7c6f9k"
        "reservedTotalQuantity": 22
    }]
}

I need to update my reservation array of objects with a new reservation of reservedTotalQuantity 10. Output should be like below.

{
        "_id": {
            "$oid": "5fe45a9de1cccf001a7c6f7f"
        },
        "caseQuantity": 5,
        "unitQuantity": 0,
        "totalQuantity": 2000,
        "currentQuantity": 2000,
        "isClaimActive": "true",
        "claim": 32,
        "status": "Active",
        "purchaseInventoryId": {
            "$oid": "5fe45a9ce1cccf001a7c6f7e"
        },
        "index": "1608800909352",
        "batchNo": 1,
        "unitPrice": 14.19,
        "casePrice": 255.75,
        "product": {
            "$oid": "5f8d9a6184c1d0005814ed61"
        },
        "productName": "Red Cow - Red Cow 18g",
        "type": {
            "$oid": "5f8d931fcc42160023d770e2"
        },
        "units": 400,
        "agency": {
            "$oid": "5f8d6f0acc42160023d770c4"
        },
        "createdBy": {
            "$oid": "5f8d6f2dcc42160023d770c5"
        },
        "__v": 0,
        "reservations": [{
            "loadingsheetId": "5fe45a9ce1cccf001a7c6f97"
            "reservedTotalQuantity": 22
        },{
           {
            "loadingsheetId": "5fe45a9ce1cccf001a7c6f98"
            "reservedTotalQuantity": 10
       }]
    }

How can i achieve this using Stock.updateOne() update operation??

Thanks to @Prasad_Saya was able to get this sorted.

Hi @Shanka_Somasiri,

Indeed, you could do it with $set + $concatArrays but it’s a bit overkill and less efficient than a simple $push which does exactly what you want.

https://docs.mongodb.com/manual/reference/operator/update/push/#append-a-value-to-an-array

Your code should be something like:

db.stock.update(
   { _id: ObjectId("5fe45a9de1cccf001a7c6f7f") },
   { $push: { reservations: {"loadingsheetId": "5fe45a9ce1cccf001a7c6f98", "reservedTotalQuantity": 10} } }
)

Here is a generic example I did in the Mongo Shell:

test:PRIMARY> db.col.insert({reservations: [{table:1, seats: 3}]})
WriteResult({ "nInserted" : 1 })
test:PRIMARY> db.col.findOne()
{
	"_id" : ObjectId("5ff79e3d87f6a6d05484e2c8"),
	"reservations" : [
		{
			"table" : 1,
			"seats" : 3
		}
	]
}
test:PRIMARY> db.col.update({"_id" : ObjectId("5ff79e3d87f6a6d05484e2c8")}, {$push: {reservations: {table: 2, seats: 10}}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
test:PRIMARY> db.col.findOne()
{
	"_id" : ObjectId("5ff79e3d87f6a6d05484e2c8"),
	"reservations" : [
		{
			"table" : 1,
			"seats" : 3
		},
		{
			"table" : 2,
			"seats" : 10
		}
	]
}

Cheers,
Maxime.

HI @MaBeuLux88. Thanks for your answer.

Ok lets say that i have 4 operations to be done.
1). Push data to reservations array
2). Update the currentQuantity
3). Update the caseQuantity
4). Update the units Quantity

My current implementation is below.

for (const el of records) {
        console.log(el);
        promiseArray.push(
          Stock.updateOne(
            {
              index: el.index,
              product: el.product,
              batchNo: el.batchNo,
              agency,
              currentQuantity: { $gte: el.loadingTotal },
            },
            [
              {
                $set: {
                  reservations: {
                    $concatArrays: [
                      '$reservations',
                      [
                        {
                          loadingSheetId: sheetAfterSave._id,
                          reservedCaseQuantity: el.loadingCaseCount,
                          reservedUnitQuantity: el.loadingUnitCount,
                          reservedTotalQuantity: el.loadingTotal,
                        },
                      ],
                    ],
                  },
                  currentQuantity: {
                    $add: ['$currentQuantity', -el.loadingTotal],
                  },
                },
              },
              {
                $set: {
                  unitQuantity: {
                    $mod: ['$currentQuantity', el.units],
                  },
                },
              },
              {
                $set: {
                  caseQuantity: {
                    $floor: {
                      $divide: ['$currentQuantity', el.units],
                    },
                  },
                },
              },
            ],
            {
              session: session,
            }
          )
        );

This works perfectly. But seems like i cannot use $push here. Getting below error since update operators cannot be used as an aggregation pipeline operator if i’m not mistaken.

[distribution] MongooseError: Invalid update pipeline operator: "$push"
[distribution]     at castPipelineOperator (/app/node_modules/mongoose/lib/helpers/query/castUpdate.js:141:9)
[distribution]     at castUpdate (/app/node_modules/mongoose/lib/helpers/query/castUpdate.js:39:22)
[distribution]     at model.Query._castUpdate (/app/node_modules/mongoose/lib/query.js:4524:10)
[distribution]     at castDoc (/app/node_modules/mongoose/lib/query.js:4553:18)
[distribution]     at model.Query._updateThunk (/app/node_modules/mongoose/lib/query.js:3735:20)
[distribution]     at model.Query.<anonymous> (/app/node_modules/mongoose/lib/query.js:3833:23)
[distribution]     at model.Query._wrappedThunk [as _updateOne] (/app/node_modules/mongoose/lib/helpers/query/wrapThunk.js:16:8)
[distribution]     at /app/node_modules/kareem/index.js:369:33
[distribution]     at processTicksAndRejections (node:internal/process/task_queues:75:11)

Is there any other way I could do this to overcome the overkill and inefficiency here??

I take that back - my bad. I didn’t understand you where using the aggregation pipeline update $set instead of the update operator $set and for some reason I thought the entire array was going over the network between your client and MDB cluster but that’s not the case here. You are all good :+1: !

The only comment I have is that I thing you can write this in a bit more compact way if you like. It won’t affect the performances but… I like compact stuff :smiley: !

for (const el of records) {
        console.log(el);
        promiseArray.push(
          Stock.updateOne(
            {
              index: el.index,
              product: el.product,
              batchNo: el.batchNo,
              agency,
              currentQuantity: { $gte: el.loadingTotal },
            },
            [
              {
                $set: {
                  reservations: {
                    $concatArrays: [
                      '$reservations',
                      [
                        {
                          loadingSheetId: sheetAfterSave._id,
                          reservedCaseQuantity: el.loadingCaseCount,
                          reservedUnitQuantity: el.loadingUnitCount,
                          reservedTotalQuantity: el.loadingTotal,
                        },
                      ],
                    ],
                  },
                  currentQuantity: {
                    $add: ['$currentQuantity', -el.loadingTotal],
                  },
                  unitQuantity: {
                    $mod: ['$currentQuantity', el.units],
                  },
                  caseQuantity: {
                    $floor: {
                      $divide: ['$currentQuantity', el.units],
                    },
                  },
                },
              }
            ],
            {
              session: session,
            }
          )
        );

Cheers,
Maxime.

1 Like

Hi @MaBeuLux88,
Thanks for your advice regarding writing in compact way. Yes it won’t affect the performance but yeah i will look into that.

Appreciate your help.

Cheers :smiley:

1 Like

This topic was automatically closed 5 days after the last reply. New replies are no longer allowed.