如何根据其 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"
}
}
])
如何根据 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"
}
}
])