原子更新多个文档并返回它们

时间:2016-12-10 02:39:30

标签: mongodb mongoose atomic

MongoDB中,我正在寻找一种方法来ATOMICally更新多个文档并在一次通话中返回所有更新的文档。

我们可以在MongoDB中执行以下所有操作:

  • 以原子方式更新一个文档并返回更新的文档:findAndModifyfindOneAndUpdate
  • 以原子方式更新多个文档:update(...{multi: true}updateMany
  • 查询并返回多个文档:find

我还没有想过更新多个文档并在一次通话中全部归还的方法。有办法吗?我使用Mongoose作为查询包。

3 个答案:

答案 0 :(得分:1)

  

以原子方式更新多个文档:update(...{multi: true}或   updateMany

不公平地说这是错误的:

  

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.

但是,您可以模拟一个事务,以"using a two-phase commit approach"的形式原子地更新多个文档,详细描述如下。

您还可以查看$isolated运算符,"prevents a write operation that affects multiple documents from yielding to other reads or writes once the first document is written""does not provide “all-or-nothing” atomicity for write operations"

总结一下,mongodb级别(也不是驱动程序)是不可能的,但您可以在应用程序级别进行模拟,从而返回您需要的内容。

答案 1 :(得分:0)

我测试了updateMany。

测试1:

使用updateMany(pull)更新40K文档,在执行期间,突然关闭db,然后一些节点通过(数据被拔出),一些失败(数据未在树中的某些5级节点中拉出),重启db和再次运行updateMany,全部通过,所有数据现在都正确。

测试2:

在字段上创建唯一索引,插入一些数据,在updateMany方法中,某些文档因唯一键违规而失败。

我的test2结果是:零文档已更新。

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);
      });
    });
  });
}

测试结果为:零文档已更新。 " msg":"错误是MongoError:E11000重复键错误集合:content-base.Entity index:age_1 dup key:{:2}"," v&#34 ;:1}     1)更新许多测试

传递0次(114ms)   1失败

答案 2 :(得分:0)

MongoDB v3.6引入了会话,可以实现以下目的:https://docs.mongodb.com/manual/reference/method/Session/。我使用猫鼬,这使得使用它们非常简单。这样的事情可能适用于您的情况:

const session = await mongoose.startSession();

return session.withTransaction(async () => {
  await SomeModel.update({
    foo: "bar"
  }, {
    $set: {
      quux:"baz"
    }
  });

  // NOTE This returns a promise, that is what withTransaction expects
  return SomeModel.find({
    foo: bar
  });
});