$type仅匹配存在且类型符合的字段值,不匹配null或缺失字段;查数组元素类型须用$map+$type,不能直接用嵌套路径;类型混用需通过聚合管道$group+$addToSet统计分布来识别。

用 $type 查字段值类型,但不能直接查“类型不一致”
MongoDB 的 $type 本身只做单值类型匹配,比如查所有 age 是字符串的文档:{ age: { $type: "string" } }。它不提供“同一字段在不同文档中类型不同”的聚合判断能力——这得靠聚合管道组合实现。
常见错误是以为写个 { field: { $not: { $type: "int" } } } 就能揪出混杂类型,其实这只是排除整型,漏掉 null、missing、array 等其他可能,还无法统计分布。
- 字段缺失(即该键根本不存在)不会被
$type匹配到,需额外用$exists: false或$ne: null配合$ifNull -
$type接受数字码(如16)或字符串名(如"int"),但不同驱动/Shell 版本对字符串名支持不一,推荐优先用数字码(见官方类型码表) - 数组字段本身类型是
"array"(码4),但数组内元素类型需用$map+$type单独提取,不能一步到位
用 $group + $addToSet 统计字段类型分布
真正发现“类型不一致”,核心是先枚举该字段所有出现过的类型,再看是否多于一种。最稳妥方式是聚合管道中用 $type 提取类型,再用 $group 收集去重集合:
db.collection.aggregate([
{
$addFields: {
"field_type": { $type: "$field_name" }
}
},
{
$group: {
_id: null,
types: { $addToSet: "$field_type" },
count: { $sum: 1 }
}
}
])
如果返回的 types 数组长度 > 1,说明该字段存在类型混用。例如结果是 { types: ["int", "string", "null"] },就明确告诉你有三种类型。
- 注意
$type对null值返回"null"(码10),对缺失字段不生成值,所以要加$ifNull统一处理:{ $type: { $ifNull: ["$field_name", null] } } - 若字段含数组,
$type返回"array",但你可能更关心数组里是不是全为数字——这时得嵌套$map:{ $map: { input: "$field_name", in: { $type: "$$this" } } } - 大数据集慎用
$addToSet,它会把整个类型数组加载进内存;可先用$sample抽样探查,再决定是否全量扫描
用 $expr + $switch 标记异常类型组合
当需要快速标出“非预期类型”的文档(比如业务规定 price 只能是双精度或整型,但实际出现了字符串),可以用 $expr 在查询条件里做复合判断:
db.collection.find({
$expr: {
$in: [
{ $type: "$price" },
["string", "bool", "object", "array"]
]
}
})
这个查询直接返回所有 price 是字符串、布尔、对象或数组的文档,跳过正常类型。
-
$in比多个$or更简洁,且支持$type输出的字符串名 - 如果要包含
null和缺失,得显式加上:["string", "null"],并配合$or: [{ price: { $exists: false } }, { price: null }] - 索引对
$expr查询无效,这类扫描是全表级的,生产环境建议先建好{ price: 1 }索引加速基础过滤
为什么 $type 查不到 NumberLong 和 NumberInt 的区别?
这是最容易踩的坑:MongoDB Shell 中显示的 NumberLong(123) 或 NumberInt(456) 是客户端格式化写法,底层 BSON 类型都是 "long"(码 18)或 "int"(码 16),但 $type 在 4.4+ 版本才开始区分它们;旧版本一律返回 "number"(码 1)。
- 确认 MongoDB 版本:
db.version(),低于 4.4 的集群无法用$type精确识别int与long - 临时验证方法:用
Object.bsonType(Node.js 驱动)或 PyMongo 的type()手动 inspect 单个值 - 业务上若强依赖整型精度,建议统一用
NumberLong存储,避免 driver 自动降级导致的隐式类型变化
类型混用问题往往藏在历史数据迁移或多个服务共写一个集合时,光靠 $type 查单点没用,必须结合聚合统计和业务语义判断哪些“不一致”真会影响逻辑——比如 status: 0 和 status: "inactive" 在代码里可能都被当作假值,但数据库层面已是类型污染。


















