MongoDB 4.0+ 推荐用 $substrCP,它按 Unicode 码点截取,对中文、emoji、带重音符号字母更安全;$substr 按字节截取,易因 UTF-8 多字节导致乱码,如 "$name", 0, 2 截 "你好" 可能只取半个汉字。

用 $substr 和 $substrCP 截取字符串时要注意什么?
MongoDB 4.0+ 推荐用 $substrCP,它按 Unicode 码点截取,对中文、emoji、带重音符号的字母(如 café)更安全;$substr 按字节截取,遇到 UTF-8 多字节字符容易切出乱码。比如对字符串 "你好" 用 $substr: ["$name", 0, 2] 可能只拿到半个汉字。
-
$substrCP三个参数:字段名、起始码点位置、截取码点数 - 起始位置从 0 开始,负数不支持(不像 JavaScript 的
slice) - 如果起始位置超出字符串长度,返回空字符串;截取长度超出剩余部分,自动截断
{
$project: {
shortName: { $substrCP: ["$fullName", 0, 5] }
}
}
大小写转换该选 $toUpper 还是 $toLower?
两者行为明确,没有兼容性陷阱,但要注意它们只处理 ASCII 和大多数常见 Unicode 字母(符合 Unicode 标准的 case mapping),不保证覆盖所有语言的特殊规则(比如土耳其语的 I/i 映射)。日常中英文、中文拼音、德法西语基本没问题。
- 必须传入字符串类型字段,若字段可能为
null或数字,先用$toString转换 - 不会修改原字段,需配合
$project或$addFields输出新字段 - 在
$group的_id中使用时,大小写敏感会影响分组结果(例如"Apple"和"apple"会被分为两组)
{
$project: {
upperName: { $toUpper: { $toString: "$name" } },
lowerEmail: { $toLower: "$email" }
}
}
想同时截取 + 转换,能不能链式写?
MongoDB 聚合表达式支持嵌套,不需要中间变量。把 $substrCP 当作 $toUpper 的输入即可,顺序按“最内层先执行”理解。
- 嵌套深度不限,但可读性会下降,建议单层嵌套为主
- 注意括号匹配和参数顺序,少一个逗号或错位就会报
Unrecognized expression - 如果截取后为空字符串,
$toUpper返回仍是空字符串,不会报错
{
$project: {
initials: {
$toUpper: { $substrCP: ["$fullName", 0, 2] }
}
}
}
为什么在 $match 阶段不能直接用 $toUpper 做模糊查询?
$match 是筛选阶段,只接受查询操作符(如 $eq、$regex),不支持聚合表达式。想实现“忽略大小写的字符串匹配”,得先在 $project 或 $addFields 中生成标准化字段(如 name_lower),再在后续 $match 中查它;或者改用 $regex 配合 i 选项(但无法利用索引加速)。
- 使用衍生字段 + 索引是最高效的做法,尤其数据量大时
-
$regex的i标志虽方便,但全表扫描风险高,且不能用普通 B-tree 索引优化大小写无关搜索 - 如果字段已建了小写索引(如用应用层存
name_lower),就别在聚合里重复计算
真正要小心的是:同一个字段反复做 $toLower + $substrCP + 比较,却没意识到每次调用都重新计算——这在长 pipeline 里可能成为性能盲点。











