进行多个 MongoDB 操作的正确方法

tic*_*tic 3 javascript mongodb node.js express

如果我需要对几个集合执行两个或三个不同的操作,是否有比将find/update操作链接在一起更好的方法?例如:

db.collection('contactinfos').findOneAndUpdate(
  { _id: ObjectID(contactID) },
  { $set: { sharedWith } }
).then(response => {
  db.collection('users').update(
    { _id: { $in: sharedWith.map(id => ObjectID(id)) } },
    { $addToSet: { hasAccessTo: contactID } },
    { multi: true }
  ).then(response => {
    db.collection('users').update(
      { _id: { $in: notSharedWith.map(id => ObjectID(id)) } },
      { $pull: { hasAccessTo: contactID } },
      { multi: true }
    ).then(response => {
      return res.send({ success: true });
    }).catch(err => {
      logger.error(`in updating sharing permissions for ${contactID} by user ${_id}`, err);
      return res.status(400).send({ reason: 'unknown' });
    });
  }).catch(err => {
    logger.error(`in updating sharing permissions for ${contactID} by user ${_id}`, err);
    return res.status(400).send({ reason: 'unknown' });
  });
}).catch(err => {
  logger.error(`in updating sharing permissions for ${contactID} by user ${_id}`, err);
  return res.status(400).send({ reason: 'unknown' });
});
Run Code Online (Sandbox Code Playgroud)

这看起来很混乱,必须有更好的方法来做到这一点。此外,如果第一个错误之后出现findOneAndUpdate阻止其他错误的错误update的无法运行,那么跨文档的数据就会不一致。这些文档包含对其他文档的 ID 引用,以便更快地查找。

另外,有没有办法捕获承诺链中的所有错误?

wsc*_*rge 5

从你的回调地狱我可以看到你没有在任何地方使用方法response的参数.then()。如果您不需要一个查询的结果来执行另一查询,请考虑使用Promise.all()方法:

const updateContactInfo = db.collection('contactinfos')
    .findOneAndUpdate(
        { _id: ObjectID(contactID) }, 
        { $set: { sharedWith } }
    );
const updateUsers = db.collection('users')
    .update(
        { _id: { $in: sharedWith.map(id => ObjectID(id)) } }, //hint: use .map(ObjectId) instead.
        { $addToSet: { hasAccessTo: contactID } },
        { multi: true }
    );
const updateUsers2 = db.collection('users')
    .update(
        { _id: { $in: notSharedWith.map(id => ObjectID(id)) } }, //hint: use .map(ObjectId) instead.
        { $pull: { hasAccessTo: contactID } },
        { multi: true }
    );

Promise
    .all([updateContactInfo, updateUsers, updateUsers2])
    .then((values) => {

        const updateContactInfoResult = values[0];
        const updateUsersResult       = values[1];
        const updateUsers2Result      = values[2];

        return res.send({ success: true });

    })
    .catch((reason) => {

        logger.error(`msg`, reason);
        return res.status(400).send({ reason: 'unknown' });

    });
Run Code Online (Sandbox Code Playgroud)

Promise.all().then()仅当所有承诺都解决时才会继续执行以下内容,否则它将落入.catch()方法中。至于错误处理,您可以轻松链接多个.catch()方法,这在此处得到了很好的解释。

如果您不能出现任何数据不一致,则:

  1. 获取一些包含事务的 SQL 数据库(更简单的解决方案)
  2. 研究MongoDB 两阶段提交

如果发生这种情况是可以接受的,假设每 1kk 次发生一次,请务必检查其在应用程序逻辑中的一致性。