Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Model.bulkWrite fails embedded array update with embedded discriminator and arrayFilters #14978

Closed
2 tasks done
lcrosetto opened this issue Oct 21, 2024 · 1 comment · Fixed by #15036
Closed
2 tasks done
Labels
confirmed-bug We've confirmed this is a bug in Mongoose and will fix it.
Milestone

Comments

@lcrosetto
Copy link

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.7.2

Node.js version

20.18.0

MongoDB server version

8.0.1

Typescript version (if applicable)

No response

Description

When updating an embedded document array with an embedded schema discriminator and filtered positional operator, the operation succeeds when done with Model.updateOne() but throws the following error when the same operation is done by calling Model.bulkWrite():

MongoInvalidArgumentError: Update document requires atomic operators

Steps to Reproduce

In the following script, update will fail with a MongoInvalidArgumentError:

      const conn = mongoose.createConnection(..);

      const embedDiscriminatorSchema = new mongoose.Schema({
        field1: String,
      });

      const embedSchema = new mongoose.Schema({
        field: String,
        key: String,
      }, {discriminatorKey: 'key'});
      embedSchema.discriminator('Type1', embedDiscriminatorSchema);

      const testSchema = new mongoose.Schema({
        testArray: [embedSchema],
      });
      const TestModel = conn.model('Test', testSchema);

      const test = new TestModel({
        testArray: [{
          key: 'Type1',
          field: 'field',
          field1: 'field1',
        }],
      });
      const r1 = await test.save();
      assert.equal(r1.testArray[0].field1, 'field1');

      const field1update = 'field1 update';
      const res = await TestModel.bulkWrite([{
        updateOne: {
          filter: { _id: r1._id },
          update: {
            $set: {
              'testArray.$[element].field1': field1update,
            },
          },
          arrayFilters: [
            {
              'element._id': r1.testArray[0]._id,
              'element.key': 'Type1',
            },
          ],
        },
      }]);
      const r2 = await TestModel.findById(r1._id);
      assert.equal(r2.testArray[0].field1, field1update);

      conn.deleteModel('Test');

However, if the call to bulkWrite() is replaced with updateOne() the operation will succeed:

      const field1update = 'field1 update';
      const res = await TestModel.updateOne(
        { _id: r1._id },
        {
          '$set': {
            'testArray.$[element].field1': field1update,
          },
        },
        {
          arrayFilters: [
            {
              'element._id': r1.testArray[0]._id,
              'element.key': 'Type1'
            },
          ],
        },
      );
      const r2 = await TestModel.findById(r1._id);
      assert.equal(r2.testArray[0].field1, field1update);

Expected Behavior

An update to an embedded array with an embedded schema discriminator should succeed with Model.bulkWrite(), as the same request does when done with Model.updateOne().

@lcrosetto lcrosetto changed the title Model.bulkWrite fails embedded array update with embedded schema discriminator and filtered positional operator Model.updateOne succeeds, Model.bulkWrite fails embedded array update with embedded discriminator and filtered positional operator Oct 23, 2024
@lcrosetto
Copy link
Author

It looks like arrayFilters is not getting passed through to castUpdate() here:

op['updateOne']['update'] = castUpdate(model.schema, update, {
strict: strict,
upsert: op['updateOne'].upsert
}, model, op['updateOne']['filter']);

If I add it to the options, it works:

        op['updateOne']['update'] = castUpdate(model.schema, update, {
          strict: strict,
          upsert: op['updateOne'].upsert,
          arrayFilters: op['updateOne'].arrayFilters
        }, model, op['updateOne']['filter']);

This may be needed for other operators as well.

@lcrosetto lcrosetto changed the title Model.updateOne succeeds, Model.bulkWrite fails embedded array update with embedded discriminator and filtered positional operator Model.bulkWrite fails embedded array update with embedded discriminator and arrayFilters Oct 25, 2024
@vkarpov15 vkarpov15 added this to the 8.8.2 milestone Nov 12, 2024
@vkarpov15 vkarpov15 added has repro script There is a repro script, the Mongoose devs need to confirm that it reproduces the issue confirmed-bug We've confirmed this is a bug in Mongoose and will fix it. and removed has repro script There is a repro script, the Mongoose devs need to confirm that it reproduces the issue labels Nov 12, 2024
vkarpov15 added a commit that referenced this issue Nov 13, 2024
fix(model): handle array filters when casting bulkWrite
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
confirmed-bug We've confirmed this is a bug in Mongoose and will fix it.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants