首页 > 解决方案 > 正在跳过多个 findOneAndUpdate 操作

问题描述

我有一个forEach循环,我正在查询一个文档,做一些简单的数学计算,然后updating是集合中的一个文档,然后继续下一次迭代。

问题是,很多时候随机一些UPDATE操作不会更新文档。我不知道为什么会这样。lock是不是因为

我曾尝试在手术前记录一些事情update。数据都是正确的,但是当涉及到时update,它根本不会随机更新。在 10 次迭代中,假设 8 次将正确工作

    const name = "foo_game";
    players.forEach(({ id, team, username }) => {
      let updatedStats = {};
      Users.findOne({ id }).then(existingPlayer => {
        if (!existingPlayer) return;

        const { stats } = existingPlayer;
        const existingStats = stats[pug.name];

        if (!existingStats) return;
        const presentWins = existingStats.won || 0;
        const presentLosses = existingStats.lost || 0;
        updatedStats = {
          ...existingStats,
          won:
            team === winningTeam
              ? presentWins + 1
              : changeWinner
              ? presentWins - 1
              : presentWins,
          lost:
            team !== winningTeam
              ? presentLosses + 1
              : changeWinner
              ? presentLosses - 1
              : presentLosses,
        };

       // THE CALCULATIONS ARE ALL CORRECT TILL THIS POINT
       // THE UPDATE WIILL RANDOMLY NOT WORK
        Users.findOneAndUpdate(
          { id, server_id: serverId },
          {
            $set: {
              username,
              stats: { ...stats, [name]: updatedStats },
            },
          },
          {
            upsert: true,
          }
        ).exec();
      });
    });

标签: node.jsmongodbmongoose

解决方案


基本上你在这里缺少的是异步操作findOne()findOneAndUpdate()不能保证在你完成之前foreach()完成。对于其中包含异步操作的循环,使用forEach()不是一个很好的选择,但这里的另一个要点是它是完全没有必要的,因为 MongoDB 有更好的方法来执行此操作,并且在对服务器的一个请求中。

简而言之,您实际上想要提供一组指令 ,而不是“循环” bulkWrite()

let server_id = serverId; // Alias one of your variables or just change it's name

Users.bulkWrite(
  players.map(({ id, team, username }) => 
    ({
      "updateOne": {
        "filter": { _id, server_id },
        "update": {
          "$set": { username },
          "$inc": {
            [`stats.${name}.won`]:
              team === winningTeam ?  1 : changeWinner ?  - 1 : 0,
            [`stats.${name}.lost`]:
              team !== winningTeam ?  1 : changeWinner ?  - 1 : 0
          }
        },
        "upsert": true
      }
    })
  )
)
.then(() => /* whatever continuation here */ )
.catch(e => console.error(e))

因此,不是循环,而是在批量操作中为每个数组成员Array.map()生成一个"updateOne"语句并将其发送到服务器。当然,另一个变化是您根本不需要findOne()读取现有值。您真正需要的是使用$inc运算符来增加减少当前值。在这里注意,如果当前在指定路径上没有记录任何内容,那么它将创建具有1/-1/0由逻辑确定的任何值并传递给$inc.

请注意,这实际上是您通常应该做的事情,除了避免不必要的异步调用循环之外,这里的主要事情是实际使用$incMongoDB 所具有的原子运算符。从数据库读取数据状态以进行更改是一种反模式,最好避免。


推荐阅读