MongoDB聚合$lookup中使用$regexMatch实现子串匹配关联查询
MongoDB 关联匹配消息子串的聚合实现
直接用管道模式的$lookup就能在关联阶段同时完成外键匹配和消息正文的子串过滤,避免全量关联后再过滤的性能浪费,针对给出的测试数据和需求,可直接运行的聚合代码如下:
// 待匹配的目标子串 const searchKeyword = "day"; db.patients.aggregate([ { $lookup: { from: "messages", // 将当前患者文档的_id定义为关联管道内可引用的变量 let: { patientId: "$_id" }, pipeline: [ { $match: { $expr: { $and: [ // 关联条件:消息由当前患者创建 { $eq: ["$createdByPatient", "$$patientId"] }, // 子串匹配:消息正文包含目标关键词,需要忽略大小写可追加options: "i" { $regexMatch: { input: "$body", regex: searchKeyword } } ] } } }, // 单患者有多条匹配消息时,取最新的1条(ObjectId自带时间戳,按_id倒序即按创建时间倒序) { $sort: { _id: -1 } }, { $limit: 1 } ], as: "matchedMessages" } }, // 过滤掉没有任何匹配消息的患者 { $match: { "matchedMessages.0": { $exists: true } } }, // 调整输出结构,对齐预期的lastMessage字段格式 { $addFields: { lastMessage: { $arrayElemAt: ["$matchedMessages", 0] } } }, // 移除中间过程生成的临时字段 { $project: { matchedMessages: 0 } } ])
关键实现说明
- 没有使用
localField/foreignField的简化$lookup写法,是因为简化写法不支持在关联时追加自定义过滤条件。通过let传参的管道式关联,可以在拉取messages表数据时直接完成双重过滤,只查询符合要求的消息,性能远高于先全量关联再过滤的写法。 $regexMatch是MongoDB 4.2及以上版本原生支持的聚合正则操作符,直接在管道内完成子串匹配,执行效率远高于$where类的脚本查询。- 关联管道内的
$sort + $limit是为了适配单患者多条匹配消息的场景,直接在关联阶段取到最新的匹配消息,避免后续处理大数组的开销。如果业务需要返回患者所有匹配的消息,删掉这两个阶段,调整后续投影逻辑直接返回关联得到的消息数组即可。 - 过滤无匹配患者时判断
matchedMessages.0是否存在,比用$size计算数组长度的执行效率更高,不需要遍历整个数组。
以上述测试数据为例,传入子串day执行聚合,返回结果和预期输出完全一致。
内容的提问来源于stack exchange,提问作者insivika
相关产品推荐
相关产品推荐

