MongoDB如何通过子字符串匹配查询ObjectId类型的_id字段
MongoDB 按_id子串匹配的正确实现方式
默认情况下MongoDB集合的_id字段是ObjectId类型,属于BSON独立对象类型,并非字符串类型,而$regex正则操作符仅支持对字符串类型字段做匹配,因此你写的查询语句{_id: {$regex: '62a0ed7'}}无法拿到预期结果。
可落地的实现方案
方案1:前缀匹配优先用范围查询(性能最高,生产环境推荐)
如果你要匹配的是_id的前缀子串(比如示例里要找开头包含62a0ed7的_id),不需要做全表正则,直接构造ObjectId的范围区间即可,能直接命中_id的默认索引,查询速度极快:
// 替换成你要匹配的子串 const matchPrefix = "62a0ed7" // 构造区间上下界 const lowerBound = ObjectId(matchPrefix.padEnd(24, "0")) const upperBound = ObjectId(matchPrefix.padEnd(24, "f")) // 执行查询 db.你的集合名称.find({ _id: { $gte: lowerBound, $lt: upperBound } })
注意:该方案仅适用于前缀匹配场景,无法匹配_id中间、末尾位置的子串。
方案2:任意位置子串匹配(MongoDB 4.4+版本)
4.4及以上版本支持在$expr中使用聚合正则运算符,直接在查询阶段把_id转为字符串后做匹配,写法更简洁:
db.你的集合名称.find({ $expr: { $regexMatch: { input: { $toString: "$_id" }, // 将ObjectId类型的_id转为字符串 regex: "62a0ed7" // 你要匹配的子串 } } })
方案3:任意位置子串匹配(MongoDB 4.4以下旧版本)
旧版本不支持在$expr中直接用正则匹配,可以通过聚合管道先转字符串再匹配:
db.你的集合名称.aggregate([ // 第一步:把_id转成字符串存为临时字段 { $addFields: { tmpIdStr: { $toString: "$_id" } } }, // 第二步:对临时字符串字段做正则匹配 { $match: { tmpIdStr: { $regex: "62a0ed7" } } }, // 第三步:去掉临时字段,返回原始文档结构 { $project: { tmpIdStr: 0 } } ])
性能注意事项
所有需要将ObjectId转为字符串再做正则匹配的查询,都无法命中_id的默认索引,集合数据量较大时会触发全表扫描,性能很差。如果业务中频繁需要按_id的子串做查询,建议写入文档时就冗余一个字符串类型的_id字段,给该字段建立索引后再做正则查询,能大幅提升查询效率。
内容的提问来源于stack exchange,提问作者conorobdev
相关产品推荐
相关产品推荐

