【问题标题】:How to update a mongodb document depending on values of document referenced by its objectId如何根据其 objectId 引用的文档值更新 mongodb 文档
【发布时间】:2022-01-21 02:42:08
【问题描述】:

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

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

// 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。游戏集合的外观示例如下:

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

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

【问题讨论】:

    标签: mongodb mongoose reference mongodb-query


    【解决方案1】:

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

    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"
        }
      }
    ])
    

    【讨论】:

    • 我已经接受了这个答案,尽管我的意思是如何更新许多竞争文件而不仅仅是一个。我想我只需要循环更新它们。
    • 抱歉,我错过了一个游戏可能有多个完成的事实。我建议使用裸 mongo 查询而不是 Mongoose 模型。请查看我的更新。
    • 是的,我需要 $merge。谢谢你。 Mongoose 也支持聚合。
    猜你喜欢
    • 2013-06-22
    • 2018-07-15
    • 2016-01-01
    • 2017-08-18
    • 2018-01-22
    • 2018-08-17
    • 1970-01-01
    • 1970-01-01
    • 2021-01-16
    相关资源
    最近更新 更多