How to update subdocuments and parent document

having a document with the next structure(this is a document only with relevant paramaeters):

{
  "_id": {
    "$oid": "663021f4425fd6ef9f2a44f8"
  },
  "docId": "100",
  "orders": [
    {
      "id": 1,
      "isReceived": true,
      "location": "TEST3",
    },
    {
      "id": 2,
      "isReceived": true,
      "location": "TEST2",
    },
    {
      "id": 3,
      "isReceived": false
    },
  ],
  "updatedBy": {
    "id": "1",
    "at": {
      "$date": "2024-05-28T23:00:16.104Z"
    }
  },
  "createdBy": {
    "id": 1,
    "at": {
      "$date": "2024-04-29T22:40:52.577Z"
    }
  },
  "__v": 0
}

I want to update the locations of the orders given an array of orderIds and if isReceived is true and if docId is given, at the same time i want to update the parent document updatedBy with a new object if an order location is updated.

i came up with something like this using mongoose

model.findOneAndUpdate(
        {
          docId,
        },
        {
          $set: {
            'orders.$[element].location': "TEST",
            updatedBy: { id:1, at: new Date()),
          },
        },
        {
          new: true,
          arrayFilters: [
            {
              'element.orders': { $in: orders },
              'element.isReceived': true,
            },
          ],
        },
      );

docId and orders are given by the users, in this case could be 100 and [1,2,3].
my attempt works but it updates the updatedBy object even if you pass an array of orders that arent in the parent, in example: for docId = 100 and orders=[5], there is no order with id 5 but the updatedBy will be updated anyways and i dont want to register an update if no order was updated.

can anyone help me with this?.
thanks in advance

Hi @haifisch,

Thanks for reaching out to MonogDB Community forums :sparkles:

I would suggest you to use the aggregation pipeline with the updateOne method. This will allow you to conditionally update the updatedBy field only if there are matching orders.

You can refer to the following aggregation code snippet for reference:

async function updateOrderLocations(docId, orderIds, newLocation, userId) {
    const dateNow = new Date();

    const result = await OrderModel.updateOne(
        {
            docId,
            orders: {
                $elemMatch: {
                    id: { $in: orderIds },
                    isReceived: true
                }
            }
        },
        [
            {
                $set: {
                    orders: {
                        $map: {
                            input: '$orders',
                            as: 'order',
                            in: {
                                $cond: [
                                    {
                                        $and: [
                                            { $in: ['$$order.id', orderIds] },
                                            { $eq: ['$$order.isReceived', true] }
                                        ]
                                    },
                                    {
                                        $mergeObjects: ['$$order', { location: newLocation }]
                                    },
                                    '$$order'
                                ]
                            }
                        }
                    }
                }
            },
            {
                $set: {
                    updatedBy: {
                        id: userId,
                        at: dateNow
                    }
                }
            }
        ]
    );

    if (result.modifiedCount > 0) {
        console.log('Document updated successfully');
    } else {
        console.log('No matching orders found, document not updated');
    }
}

Also, please ensure that the above aggregation code is based on your sample document shared. Make sure you do thorough testing before implementing it in your production environment.

I hope it helps! In case you have any further questions, please feel free to reach out.

Best regards,
Kushagra

3 Likes

thank you for your answer i will try it and get back with the results

1 Like