如何在获取Mongoose文档后选择字段?去除_id返回客户端
如何在返回Mongoose文档给客户端时去除_id字段?
嘿,你现在手动把文档里的_id和敏感字段设为null的方式确实能解决问题,但其实Mongoose本身提供了更规范、更省心的实现方式,不用这么繁琐地遍历修改~下面给你几种更专业的方案:
方案1:Schema定义时全局配置转换规则(最推荐)
这是一劳永逸的方式,直接在Schema里配置toJSON和toObject的转换逻辑,以后每次把文档转成JSON或普通对象时,都会自动帮你过滤掉指定字段:
const VKAccountSchema = new mongoose.Schema({ // 这里写你的VKAccount字段定义 settings: { Account: String, access_token: String, // 其他settings相关字段 }, Sources: [{ // 这里写Sources数组的子文档字段定义 settings: { Account: String, access_token: String, // 其他子settings字段 } }] }, { // 配置转JSON时的处理逻辑 toJSON: { transform(doc, ret) { // 删除顶层的_id delete ret._id; // 清理settings里的敏感字段和_id delete ret.settings._id; delete ret.settings.Account; delete ret.settings.access_token; // 遍历Sources数组,逐个清理子文档 ret.Sources.forEach(source => { delete source._id; delete source.settings._id; delete source.settings.Account; delete source.settings.access_token; }); return ret; } }, // 如果需要把文档转成普通对象时也生效,加上toObject配置 toObject: { transform(doc, ret) { // 和上面toJSON的逻辑完全一致 delete ret._id; delete ret.settings._id; delete ret.settings.Account; delete ret.settings.access_token; ret.Sources.forEach(source => { delete source._id; delete source.settings._id; delete source.settings.Account; delete source.settings.access_token; }); return ret; } } });
之后不管你是直接把文档作为Express响应返回(框架会自动调用toJSON),还是手动调用doc.toJSON(),都会得到已经清理好的对象,不用再每次手动处理。
方案2:查询时用投影临时排除字段
如果只是某个特定查询需要过滤这些字段,不想修改Schema的话,可以在find方法里直接指定投影规则:
VKAccount.find({}, '-_id -settings._id -settings.Account -settings.access_token -Sources._id -Sources.settings._id -Sources.settings.Account -Sources.settings.access_token') .then(cleanedAccounts => { res.send(cleanedAccounts); }) .catch(err => { // 这里处理查询错误 });
投影字符串里的-前缀表示排除该字段,这种方式适合临时需求,灵活但需要每次查询都写清楚规则。
方案3:用工具库简化手动处理(适合特殊场景)
如果因为某些限制不能用上面两种方法,也可以用lodash这类工具库来简化你的手动处理逻辑,比逐个设null更优雅:
const _ = require('lodash'); const processedSources = { VK_Accounts: sources.VK_Accounts.map(account => { // 先把Mongoose文档转成普通对象,再移除顶层_id const cleanedAccount = _.omit(account.toObject(), '_id'); // 清理settings字段 cleanedAccount.settings = _.omit(cleanedAccount.settings, ['_id', 'Account', 'access_token']); // 遍历清理Sources数组里的每个子文档 cleanedAccount.Sources = cleanedAccount.Sources.map(source => { const cleanedSource = _.omit(source, '_id'); cleanedSource.settings = _.omit(cleanedSource.settings, ['_id', 'Account', 'access_token']); return cleanedSource; }); return cleanedAccount; }) };
这种方式代码可读性更高,也不容易漏掉需要清理的字段。
总的来说,最推荐方案1,一次配置终身受益;临时需求用方案2;特殊场景下再考虑方案3。
内容的提问来源于stack exchange,提问作者Олег Михайлов
相关产品推荐
相关产品推荐

