Golang实现MongoDB聚合查询返回空数据问题排查
MongoDB聚合查询Golang实现返回空对象问题解决
问题场景
需要通过MongoDB聚合查询获取指定chat_id对应的最新消息,逻辑为:匹配指定chat_id数组 → 按created字段倒序排序 → 按chat_id分组并取每组最后一条消息。控制台执行聚合语句可正常返回结果,但Golang实现返回空对象。
控制台正常聚合语句
db.message.aggregate([ { $match: { chat_id: { $in: ["dtlzoD9fI15q8_YM6eqp", "unps_ZwBZ7mCubC3TsKl"], }, } }, { $sort: { "created": -1, }, }, { $group: { "_id": {"chat_id": "$chat_id"}, "doc": { "$last": "$$ROOT" } } } ])
控制台返回结果
[ { "_id": { "chat_id": "unps_ZwBZ7mCubC3TsKl" }, "doc": { "_id": {"$oid": "63ac61d71a11d3b05340c001"}, "id": "wsBFKoN51q5v4Nnv3A-B", "chat_id": "unps_ZwBZ7mCubC3TsKl", "body": "Certainty determine at of arranging perceived situation or. Or wholly pretty county in oppose. Favour met itself wanted settle put garret twenty. In astonished apartments resolution so an it. Unsatiable on by contrasted to reasonable companions an. On otherwise no admitting to suspicion furniture it. ", "created": 1672241623466 } }, { "_id": { "chat_id": "dtlzoD9fI15q8_YM6eqp" }, "doc": { "_id": {"$oid": "63ac607f141f0526cba7113d"}, "id": "jhjVxQUzQbPCLebnupS9", "chat_id": "dtlzoD9fI15q8_YM6eqp", "body": "Certainty determine at of arranging perceived situation or. Or wholly pretty county in oppose. Favour met itself wanted settle put garret twenty. In astonished apartments resolution so an it. Unsatiable on by contrasted to reasonable companions an. On otherwise no admitting to suspicion furniture it. ", "created": 1672241279354 } } ]
Golang异常实现代码
type Message struct { ID string `json:"id" bson:"id"` ChatID string `json:"chat_id" bson:"chat_id"` Body string `json:"body" bson:"body"` Created int64 `json:"created" bson:"created"` } ... ids := []string{"unps_ZwBZ7mCubC3TsKl", "dtlzoD9fI15q8_YM6eqp"} matchStage := bson.D{{ Key: "$match", Value: bson.D{{Key: "chat_id", Value: bson.D{{Key: "$in", Value: ids}}}}, }} sortStage := bson.D{{"$sort", bson.D{{"created", -1}}}} groupStage := bson.D{{ Key: "$group", Value: bson.D{ { Key: "_id", Value: bson.D{ {"chat_id", "$chat_id"}, }, }, { Key: "message", Value: bson.D{ {"$last", "$$ROOT"}, }, }, }, }} cursor, err := db.Aggregate(context.Background(), mongo.Pipeline{matchStage, groupStage, sortStage}) if err != nil {} var messages []*Message err = cursor.All(context.Background(), &messages) if err != nil { fmt.Print("Cursor err: ", err) return } defer func() { err := cursor.Close(context.Background()) if err != nil { return } }()
Golang返回结果
MSG: &{ 0} // empty data MSG: &{ 0}
问题原因分析
- 聚合阶段顺序错误:Golang代码中把
$group阶段放在$sort之前,导致分组后再排序无法保证每组取到最新消息,逻辑完全错误。正确顺序应为$match→$sort→$group。 - 结果结构不匹配:聚合返回的是包含
message字段的嵌套对象,但直接反序列化为Message结构体,字段无法对应,导致反序列化失败,得到空对象。 - 缺少MongoDB原生_id映射:结构体未定义MongoDB的
_id字段(ObjectId类型),无法映射返回结果中的_id字段。
修复后的Golang代码
import ( "context" "fmt" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/bson/primitive" "go.mongodb.org/mongo-driver/mongo" ) // 修正结构体,添加MongoDB _id映射,并重命名原id字段为MsgID避免冲突 type Message struct { ID primitive.ObjectID `json:"_id" bson:"_id"` MsgID string `json:"id" bson:"id"` ChatID string `json:"chat_id" bson:"chat_id"` Body string `json:"body" bson:"body"` Created int64 `json:"created" bson:"created"` } func getLatestMessages(db *mongo.Collection) ([]*Message, error) { ids := []string{"unps_ZwBZ7mCubC3TsKl", "dtlzoD9fI15q8_YM6eqp"} // 匹配阶段 matchStage := bson.D{{ Key: "$match", Value: bson.D{{Key: "chat_id", Value: bson.D{{Key: "$in", Value: ids}}}}, }} // 排序阶段:按created倒序,确保最新消息排在前面 sortStage := bson.D{{"$sort", bson.D{{"created", -1}}}} // 分组阶段:按chat_id分组,取每组最后一条(即最新)消息 groupStage := bson.D{{ Key: "$group", Value: bson.D{ {Key: "_id", Value: "$chat_id"}, // 简化分组id为chat_id字符串 {Key: "latestMsg", Value: bson.D{{"$last", "$$ROOT"}}}, }, }} // 替换根节点:将分组后的latestMsg作为根文档,结构与Message匹配 replaceRootStage := bson.D{{ Key: "$replaceRoot", Value: bson.D{{Key: "newRoot", Value: "$latestMsg"}}, }} // 正确的聚合阶段顺序 cursor, err := db.Aggregate(context.Background(), mongo.Pipeline{matchStage, sortStage, groupStage, replaceRootStage}) if err != nil { return nil, err } defer cursor.Close(context.Background()) var messages []*Message err = cursor.All(context.Background(), &messages) if err != nil { fmt.Print("Cursor err: ", err) return nil, err } return messages, nil }
修复后验证结果
返回结构与期望一致:
[ { "_id": {"$oid": "63ac607f141f0526cba7113d"}, "id": "jhjVxQUzQbPCLebnupS9", "chat_id": "dtlzoD9fI15q8_YM6eqp", "body": "Certainty determine at of arranging perceived situation or. Or wholly pretty county in oppose. Favour met itself wanted settle put garret twenty. In astonished apartments resolution so an it. Unsatiable on by contrasted to reasonable companions an. On otherwise no admitting to suspicion furniture it. ", "created": 1672241279354 }, { "_id": {"$oid": "63ac61d71a11d3b05340c001"}, "id": "wsBFKoN51q5v4Nnv3A-B", "chat_id": "unps_ZwBZ7mCubC3TsKl", "body": "Certainty determine at of arranging perceived situation or. Or wholly pretty county in oppose. Favour met itself wanted settle put garret twenty. In astonished apartments resolution so an it. Unsatiable on by contrasted to reasonable companions an. On otherwise no admitting to suspicion furniture it. ", "created": 1672241623466 } ]
内容的提问来源于stack exchange,提问作者alex
相关产品推荐
相关产品推荐

