字符串字段需在字段定义中设 required: true 和 maxlength;日期默认值用 Date.now(不带括号);数组字段须声明 type: [String] 并设 default: [];自定义 validator 仅在 save/validate 时生效,update 需 runValidators: true。

Schema 中怎么写字符串长度限制和必填校验
字符串字段的 required 和 maxlength 必须写在字段定义对象里,不是挂在 Schema 构造函数上。漏掉 required: true 会导致空字符串或 null 被静默接受;只写 maxlength 不加 validate 或原生约束,MongoDB 层不会拦截超长内容——Mongoose 只在 save 前校验。
常见错误现象:save() 成功但数据库存了 200 字符的 title,而你期望最多 50 字。
-
required: true是布尔值,不是字符串"true" -
maxlength对中文也按字符数算(不是字节数),一个汉字 = 1 - 如果还要支持正则校验(比如邮箱格式),得额外加
validate函数,maxlength不管格式
const userSchema = new mongoose.Schema({
name: { type: String, required: true, maxlength: 50 },
email: {
type: String,
required: true,
validate: [/^\S+@\S+\.\S+$/, '邮箱格式不合法']
}
});
日期类型怎么设默认值才可靠
用 Date.now(不带括号)设默认值,而不是 new Date()。后者在 Schema 定义时就执行一次,所有文档共享同一个时间戳;前者每次新建文档时才调用,拿到实时时间。
使用场景:创建时间(createdAt)、最后更新时间(updatedAt)这类字段。
-
default: Date.now✅ 每次实例化都重新取当前时间 -
default: new Date()❌ Schema 编译时就固定了,所有 doc 都一样 - 如果想自动更新
updatedAt,得配合timestamps: true或中间件,单靠default不行
const postSchema = new mongoose.Schema({
title: String,
createdAt: { type: Date, default: Date.now }, // 注意没有 ()
updatedAt: { type: Date, default: Date.now }
}, { timestamps: true }); // 这个会自动管理 updatedAt
数组字段的类型声明和默认空数组陷阱
数组字段必须显式声明 type: Array 并用 items(或旧写法 of)指定元素类型,否则 Mongoose 不会校验数组内容,甚至允许 push 任意类型进去。
容易踩的坑:写成 tags: [String] 看似简洁,但 Mongoose 5.10+ 会警告并推荐用 { type: [String] };更关键的是,如果没设 default,该字段在 document 中是 undefined,不是空数组——导致 doc.tags.push() 直接报错。
- 安全写法:
tags: { type: [String], default: [] } - 嵌套对象数组要写全:
comments: { type: [{ author: String, text: String }], default: [] } -
required: true对数组意味着“不能是undefined或null”,但允许空数组[]
自定义 validator 和异步校验的边界在哪
Mongoose 的 validate 支持同步函数和返回 Promise 的异步函数,但仅限于 save() 和 validate() 调用时触发。它不会在 updateOne、findOneAndUpdate 等直接操作中运行——这些命令绕过 Mongoose 文档中间层,校验直接失效。
性能影响明显:异步 validator(比如查数据库去重)会让 save() 变慢,且无法并发控制;一旦出错,整个 save 中断,但事务里其他操作已提交的部分不会回滚。
- 唯一性校验别只靠
validate,必须加 MongoDB 唯一索引(unique: true)兜底 - 异步 validator 返回 Promise,拒绝时传 Error 实例,Mongoose 才能捕获为校验错误
- update 操作想跑校验,得用
runValidators: true选项,且只对 $set 等部分修改生效
username: {
type: String,
validate: {
validator: async function(v) {
const count = await this.constructor.countDocuments({ username: v });
return count === 0;
},
message: '用户名已被占用'
}
}
Mongoose 的校验逻辑只活在 document 实例的生命周期里,脱离这个上下文(比如 bulkWrite、aggregate、原生 driver 操作),所有 schema 约束都形同虚设。真正起作用的永远是 MongoDB 自身的索引和 validator,Mongoose 只是第一道软关卡。

















