news 2026/8/6 19:58:54

MongoDB 之 aggregate $lookup 关联查询 进阶篇

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
MongoDB 之 aggregate $lookup 关联查询 进阶篇

需求:

要对 topics é—®é¢˜è¡¨æ ¹æ® teach_id(教师ID),tips._idï¼ˆé—®é¢˜æ ‡ç­¾ID) å­—æ®µè¿›è¡Œåˆ†ç»„ç»Ÿè®¡æ•™å¸ˆçš„ç­”é¢˜æƒ å†µç”ŸæˆæŠ¥è¡¨ï¼Œæ‰€ä»¥å¾—æ ¹æ® 教师IDï¼Œæ ‡ç­¾ID å ³è”æŸ¥è¯¢å‡º æ•™å¸ˆä¿¡æ¯ï¼Œæ ‡ç­¾ä¿¡æ¯ 用作报表展示。

本文基于 MongoDB V3.2 聚合查询语法,V3.6 可使用更多的特性实现

topics 表数据结构:

{

"_id" : "ffffea46-ab7d-4c19-afa4-fe37709d5300",

"evaluation_type" : 3,

"type" : 3,

"content" : "一般纳税人怎么结转成本",

"tips" : [

{

"_id" : 1000,

"tip_name" : "会计基础"

}

],

"update_at" : NumberLong("1469801351305"),

"author_id" : 10334093,

"timestamp" : NumberLong("1469714932097"),

"race_time" : NumberLong("1469714947334"),

"teach_id" : 10122995,

"last_reply_content" : "è¿™å°±è¦çœ‹ä½ ç”¨çš„æ˜¯ä»€ä¹ˆæˆæœ¬è®¡ä»·æ–¹æ³•ã€‚",

"last_reply_at" : NumberLong("1469715279859"),

"end_type" : 2

}

从数据结构中可以知道第一步操作是:

æ ¹æ® teach_id,tips._id 字段进行分组,

可得 group(分组) 语句:

$group:{

_id:{"teacherId":"$teach_id","tipId":"$tips._id"},

evaluationTypes:{

$push:"$evaluation_type"

},

sum: {$sum: 1}

}

$push官方文档(https://docs.mongodb.com/manual/reference/operator/update/push/index.html)

$push:可以分组之后把 evaluation_type 字段值 赋给 evaluationTypes åŠ å ¥åˆ°åˆ†ç»„ä¹‹åŽçš„è¿”å›žå€¼ä¸­

$sum官方文档(https://docs.mongodb.com/manual/reference/operator/aggregation/sum/index.html)

$sum:可得到 teach_id,tips._id 都相同的记录条数

使用脚本:

db.getCollection("topics").aggregate([** {

$match:{

"race_time" : {"$gte":1522512000000,"$lte":1525103999000},

"type":3

}

},

{

$group:{

_id:{"teacherId":"$teach_id","tipId":"$tips._id"},

evaluationTypes:{

$push:"$evaluation_type"

},

sum: {$sum: 1}

}

}

])

查询结果:**

从结果可以看出 教师:10706280 åœ¨è¿™ä¸ªæ ‡ç­¾ï¼š1002 ä¸‹ä¸€å ±æŠ¢ç­”äº† 4 条问题,以及问题的 evaluationTypes(评价类型)

一个分组作为报表的一条记录,所以我们得计算报表的总记录数(总行数)totalï¼Œæ‰€ä»¥å†å¯¹åˆ†å¥½ç»„çš„è®°å½•è¿›è¡Œæ— æ¡ä»¶åˆ†ç»„ï¼ˆ_id: null)**

使用脚本:**

db.getCollection("topics").aggregate([

{

$match:{

"race_time" : {"$gte":1525103910000,"$lte":1525103999000},

"type":3

}

},

{

$group:{

_id:{"teacherId":"$teach_id","tipId":"$tips._id"},

evaluationTypes:{

$push:"$evaluation_type"

},

sum: {$sum: 1}

}

},

{

$group:{

_id: null,

list:{

$push:"$$ROOT"

},

total: {$sum: 1}

}

}

])

$$ROOT 指定的是分组前的所有数据,所以 list 会把第一次分组得到的数据都带上

total æ˜¯ç¬¬ä¸€æ¬¡åˆ†ç»„ä¹‹åŽçš„è®°å½•æ•°ï¼Œå³ä¸€å ±åˆ†äº†å¤šå°‘ç»„ï¼Œä¹‹åŽç”¨ä½œåˆ†é¡µæ€»æ•°

查询结果:

这里的 group 使用可参考:MongoDB 之 aggregate $group 巧妙运用

教师ID 以及 æ ‡ç­¾ID ç­‰ä¿¡æ¯åŒ å«åœ¨ list 集合里,所以需要对 list 进行扁平化($unwind)操作,可参考:MongoDB èšåˆåµŒå ¥çš„æ•°ç»„ï¼ˆæ‰å¹³åŒ–æ•°æ®+管道)

使用脚本:**

db.getCollection("topics").aggregate([

{

$match:{

"race_time" : {"$gte":1522512000000,"$lte":1525103999000},

"type":3

}

},

{

$group:{

_id:{"teacherId":"$teach_id","tipId":"$tips._id"},

evaluationTypes:{

$push:"$evaluation_type"

},

sum: {$sum: 1}

}

},

{

$group:{

_id: null,

list:{

$push:"$$ROOT"

},

total: {$sum: 1}

}

},

{

$unwind:"$list"

},

{

$sort: {"list.sum": -1}

},

{

$skip: 0

},

{

$limit: 100

}

])

查询结果:**

扁平化 list é›†åˆæ•°æ®ä¹‹åŽï¼Œå¯æ ¹æ® 教师IDï¼Œæ ‡ç­¾ID å ³è”æŸ¥è¯¢ï¼ˆ$lookupï¼‰å‡ºæ•™å¸ˆä¿¡æ¯ï¼Œæ ‡ç­¾ä¿¡æ¯

$lookup:

{

from:"users",

localField:"list._id.teacherId",

foreignField: "_id",

as: "users"

}

$lookup(官方文档--https://docs.mongodb.com/manual/reference/operator/aggregation/lookup/index.html)

3.2版本 åªèƒ½æ ¹æ® localField,foreignField å•æ¡ä»¶å ³è”ï¼Œ

3.6版本中 å ³è”æŸ¥è¯¢å¼•å ¥ pipeline ç®¡é“æ”¯æŒæ›´å¤šæ›´ä¸°å¯Œçš„æŸ¥è¯¢ï¼Œå¯¹åº”æœ¬æ–‡ä¼šæœ‰æ›´å¥½çš„è§£å†³æ–¹æ¡ˆã€‚å ·ä½“ä½¿ç”¨å¯ä»¥æŸ¥çœ‹å®˜æ–¹æ–‡æ¡£

ä¹‹åŽä¾¿æ˜¯å¯¹æ•°æ®çš„å¤šæ¬¡å ³è”æŸ¥è¯¢ï¼Œä»¥åŠæ‰å¹³åŒ–æ“ä½œï¼Œä¸‹é¢è´´ä¸Š

完整查询脚本:

db.getCollection("topics").aggregate([** {

$match:{

"race_time" : {"$gte":1522512000000,"$lte":1525103999000},

"type":3

}

},

{

$group:{

_id:{"teacherId":"$teach_id","tipId":"$tips._id"},

evaluationTypes:{

$push:"$evaluation_type"

},

sum: {$sum: 1}

}

},

{

$group:{

_id: null,

list:{

$push:"$$ROOT"

},

total: {$sum: 1}

}

},

{

$unwind:"$list"

},

{

$sort: {"list.sum": -1}

},

{

$skip: 0

},

{

$limit: 100

},

{

$lookup:

{

from:"users",

localField:"list._id.teacherId",

foreignField: "_id",

as: "users"

}

},

{

$lookup:

{

from:"tip_content",

localField:"list._id.tipId",

foreignField: "_id",

as: "childTip"

}

},

{

$lookup:

{

from:"tip_content",

localField:"childTip.parent_tip_id",

foreignField: "_id",

as: "parentTip"

}

},

{

$unwind:"$users"

},

{

$unwind:"$childTip"

},

{

$unwind:"$parentTip"

},

{

$project:{

"teacherId":"$list._id.teacherId",

"teacherName":"$users.real_name_from_nc",

"tuid":"$users.tuid",

"mobile":"$users.mobile",

"tipId":"$childTip._id",

"tipName":"$childTip.tip_name",

"parentTipId":"$parentTip._id",

"parentTipName":"$parentTip.tip_name",

"sum":"$list.sum",

"total":"$total",

"evaluationTypes":"$list.evaluationTypes",

"_id":0

}

}

])

查询结果:**


作者:molashaonian

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/6 19:58:39

highlight主题推荐:10款热门CSS主题对比与选择指南

highlight主题推荐:10款热门CSS主题对比与选择指南 【免费下载链接】highlight Fast, extensible, server-side code highlighting for web and terminal 项目地址: https://gitcode.com/gh_mirrors/high/highlight highlight是一款快速、可扩展的服务器端代…

作者头像 李华
网站建设 2026/8/6 19:58:21

哪家的办公文档加密软件比较好?

企业选购办公文档加密软件,最怕遇到三个问题:加密拖慢效率,员工抵触;权限管控粗放,核心数据“一刀切”式开放;文件发给供应商后完全失控,被二次扩散也无从知晓。据统计,68%的企业曾遭…

作者头像 李华
网站建设 2026/8/6 19:58:15

1000fps帧率的高灵敏短波红外相机

高速工业检测、微观瞬态科研实验等场景需要捕捉快速演变的动态过程,对成像设备的帧率、灵敏度有着严苛要求。短波红外相机的帧率受限于传感器读出速度和数据传输带宽,目前多数短波红外相机的全幅帧率大多维持在100–400fps,难以满足超高速动态…

作者头像 李华
网站建设 2026/8/6 19:57:01

分享职业技术证书|Python技术应用工程师证书报考条件指南

从互联网巨头到传统制造业,从金融风控到智慧医疗,Python凭借其简洁的语法、强大的库生态和跨领域的适用性,已经成为最炙手可热的编程语言。然而,技能是需要培养的,如何能够建立起自己的技能体系和证明自己具备Python技…

作者头像 李华
网站建设 2026/8/6 19:52:09

从入门到精通:NeoTerm终端会话管理与多任务处理技巧

从入门到精通:NeoTerm终端会话管理与多任务处理技巧 【免费下载链接】NeoTerm 👍 modern terminal emulator for Android 项目地址: https://gitcode.com/gh_mirrors/ne/NeoTerm NeoTerm是一款现代化的Android终端模拟器,专为提升移动…

作者头像 李华