
本文详解如何使用MongoDB聚合管道筛选指定campaignID的文档,并按嵌套在entries数组中的questionscorrect字段降序排序,同时保持外层文档结构完整——核心在于合理组合$match、$unwind、$sort与$group阶段。
本文详解如何使用mongodb聚合管道筛选指定campaignid的文档,并按嵌套在entries数组中的questionscorrect字段降序排序,同时保持外层文档结构完整——核心在于合理组合$match、$unwind、$sort与$group阶段。
在MongoDB中,当需对嵌套在数组内的字段进行排序(如本例中entries.questionscorrect),直接使用.find().sort()无法生效——因为sort()作用于顶层文档,而questionscorrect位于entries数组内部,属于“多级路径”数据。此时必须借助聚合管道(Aggregation Pipeline),通过结构化阶段逐层处理。
✅ 正确解法:四阶段聚合流水线
以下为标准MongoDB Shell语法的完整聚合查询:
db.getCollection('main').aggregate([
// 阶段1:精准筛选 campaignID = 12 的文档(推荐加索引提升性能)
{ $match: { "campaignID": 12 } },
// 阶段2:展开 entries 数组,将每个元素转为独立文档流
{ $unwind: "$entries" },
// 阶段3:按嵌套字段排序(支持点号路径语法)
{ $sort: { "entries.questionscorrect": -1 } },
// 阶段4:重新聚合成原始结构,仅保留 top 10 条 entries
{ $group: {
_id: "$_id",
"campaignID": { $first: "$campaignID" },
"name": { $first: "$name" },
"description": { $first: "$description" },
"entries": { $push: "$entries" }
}
},
// 阶段5(可选):截取前10条 entries(注意:$limit作用于分组后文档数,非entries数组长度)
{ $addFields: { "entries": { $slice: ["$entries", 10] } } }
])
? 关键说明:
"$entries"是数组字段名,$unwind将其“扁平化”,使每条entry成为独立处理单元;- 排序字段写为
"entries.questionscorrect"(带引号的字符串路径),MongoDB自动解析嵌套层级;$group中使用$first保留非数组字段(如name、description),用$push重建entries数组;- 最终通过
$addFields + $slice精确控制返回的entries数量($limit在此处无效,因它限制的是分组后文档总数,而非数组长度)。
⚠️ 注意事项与最佳实践
性能优化:为
campaignID字段建立索引(db.main.createIndex({ "campaignID": 1 })),确保$match阶段高效过滤;空数组/缺失字段防护:若
entries可能为空或缺失,添加$match: { "entries.0": { $exists: true } }在$unwind前避免报错;-
Go(mgo/mongo-go-driver)适配示例(以官方驱动为例):
pipeline := []bson.M{ {"$match": bson.M{"campaignID": 12}}, {"$unwind": "$entries"}, {"$sort": bson.M{"entries.questionscorrect": -1}}, {"$group": bson.M{ "_id": "$_id", "campaignID": bson.M{"$first": "$campaignID"}, "name": bson.M{"$first": "$name"}, "description": bson.M{"$first": "$description"}, "entries": bson.M{"$push": "$entries"}, }}, {"$addFields": bson.M{"entries": bson.M{"$slice": []interface{}{"$entries", 10}}}}, } cursor, _ := collection.Aggregate(context.TODO(), pipeline) 替代方案对比:
若仅需获取排序后的entries列表(不关心外层字段),可省略$group,直接$project提取所需字段;但本例强调“保持原始文档结构+top 10 entries”,故$group + $slice是唯一健壮解法。
掌握这一模式,即可轻松应对所有“外层筛选 + 内层排序 + 结构重组”的复杂场景——这正是MongoDB聚合框架区别于简单查询的核心价值:以声明式流水线,实现关系型数据库中需多表JOIN+子查询才能完成的分析逻辑。











