如何获取每个聊天室的最新单条消息?聚合与循环查询对比
问题
我希望获取每个聊天室的最新用户消息数组,但当前实现的Go语言MongoDB查询代码返回的是所有聊天室的全部消息。现有代码如下:
func (r *Mongo) findLastMessages(ctx context.Context, chatIds []string) ([]*Message, error) { if len(chatIds) == 0 { return nil, nil } query := bson.M{"chat_id": bson.M{"$in": chatIds}} cursor, err := r.colMessage.Find(ctx, query, nil) if err != nil { return nil, err } var messages []*Message if err = cursor.All(ctx, &messages); err != nil { return nil, err } err = cursor.Close(ctx) if err != nil { return nil, ErrInternal } return messages, err }
请问如何过滤以得到每个聊天室的最后一条消息?另外,是否应使用聚合查询?若使用聚合,相比循环Find哪种方式更优?
解决方案
1. 用聚合查询获取每个聊天室的最新消息
推荐使用MongoDB聚合框架,直接在数据库层面完成筛选、排序和分组,得到每个聊天室的最新消息。修改后的代码如下:
func (r *Mongo) findLastMessages(ctx context.Context, chatIds []string) ([]*Message, error) { if len(chatIds) == 0 { return nil, nil } pipeline := []bson.M{ // 筛选指定聊天室的所有消息 {"$match": bson.M{"chat_id": bson.M{"$in": chatIds}}}, // 按消息创建时间倒序排序,确保最新消息排在首位 {"$sort": bson.M{"created_at": -1}}, // 按chat_id分组,取每组第一条(最新)消息 {"$group": bson.M{ "_id": "$chat_id", "lastMessage": bson.M{"$first": "$$ROOT"}, }}, // 将分组后的消息内容作为根文档,匹配原Message结构 {"$replaceRoot": bson.M{"newRoot": "$lastMessage"}}, } cursor, err := r.colMessage.Aggregate(ctx, pipeline) if err != nil { return nil, err } defer cursor.Close(ctx) var messages []*Message if err = cursor.All(ctx, &messages); err != nil { return nil, err } return messages, nil }
2. 聚合查询 vs 循环Find的优劣对比
聚合查询是更优的选择,理由如下:
- 减少网络开销:聚合仅需一次数据库请求,直接返回最终结果;循环Find需要为每个chatId发起一次请求,chatIds数量越多,网络延迟和开销越大。
- 降低应用层压力:聚合在数据库端完成数据筛选和分组,应用层无需处理大量冗余数据,节省内存和CPU资源。
- 保证一致性:聚合是单次原子操作,避免循环查询过程中数据更新导致的结果不一致问题。
仅当chatIds数量极少(比如个位数)时,循环Find的性能差异可忽略,但从扩展性和长期性能来看,聚合查询是更合理的方案。
内容的提问来源于stack exchange,提问作者alex
相关产品推荐
相关产品推荐

