Description
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:

Steps to Reproduce
Reproduction link here.
Expected Behavior
I expect the modification to be successfully applied in all three cases: save
, bulkSave
, and bulkWrite
.