Skip to content

bulkSave and bulkWrite ignore changes to fields belonging to subclasses of subdocuments #15410

Closed
@nikzanda

Description

@nikzanda

Prerequisites

  • I have written a descriptive issue title
  • I have searched existing issues to ensure the bug has not already been reported

Mongoose version

8.14.1

Node.js version

20.10.0

MongoDB server version

6.0.2

Typescript version (if applicable)

5.8.3

Description

My model Order has a complex structure of embedded documents and subclasses: it has a first field called rows which is an embedded array, containing another field rows which is another embedded array, and finally inside this last rows there is requirements, an embedded array that is extended by two subclasses, componentRequirement and toolRequirement, which specify additional fields beyond those in requirement.

The problem I encountered is that if I modify a field of one of the subclasses, for example order.rows.rows.requirements.set({ toolTest: new ObjectId() });, and then run Order.bulkSave(order) or Order.bulkWrite({ updateOne: { .... } }), the change is not taken into account. However, I believe I have configured everything correctly, because if I run order.save(), everything works as expected.

Order model:

interface IOrder {
  _id: Types.ObjectId;
  code: string;
  rows: IOrderRow[];
  createdAt: Date;
  updatedAt: Date;
}

OrderRow subdocument:

interface IOrderRow {
  rows: ISubRow[]
}

SubRow subdocument and requirements configuration:

interface ISubRow {
  requirements: IRequirement[];
}

const requirements = schema.path<Schema.Types.DocumentArray>('requirements');
requirements.discriminator<IComponentRequirement, ComponentRequirementModelType>('ComponentRequirement', componentRequirementSchema);
requirements.discriminator<IToolRequirement, ToolRequirementModelType>('ToolRequirement', toolRequirementSchema);

Requirement subdocument:

interface IRequirement {
  kind: 'ComponentRequirement' | 'ToolRequirement';
  quantity: number;
  notes?: string;
}

ComponentRequirement subclass:

interface IComponentRequirement extends IRequirement {
  kind: 'ComponentRequirement';
  componentTest?: Types.ObjectId;
}

ToolRequirement subclass:

interface IToolRequirement extends IRequirement {
  kind: 'ToolRequirement';
  toolTest?: Types.ObjectId;
}

Repro script:

async function checkValue(
  Order: ReturnType<typeof registerOrderModel>,
  orderId: Types.ObjectId,
  newObjectId: Types.ObjectId,
) {
  const updatedOrder = await Order.findById(orderId).orFail();
  const updatedOrderValue = (updatedOrder.rows[0].rows[0].requirements[1] as ToolRequirementHydratedDocument).toolTest;
  assert(updatedOrderValue?.toString() === newObjectId.toString(), updatedOrder.code);
}

async function run() {
  const connection = await getConnection();

  const Order = registerOrderModel(connection);

  const [
    order1,
    order2,
    order3,
  ] = await Order.create(
    [1, 2, 3].map((index) => ({
      code: `test-${index}`,
      rows: [{
        rows: [{
          requirements: [
            {
              kind: 'ComponentRequirement',
              quantity: 1,
            },
            {
              kind: 'ToolRequirement',
              quantity: 1,
            },
          ]
        }]
      }],
    }))
  );

  const newObjectId = new Types.ObjectId();

  // ORDER 1
  order1.rows[0].rows[0].requirements[1].set({
    toolTest: newObjectId,
  });
  await order1.save();
  await checkValue(Order, order1._id, newObjectId);

  // ORDER 2
  order2.rows[0].rows[0].requirements[1].set({
    toolTest: newObjectId,
  });
  await Order.bulkSave([order2]);
  await checkValue(Order, order2._id, newObjectId);

  // ORDER 3
  await Order.bulkWrite([{
    updateOne: {
      filter: {
        _id: order3._id,
      },
      update: {
        $set: {
          'rows.0.rows.0.requirements.1.toolTest': newObjectId,
        }
      }
    }
  }]);
  await checkValue(Order, order3._id, newObjectId);

  await connection.close();
}

run();

Execution:

Image

Steps to Reproduce

Reproduction link here.

Expected Behavior

I expect the modification to be successfully applied in all three cases: save, bulkSave, and bulkWrite.

Metadata

Metadata

Assignees

No one assigned

    Labels

    confirmed-bugWe've confirmed this is a bug in Mongoose and will fix it.

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions