使用Mongoose创建文档后如何仅返回指定字段?
问题解答
核心结论
select: false确实仅适用于查询类操作(比如find、findOne、findById等从数据库读取数据的方法),而save/create这类创建文档的操作,返回的是内存中的文档实例,不会自动应用select字段过滤规则,所以会包含所有字段,包括标记为隐藏的字段。
解决方案
下面提供几种获取"干净"文档的方法,按需选择:
方法1:创建后执行一次查询(最稳妥,严格遵循Schema规则)
创建文档后,用findById配合默认的select规则重新查询一次,返回结果会自动过滤掉select: false的字段:
const newUser = await UserModel.create({ nickname: "hello", email: "world", myHiddenField: "bluh bluh" }); // 重新查询获取过滤后的文档 const cleanUser = await UserModel.findById(newUser._id); return cleanUser.toObject();
方法2:配置Schema全局转换规则(一劳永逸)
在定义Schema时,添加toObject和toJSON的transform配置,让每次转换为普通对象时自动移除select: false的字段:
const { Schema, model } = require('mongoose'); const userSchema = new Schema({ nickname: { type: String, required: true }, email: { type: String, required: true }, myHiddenField: { type: String, required: true, select: false }, }, { // 配置toObject转换时自动清理隐藏字段 toObject: { transform: (doc, ret) => { Object.keys(doc.schema.paths).forEach(path => { if (doc.schema.paths[path].options.select === false) { delete ret[path]; } }); return ret; } }, // 配置toJSON转换时同样生效(比如接口返回JSON时) toJSON: { transform: (doc, ret) => { Object.keys(doc.schema.paths).forEach(path => { if (doc.schema.paths[path].options.select === false) { delete ret[path]; } }); return ret; } } }); const UserModel = model('user', userSchema); module.exports = UserModel;
之后再用save或create创建文档,调用toObject()就会直接得到干净的结果:
const userResult = (await UserModel.create({ nickname: "hello", email: "world", myHiddenField: "bluh bluh" })).toObject(); // userResult中不会包含myHiddenField return userResult;
方法3:手动编写清理函数(灵活可控)
如果不想修改Schema全局配置,可以写一个通用函数,根据Schema规则清理文档:
// 通用清理函数:移除所有select: false的字段 function cleanDocument(doc) { const docObj = doc.toObject(); Object.keys(doc.schema.paths).forEach(path => { if (doc.schema.paths[path].options.select === false) { delete docObj[path]; } }); return docObj; } // 使用示例 const user = await UserModel.create({ nickname: "hello", email: "world", myHiddenField: "bluh bluh" }); const cleanUser = cleanDocument(user); return cleanUser;
方法4:手动删除指定字段(简单直接,适合少量隐藏字段)
如果隐藏字段数量很少,也可以直接手动删除:
const userResult = (await UserModel.create({ nickname: "hello", email: "world", myHiddenField: "bluh bluh" })).toObject(); // 直接删除隐藏字段 delete userResult.myHiddenField; return userResult;
内容的提问来源于stack exchange,提问作者Shir Shtrnfld
相关产品推荐
相关产品推荐

