ATOMICally update multiple documents AND return th

2019-06-01 05:30发布

问题:

In MongoDB, I'm looking for a way to ATOMICally update multiple documents and return all of the updated documents in a single call.

We can do all of the following in MongoDB:

  • Atomically update one document and return the updated document: findAndModify or findOneAndUpdate
  • Atomically update multiple documents: update(...{multi: true} or updateMany
  • Query and return multiple documents: find

I haven't fond a way to update multiple documents and return them all in one call. Is there a way? I'm using Mongoose as the querying package.

回答1:

Atomically update multiple documents: update(...{multi: true} or updateMany

Unfourtantely that is false:

In MongoDB, write operations, e.g. db.collection.update(), db.collection.findAndModify(), db.collection.remove(), are atomic on the level of a single document.


In MongoDB, a write operation is atomic on the level of a single document, even if the operation modifies multiple embedded documents within a single document.

However, you can simulate a transaction to atomically update multiple documents by "using a two-phase commit approach" which is described in detail there.

You may also look at the $isolated operator, which "prevents a write operation that affects multiple documents from yielding to other reads or writes once the first document is written" but it "does not provide “all-or-nothing” atomicity for write operations"

As a summary, it is not possible at mongodb level (nor the driver), but you can simulate it at your application level therefore return what you need.



回答2:

I tested updateMany.

Testing1 :

Update 40K documents with updateMany (pull) , during execution , shutdown db suddenly , then some nodes passed(data was pulled out) , some failed (data was not pulled out in some level 5 nodes in tree) , restart db and run updateMany again , all passed and all data are correct now.

Testing 2:

Create an unique index on a field, insert some data, in updateMany method, some document will failed because of unique key violation.

My test2 result is : zero document was updated.


function insertData() {
  const dataSource = app.models.Entity.getDataSource();
  return new Promise((resolve, reject) => {
    dataSource.connector.connect((err, db) => {
      if (err) {
        reject(new Error('.... error'));
        return;
      }
      const entityCollection = db.collection('Entity');
      // Create index
      entityCollection.createIndex({ age: 1 }, { unique: true })
      .then(() => {
        // Insert data
        const data = [
          {
            id: uuid.v4(),
            age: 1,
            type: 'test',
          },
          {
            id: uuid.v4(),
            age: 2,
            type: 'test',
          },
          {
            id: uuid.v4(),
            age: 3,
            type: 'test',
          },
          {
            id: uuid.v4(),
            age: 4,
            type: 'test',
          },
          {
            id: uuid.v4(),
            age: 5,
            type: 'test',
          },
          {
            id: uuid.v4(),
            age: 6,
            type: 'test',
          },
        ];
        return insertData(data);
      })
      .then(() => {
        resolve();
      })
      .catch((err2) => {
        reject(err2);
      });
    });
  });
}

function updateAge() {
  const dataSource = app.models.Entity.getDataSource();
  return new Promise((resolve, reject) => {
    dataSource.connector.connect((err, db) => {
      if (err) {
        reject(new Error('...error'));
        return;
      }
      const entityCollection = db.collection('Entity');
      entityCollection.updateMany(
        { age: { $gt: 0 } },
        { $mul: { age: 2 } },
      ).then(() => {
        resolve();
      })
      .catch((err2) => {
        logger.error(`ERROR is ${err2}`);
        reject(err2);
      });
    });
  });
}

The test result is : Zero documents are updated. "msg":"ERROR is MongoError: E11000 duplicate key error collection: content-base.Entity index: age_1 dup key: { : 2 }","v":1} 1) updateMany test

0 passing (114ms) 1 failing