MongoDB 3.6版本下如何查询files集合中filename无对应records集合_id的文档
MongoDB 3.6 跨集合无匹配文档查询实现
核心场景说明
- 关联逻辑:
files.filename存储的是对应records._id的字符串形式,目标是找出files集合中,值在records._id范围内没有匹配项的所有文档 - 版本约束:MongoDB 3.6 不支持4.0+版本才引入的聚合类型转换操作符,且MongoDB为严格类型匹配,字符串和ObjectId类型就算内容完全一致,直接做等值匹配也会查不到结果
适配3.6版本的可行方案
方案1:客户端过滤(推荐新手使用,兼容性最好)
操作步骤全程在mongo shell中即可完成,无任何语法兼容问题:
- 提取所有
records集合的_id对应的字符串值,生成匹配列表// 拉取records集合所有_id值,通过ObjectId自带的str属性转成字符串列表 const recordIdStrList = db.records.find({},{_id: 1}).map(doc => doc._id.str) - 基于列表查询
files集合中的无匹配文档// 查询filename不在上述匹配列表中的所有files文档 db.files.find({ filename: { $nin: recordIdStrList } })
适用场景与注意事项
- 适合单集合数据量在10万条以内的场景,操作简单不易出错
- 如果
records集合数据量过大,导致生成的字符串列表大小超过MongoDB单文档16MB的限制,需要分批拉取列表再逐次做匹配 - 建议提前给
files.filename字段建立普通索引,大幅提升$nin查询的性能
方案2:服务端聚合优化(不推荐3.6版本使用)
MongoDB 3.6虽然引入了带自定义管道的$lookup关联能力,但因为缺少跨类型转换的表达式支持,无法直接对ObjectId和字符串类型的字段做等值关联,强行写关联逻辑会因为类型不匹配返回空结果。如果后续升级到4.0及以上版本,可直接在服务端完成关联计算,减少客户端内存消耗,参考语法(不适用于3.6版本):
db.files.aggregate([ { $lookup: { from: "records", let: { fileFname: "$filename" }, pipeline: [ { $match: { $expr: { $eq: [ { $toObjectId: "$$fileFname" }, "$_id" ] } } } ], as: "matchedRecords" } }, // 过滤出未匹配到任何records的files文档 { $match: { matchedRecords: { $size: 0 } } } ])
长期优化建议
如果后续需要频繁做两集合的关联查询,建议给files集合新增recordId字段,直接存储对应关联的records._id的ObjectId类型值,避免跨类型匹配的性能损耗,同时给该字段建立索引,可大幅提升后续关联查询的效率。
内容的提问来源于stack exchange,提问作者chimera_girl
相关产品推荐
相关产品推荐

