Promise循环中无法更新Mongoose文档的nextTick字段,求解决方案
解决Mongoose Promise循环中无法修改文档字段
nextTick的问题 你遇到的问题其实是Mongoose文档对象的特性导致的:你从查询里拿到的timer数组中的每个元素,是Mongoose的Document实例,不是普通的JavaScript对象。这类实例对已在Schema中定义的字段做了封装,直接赋值修改不会生效——因为访问这些字段时会调用Mongoose内置的getter方法,返回的还是原始的存储值(也就是你的时间戳)。而你新增的date字段因为Schema里没有定义,Mongoose不会对它做特殊处理,所以能正常赋值和读取。
下面给你几种可行的解决方案:
方案1:查询时直接返回普通JS对象(推荐)
在查询语句末尾加上.lean()方法,让Mongoose跳过文档实例的封装,直接返回纯JS对象。这样你就可以像修改普通对象一样修改nextTick字段了:
var queryPromise = cronie.find({}) .where('id').equals(req.user._id) .lean() // 关键:返回普通对象 .exec(); queryPromise.then(function(timer) { for (var i = 0, len = timer.length; i < len; i++) { const readableTime = moment.unix(timer[i].nextTick).format('dddd, MMMM Do, YYYY h:mm:ss A'); console.log(readableTime); timer[i].nextTick = readableTime; console.log(timer[i].nextTick); // 现在会输出转换后的字符串 } return timer; }).then(function(timer) { res.render('dash.ejs', { user: req.user, timer: timer }); });
方案2:将文档实例转为普通对象
如果因为某些原因不能用.lean(),可以在循环里把每个文档实例转为普通对象,再修改字段:
queryPromise.then(function(timer) { // 把每个Mongoose文档转为普通对象 const processedTimer = timer.map(doc => { const plainDoc = doc.toObject(); plainDoc.nextTick = moment.unix(plainDoc.nextTick).format('dddd, MMMM Do, YYYY h:mm:ss A'); return plainDoc; }); return processedTimer; }).then(function(timer) { res.render('dash.ejs', { user: req.user, timer: timer }); });
方案3:用Mongoose的set方法修改字段
如果你需要保留文档实例的其他功能(比如后续要保存到数据库),可以用Mongoose提供的set方法来修改字段:
queryPromise.then(function(timer) { for (var i = 0, len = timer.length; i < len; i++) { const readableTime = moment.unix(timer[i].nextTick).format('dddd, MMMM Do, YYYY h:mm:ss A'); timer[i].set('nextTick', readableTime); // 使用set方法赋值 console.log(timer[i].nextTick); // 现在会正确显示修改后的值 } return timer; }).then(function(timer) { res.render('dash.ejs', { user: req.user, timer: timer }); });
注意:如果你的Schema里nextTick定义的是Number类型,用这个方法修改成字符串后,后续如果要保存文档到数据库可能会触发类型校验错误——不过你这里只是用来渲染页面,所以完全没问题。
内容的提问来源于stack exchange,提问作者nrm
相关产品推荐
相关产品推荐

