Mongoose嵌套Schema仅存字段值,如何添加手机号/邮箱验证状态?
首先,咱们先搞定你当前遇到的verified字段没存入数据库的问题,再聊聊更贴合业务的验证状态标记方案。
一、为什么verified字段没保存?怎么修复?
你的Schema嵌套结构定义是没问题的,大概率是创建用户时的数据结构不匹配,或者有中间件误修改了数据,按下面的步骤排查:
1. 确保创建文档时用正确的嵌套结构
phone和email是嵌套对象,所以创建用户时必须把number/address(以及可选的verified)放在对应的嵌套层级里:
// 正确的创建示例 const newUser = new User({ username: "terryjia", phone: { number: "13800138000", // 不手动设置的话,Mongoose会自动用default: false // verified: true }, email: { address: "terry@example.com" } }); newUser.save() .then(savedUser => { console.log(savedUser); // 这里应该能看到phone.verified和email.verified字段 }) .catch(err => console.error(err));
如果你的代码里把手机号/邮箱作为顶级字段传递(比如phoneNumber: 'xxx'),完全不匹配Schema结构,自然不会生成verified字段,这是最常见的错误。
2. 检查pre-save中间件是否误删字段
如果你的Schema或模型定义了pre('save')中间件,排查有没有不小心删除verified的逻辑:
// 错误示例:中间件误删了verified字段 UserSchema.pre('save', function(next) { delete this.phone.verified; // 这种操作会导致字段丢失 next(); });
3. 给verified字段加required: true(可选)
为了确保这个字段一定会被存入数据库,可以给verified加上required: true,强制Mongoose生成该字段:
phone: { verified: { type: Boolean, default: false, required: true // 确保字段必须存在 }, number: { /* ... */ } }
另外还有个小细节:你的dateCreated默认值写的是Date.now(),这会导致所有新文档的创建时间都是服务器启动的时间,应该改成函数形式,每次创建文档时才获取当前时间:
dateCreated: { type: Date, default: () => Date.now() // 或者直接写 default: Date.now }
二、更优的验证状态标记方案
根据你的业务需求,有几种比嵌套对象更灵活的方案:
方案1:扁平化字段(最简单直观)
如果phone/email只有验证状态和值两个属性,直接扁平化字段会更方便查询和更新:
const UserSchema = mongoose.Schema({ username: { type: String, required: true, unique: [true,'Sorry username is taken please try another'] }, // 手机号相关 phoneNumber: { type: String, required: true, unique: [true,'Mobile already registered'] }, isPhoneVerified: { type: Boolean, default: false }, phoneTypes: Array, // 邮箱相关 emailAddress: { type: String, required: true, unique: [true,'Email already registered'] }, isEmailVerified: { type: Boolean, default: false }, emailTypes: Array, dateCreated: { type: Date, default: Date.now } });
优点:结构简单,更新验证状态时直接写User.updateOne({_id: userId}, {isPhoneVerified: true}),比嵌套的phone.verified更直观;查询也更简洁。
方案2:子文档数组(支持多联系方式)
如果业务允许用户绑定多个手机号/邮箱,用子文档数组是更好的选择,每个联系人条目包含值、验证状态和类型:
// 定义联系方子文档Schema const ContactSchema = mongoose.Schema({ value: { type: String, required: true, unique: true }, verified: { type: Boolean, default: false }, type: { type: String, enum: ['phone', 'email'], // 限制类型只能是phone或email required: true } }); const UserSchema = mongoose.Schema({ username: { type: String, required: true, unique: [true,'Sorry username is taken please try another'] }, contacts: [ContactSchema], // 存储所有联系方式 dateCreated: { type: Date, default: Date.now } });
这种方式扩展性极强,用户可以添加多个手机号或邮箱,每个都有独立的验证状态,适合需要多联系人的业务场景。
方案3:保留嵌套结构(适合单联系方式场景)
如果坚持要用原来的嵌套结构,确保默认值和必填项设置正确,同时严格按照结构传入数据即可,就像前面修复的那样。
内容的提问来源于stack exchange,提问作者Terry Jia

