自动更新多个文档并返回它们

问题描述 投票:0回答:3

MongoDB
中,我正在寻找一种方法来原子地更新多个文档并在一次调用中返回所有更新的文档。

我们可以在

MongoDB
中执行以下所有操作:

  • 原子更新一个文档并返回更新后的文档:
    findAndModify
    findOneAndUpdate
  • 自动更新多个文档:
    update(...{multi: true}
    updateMany
  • 查询并返回多个文档:
    find

我不喜欢一种方法来更新多个文档并在一次调用中将它们全部返回。有办法吗?我使用

Mongoose
作为查询包。

mongodb mongoose transactions
3个回答
4
投票

自动更新多个文档:

update(...{multi: true}
updateMany

不幸的是,这是错误的:

在 MongoDB 中,写操作,例如db.collection.update(), db.collection.findAndModify()、db.collection.remove() 是 atomic on 单个文档的级别.


在 MongoDB 中,写操作在单个级别上是原子的 document,即使操作修改了多个嵌入文档 在单个文档中。

但是,您可以通过 “使用两阶段提交方法” 来模拟事务以原子方式更新多个文档,这在此处有详细描述。

您还可以查看

$isolated
运算符,它 “防止影响多个文档的写入操作在第一个文档写入后屈服于其他读取或写入”,但它 “不提供“所有-or-nothing”写入操作的原子性”

总而言之,在 mongodb 级别(也不在驱动程序)上不可能,但您可以在应用程序级别模拟它,因此返回您需要的内容。


1
投票

MongoDB v3.6 引入了会话,这使得这成为可能:https://docs.mongodb.com/manual/reference/method/Session/。我使用猫鼬,这使得使用它们非常简单。像这样的东西可能适合你的情况:

const session = await mongoose.startSession();

let result;

// NOTE `withTransaction` expects a promise so I'm using `async`
await session.withTransaction(async () => {
  await SomeModel.update({
    foo: "bar"
  }, {
    $set: {
      quux:"baz"
    }
  }).session(session);

  result = await SomeModel.find({
    foo: bar
  }).session(session);
});

return result;

0
投票

我测试了 updateMany。

测试1:

使用updateMany更新40K文档(拉取),执行过程中,突然关闭db,然后一些节点通过(数据被拉出),一些失败(树中某些5级节点数据没有拉出),重新启动db并运行updateMany再次,所有通过并且所有数据现在都是正确的。

测试2:

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

我的测试2结果是:更新了零文档。


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 重复键错误集合:内容库。实体索引:age_1 dup 键:{ : 2 }”,“v”:1} 1)更新多次测试

0 通过(114ms) 1 次失败

© www.soinside.com 2019 - 2024. All rights reserved.