如何根据其 objectId 引用的文档值更新 mongodb 文档

How to update a mongodb document depending on values of document referenced by its objectId

如何根据 objectId 引用的文档值更新 MongoDB 文档? (我正在通过猫鼬使用 MongoDB)

假设我有两个 collection。一种叫做比赛,另一种叫做游戏。一场比赛可以有几场比赛。请参阅下面的代码示例

// competition documents
[
    {
        compeititionName:"myCompetition",
        games:["617...b16", "617...b19", "617...b1c",
        competitionStatus:"notStarted",
    },
    {
        compeititionName:"yourCompetition",
        games:["617...b18", "617...b19", "617...b1c",
        competitionStatus:"playing",
    },
    {
        compeititionName:"ourCompetition",
        games:["617...b14", "617...b19", "617...b2b",
        competitionStatus:"ended",
    }

]

以上 competitionStatus 取决于该比赛的比赛状态。

如果所有的比赛都没有开始那么比赛应该有 notStarted 作为它的 competitionStatus。但是,如果正在玩任何游戏,或者有游戏尚未开始而其他游戏已完成,则比赛状态应为 playing。最后,如果所有比赛都结束了,比赛状态应该是ended。游戏 collection 的外观示例如下:

// game documents
[
    {
        _id:"617...b16",
        gameStatus:"notStarted"
    },
    {
        _id:"617...b18",
        gameStatus:"playing"
    },
    {
        _id:"617...b14",
        gameStatus:"ended"
    },
]

如果 _id 状态刚刚更改的游戏,我如何更新 competitionStatus

既然是猫鼬,你select先更新你要更新的模型:

const completion = await CompletionModel.FindOne({games: _id_of_the_game});

然后汇总所有游戏状态:

const statuses = await GameModel.aggregate([
  {$match: {_id: {$in: completion.games}}},
  {$group: {_id: gameStatus}}
]).toArray();

然后应用您的业务逻辑来设置状态:

if(statuses.leength === 1) { // all games have same status
  if(statuses[0]._id === "notStarted") {
    completion.competitionStatus = "notStarted";
  } elseif (statuses[0]._id === "ended") {
    completion.competitionStatus = "ended";
  } else {
    completion.competitionStatus = "playing";
} else {
  completion.competitionStatus = "playing";
}

然后保存到数据库:

await completion.save();

请记住,此伪代码容易出现竞争条件 - 如果游戏在 aggregate()save() 之间更改状态,您可能会在完成文档中看到陈旧的状态。如果需要,您可能希望添加额外的查询以确保数据的一致性。

更新

如果一个 game 可以超过 1 个 completion 那么使用 Mongoose 将是非常低效的。从 v4.2 开始,您可以使用 $merge 聚合阶段在数据库端进行所有计算,并更新匹配的文档:

db.competition.aggregate([
  {
    $match: {
      games: "id_of_the_game"
    }
  },
  {
    "$lookup": {
      from: "games",
      let: {
        g: "$games"
      },
      pipeline: [
        {
          $match: {
            $expr: {
              $in: [
                "$_id",
                "$$g"
              ]
            }
          }
        },
        {
          $group: {
            _id: "$gameStatus"
          }
        }
      ],
      "as": "statuses"
    }
  },
  {
    $set: {
      competitionStatus: {
        "$cond": {
          "if": {
            "$gt": [
              {
                "$size": "$statuses"
              },
              1
            ]
          },
          "then": {
            _id: "playing"
          },
          "else": {
            "$arrayElemAt": [
              "$statuses",
              0
            ]
          }
        }
      }
    }
  },
  {
    "$project": {
      competitionStatus: "$competitionStatus._id"
    }
  },
  {
    "$merge": {
      "into": "competition"
    }
  }
])