在 MongoDB 聚合框架中,$split 操作符用于将一个字符串按照指定分隔符拆分成数组。它属于字符串表达式操作符,语法非常简洁,但背后有一些隐式行为需要开发者留意。本文将从基本语法、实际场景和常见误区的角度,完整梳理 $split 在聚合管道中的使用方式。

一、$split 操作符的基本语法与行为
$split 的基本语法形式为 { $split: [ <string expression>, <delimiter> ] }。第一个参数是要被分割的字符串表达式,可以是字段引用、字符串常量或其他返回字符串的表达式;第二个参数是分隔符,必须是一个字符串常量或返回字符串的表达式。执行后,$split 会返回一个由分割后的子字符串组成的数组。例如将 fullName 字段按空格拆分,可以得到名字数组。
需要注意的是,$split 的第二个参数不能是空字符串以外的正则表达式,传入正则会直接报错。如果分隔符是空字符串,$split 会把输入字符串按每个 UTF-8 字符切分,相当于生成一个字符数组。这个行为在处理单个字符提取场景时非常有用,但很多人会忽略它与普通按字符切分函数之间的差异。此外,当输入字段为 null 或缺失时,结果也会是 null,不会自动变成空数组。如果字段类型不是字符串,比如是数字或日期,聚合管道会抛出类型错误。
下面是一个最基本的拆分示例,假设 users 集合中有文档 { fullName: "Zhang San" }。
db.users.aggregate([
{
$project: {
nameParts: { $split: ["$fullName", " "] }
}
}
])
// 输出:{ nameParts: ["Zhang", "San"] }
如果希望按空字符串拆分成单个字符,可以这样写:
db.codes.aggregate([
{
$project: {
chars: { $split: ["$code", ""] }
}
}
])
// 输入:{ code: "a1" }
// 输出:{ chars: ["a", "1"] }
二、$split 的典型应用场景
在实际数据中,很多字段并不是规范的结构化数组,而是以逗号、竖线、分号等分隔的字符串。例如产品文档里的 tags 字段可能保存为 "mongodb,database,nosql"。如果直接查询或展示,这种字符串形式非常不便。使用 $split 可以在聚合管道中将其转换为数组,再配合 $unwind 展开成多条文档,或者用 $size 统计标签数量。
另一个常见需求是拆分姓名。比如一个 姓名 字段保存了 "Zhang San" 这样的全名,业务上需要分别取出姓和名。可以先用 $split 按空格拆分,再用 $arrayElemAt 获取数组的第一个和第二个元素。这种操作在数据清洗和数据迁移过程中特别实用,可以减少应用层多次读写数据库的开销。
日志分析场景也经常用到 $split。例如访问日志中一段内容可能是 "timestamp|level|message" 的形式,在聚合管道中先按竖线拆分,再按索引提取级别和消息内容,可以迅速把非结构化日志转成结构化字段。下面是一个解析产品标签并统计第一个标签的示例:
db.products.aggregate([
{
$project: {
tagArray: { $split: ["$tags", ","] }
}
},
{
$project: {
firstTag: { $arrayElemAt: ["$tagArray", 0] },
tagCount: { $size: "$tagArray" }
}
}
])
// 输入:{ tags: "mongodb,database,nosql" }
// 输出:{ firstTag: "mongodb", tagCount: 3 }
使用 $split 的另一个好处是它完全在数据库侧执行,能够避免把大量原始字符串数据传输到应用服务器再拆分。特别是在分片集群或大数据量集合中,数据库内完成字段标准化能够明显减少网络传输和客户端内存占用。
三、$split 与相关操作符的组合使用
$split 本身只负责拆分,实际业务往往还需要对拆分结果做进一步清洗。由于 $split 不会自动去除元素两端的空白,当分隔符两侧存在空格时,结果数组里会残留空格。例如 "a, b, c" 按照逗号拆分后得到 ["a", " b", " c"],直接使用可能引起匹配失败。此时可以结合 $map 和 $trim 对每个元素进行清理。
下面的示例展示了如何先拆分带空格的标签字符串,再用 $map 去除每个标签首尾空白:
db.inventory.aggregate([
{
$project: {
cleanedTags: {
$map: {
input: { $split: ["$tags", ","] },
as: "tag",
in: { $trim: { input: "$$tag" } }
}
}
}
}
])
// 输入:{ tags: " apple, banana , orange " }
// 输出:{ cleanedTags: ["apple", "banana", "orange"] }
如果需要过滤掉空字符串或某些无效元素,可以在拆分后使用 $filter 操作符。例如某些分隔字符串中可能存在连续逗号,$split 会生成空字符串元素。通过 $filter 可以只保留非空且长度大于零的数组项。这样能够让后续的 $unwind 或 $in 查询更加干净。
还要注意,$split 不支持正则表达式分隔符。如果遇到需要按多种标点或大小写不敏感的方式拆分,通常的做法是先使用 $toLower 转换大小写,再用固定分隔符拆分,或者使用 $regexFindAll 提取所有匹配片段。直接使用正则表达式作为 $split 的参数会导致错误,这是设计上的限制,不是语法问题。
四、常见错误与性能优化建议
最常见的错误是字段类型不一致。当集合中某些文档的 notes 字段是字符串,而另一些是 null 或数字时,直接使用 $split 会让整条聚合管道在执行到非字符串文档时失败。为了避免这种情况,可以先使用 $type 判断字段类型,再配合 $cond 决定是否执行拆分。如下所示:
db.collection.aggregate([
{
$project: {
parts: {
$cond: [
{ $eq: [ { $type: "$notes" }, "string" ] },
{ $split: ["$notes", "|"] },
[]
]
}
}
}
])
这段管道在 notes 不是字符串时返回空数组,而不是直接抛出异常。对于字段缺失的情况,也可以使用 $ifNull 设置默认值,再交给 $split 处理。需要注意 $type 返回的类型名称必须与 "string" 匹配,不要误写成 "string" 之外的格式。
另一个容易忽略的问题是分隔符大小写敏感。例如按 "x" 拆分 "aXb" 不会产生任何拆分效果,因为小写 x 和大写 X 是不同的字符。如果业务上需要忽略大小写,应在拆分前先执行 $toLower 或 $toUpper 统一大小写。
从性能角度看,$split 属于聚合表达式计算,它会在每个匹配文档上执行。如果集合非常大,在没有筛选条件的情况下直接对整个集合做拆分和后续处理,会消耗大量 CPU 和内存。因此建议在管道早期使用 $match 缩小文档范围,或者利用索引过滤掉无关数据。对于频繁执行的拆分需求,也可以考虑在写入时将原始字符串预先转换为数组字段,避免每次查询都重复计算。
总体而言,$split 是 MongoDB 聚合管道中处理非结构化字符串的高效工具。理解它的返回行为、边界情况以及与其他数组操作符的组合方式,能够帮助开发者在数据清洗、日志解析和字段标准化等场景中写出更稳健、更高效的聚合管道。
MongoDB聚合管道$split字符串分割修改时间:2026-08-30 00:09:45