Go操作MongoDB:基于内嵌数组值批量更新文档的性能选型
问题背景
我的Games集合文档结构如下:
集合内所有文档均采用上述结构,单个文档对应一场游戏对局。我的需求是:当玩家获得新增积分时,更新该玩家在所有关联文档中的分数,同时计算并设置文档的Leader和TotalScore字段,规则如下:
Leader为当前文档内分数最高的玩家TotalScore为第一名与第二名的分差- 若第一名和第二名分数无差距,两个字段分别设为
"None"和0
我目前通过如下Go代码片段实现该功能:
playerscore0 := BsonArrayValAt("Players", 0, "Score") playerscore1 := BsonArrayValAt("Players", 1, "Score") matchstage := bson.D{{Key: "$match", Value: bson.M{"_id": bson.M{"$in": User.Games}}}} unwindstage := bson.D{{Key: "$unwind", Value: "$Players"}} setscorestage := bson.D{{Key: "$set", Value: bson.M{"Players.Score": bson.M{"$cond": bson.M{ "if": bson.M{"$eq": bson.A{"$Players.Player", from}}, "then": bson.M{"$sum": bson.A{"$Players.Score", score}}, "else": "$Players.Score"}}}}} sortstage := bson.D{{Key: "$sort", Value: bson.M{"_id": 1, "Players.Score": -1}}} groupstage := bson.D{{Key: "$group", Value: bson.M{ "_id": "$_id", "Players": bson.M{"$push": "$Players"}, "Leader": bson.M{"$first": "$Leader"}, "TotalScore": bson.M{"$first": "$TotalScore"}}}} setleaderstage := bson.D{{Key: "$set", Value: bson.M{ "Leader": bson.M{"$cond": bson.M{ "if": bson.M{"$gt": bson.A{playerscore0, playerscore1}}, "then": BsonArrayValAt("Players", 0, "PlayerName"), "else": "None"}}, "TotalScore": bson.M{"$cond": bson.M{ "if": bson.M{"$gt": bson.A{playerscore0, playerscore1}}, "then": bson.M{"$subtract": bson.A{playerscore0, playerscore1}}, "else": 0}}}}} mergestage := bson.D{{Key: "$merge", Value: bson.M{"into": "Games", "on": "_id", "whenMatched": "merge", "whenNotMatched": "discard"}}} pipeline := mongo.Pipeline{matchstage, unwindstage, setscorestage, sortstage, groupstage, setleaderstage, mergestage} if cur, err := GameStateC.Aggregate(context.TODO(), pipeline); cur != nil && err == nil { } else { // 错误处理逻辑 } func BsonArrayValAt(arr string, idx uint, val string) bson.M { return bson.M{"$getField": bson.M{"field": val, "input": bson.M{"$arrayElemAt": bson.A{"$" + arr, idx}}}} }
核心疑问
目前的困扰是,这个逻辑本身十分简单,但实现代码存在大量样板代码。据我所知,聚合管道中无法使用Players.0.Score这类点表示法进行数组索引访问,ArrayFilters和$[]操作符也仅支持在update类命令中使用。
我的核心问题是:从性能角度考量,以下三种方案哪种更优?
- 方案1:通过单次聚合管道调用完成所有数据库操作
- 方案2:将逻辑拆分为两部分,先使用支持点表示法和
ArrayFilters的updateMany更新玩家分数,再通过聚合操作更新Leader/TotalScore字段 - 方案3:采用逐文档遍历更新的方式
内容的提问来源于stack exchange,提问作者Ravgni
相关产品推荐
相关产品推荐

