How update object in array or add new value in array

i have model

const cartSchema = new Schema<ICart>(
  {
    orders: {
      type: [
        {
          hotelId: {
            type: SchemaTypes.ObjectId,
            required: true,
            ref: 'hotels',
          },
          startDate: { type: Date, required: true },
          endDate: { type: Date, required: true },
          rooms: {
            type: [
              {
                roomTypeId: {
                  type: SchemaTypes.ObjectId,
                  required: true,
                  ref: 'roomTypes',
                },
                quantity: {
                  type: Number,
                  min: 1,
                  required: true,
                },
              },
            ],
            required: true,
            min: 1,
          },
        },
      ],
      required: true,
      max: 20,
      min: 1,
    },
    userId: {
      type: SchemaTypes.ObjectId,
      index: true,
      unique: true,
      required: true,
      ref: 'users',
    },
    isActive: { type: Boolean, default: true, required: true },
  },
  { timestamps: true, collection: 'carts' },
);

const Cart = model<ICart>('carts', cartSchema);

Cart .findOneAndUpdate(
      {
        userId,
        orders: {
          $elemMatch: {
            hotelId: newOrder.hotelId,
            startDate: newOrder.startDate,
            endDate: newOrder.endDate,
          },
        },
      },
      {
        $set: { 'orders.$': newOrder },
      },
      { new: true },
    );

i want if orders have hoteiId startDate, endDate same value in newOrder it will update, if not , it insert new newOrder , but not work

Hi @Anh_Tu_n_Hu_nh_Van and welcome to MongoDB community forums!!

Based on the schema design, I tried to create the sample document in my local environment:
The sample document looks like the following:

[
  {
    _id: ObjectId("645a057f469b1090934a6747"),
    orders: [
      {
        hotelId: ObjectId("61a3a731c0a774594ee276d5"),
        startDate: ISODate("2023-05-14T00:00:00.000Z"),
        endDate: ISODate("2023-05-15T00:00:00.000Z"),
        rooms: [
          {
            roomTypeId: ObjectId("61a3a731c0a774594ee276d4"),
            quantity: 2
          }
        ]
      },
      {
        hotelId: ObjectId("61a3a731c0a774594ee276d6"),
        startDate: ISODate("2023-05-15T00:00:00.000Z"),
        endDate: ISODate("2023-05-16T00:00:00.000Z"),
        rooms: [
          {
            roomTypeId: ObjectId("61a3a731c0a774594ee276d7"),
            quantity: 1
          }
        ]
      }
    ],
    userId: ObjectId("61a3a731c0a774594ee276d8"),
    isActive: true,
    createdAt: ISODate("2023-05-09T14:30:00.000Z"),
    updatedAt: ISODate("2023-05-09T14:30:00.000Z")
  }
]

and if I understand the above statement correctly, you wish to add a new order to the order array if the hotelID, startDate and EndDate not have the match.

Based on my understanding, here is how the update can be performed.

  1. Create a sample subdocument to be inserted:
const newOrder = {
  hotelId: ObjectId('61a3a731c0a774594ee276dA'), // the ID of the new hotel
  startDate: ISODate('2023-05-17T00:00:00.000Z'),
  endDate: ISODate('2023-05-18T00:00:00.000Z'),
  rooms: [
    {
      roomTypeId: ObjectId('61a3a731c0a774594ee276dB'),
      quantity: 1,
    },
  ],
};
  1. Check if the existing subdocument already exists:
const filter = {
  'orders.hotelId': { $ne: newOrder.hotelId },
  'orders.startDate': { $ne: newOrder.startDate },
  'orders.endDate': { $ne: newOrder.endDate },
};
  1. push the required subdocument, if conditions are a match:
const update = {
  $push: {
    orders: newOrder,
  },
};
  1. Finally, update the document.

db.post225342_02.updateOne( filter, update)

Let us know if the above query does not work for the requirements.

Regards
Aasawari


it can not update element 8 , even though it’s the same hotelId startDate endDate

my solution

    // update order if order exist
 const cartDb = await cartService.findOne({ userId: userId });

    const updateCartOfUser = await cartService.findOneAndUpdate(
      {
        userId,
        'orders.hotelId': newOrder.hotelId,
        'orders.startDate': newOrder.startDate,
        'orders.endDate': newOrder.endDate,
      },
      {
        $set: { 'orders.$': newOrder, isActive: true },
      }
    );

    // if not add order
    if (!updateCartOfUser) {
      cartDb.orders.push(newOrder);

      await cartDb.save();
      return oke(newOrder);
    }

but i want a method that can just update or add a new element