MrErHu/blog

Mongoose返回数据修改中遇到的坑

MrErHu opened this issue · 1 comments

之前接手了一个微信公众号的项目,项目的技术栈是: NodeJs+ExpressJs+mongo+mongoose+jade,第一次使用ODM(Object DatabaseManage)工具mongoose,发现使用起来还是非常方便的,你可以预先定义一个model,把mongo这种NoSql当做关系型数据库使用,并且通过populate能基本实现一些比较简单的连接查询 ,但是在使用的过程中也是踩了不少坑,举例讲一个比较典型的:对于mongoose返回数据的修改的问题。
假设我们需要涉及一个关于评论的model,我们用下面的代码举例:

var mongoose = require('mongoose');
var Schema = mongoose.Schema;

var CommentSchema = new Schema({
    topicId: {type: Schema.ObjectId, ref:'Topic',index: true},
    author: { type: Schema.ObjectId, ref: 'User'},
    group: {type: Number, default: 0, index: true},
    content: String,
    status: {type: Number, default: 0},
    createdAt: { type: Date, default: Date.now },
    favNum: {type:Number, default: 0}
});

var Comment = mongoose.model('Comment', CommentSchema);

module.exports = Comment;

上面的代码非常的简单明了,定义了model的各种字段、类型、引用,这样我们在代码中就可以通过mongoose对mongo进行查询了,有Java的经验老司机会发现这个工具和Hibernate非常相似。
假设现在我们有一个业务,我需要提供一个Restful Get接口,返回当前文章的评论信息,并且根据session判断登录用户,去对每条评论判断是否该登录用户已经点赞,那么我想要在返回的数据中临时增加一个字段isPraisetrue代表是当前登录用户已经点赞,false代表当前用户未点赞。
我们可能会有下面两种mongoose的代码风格

Comment.findOne({
    id: commentId,
},function (err,data) {
    if(){
        //如果用户点赞
        data.isPraise = true;
    }else{
        //如果用户未点赞
        data.isPraise = false;
    }

});

或者

Comment.findOne({
    id: commentId
}).exec(function (err,data) {
    if(){
        //如果用户点赞
        data.isPraise = true;
    }else {
        //如果用户未点赞
        data.isPraise = false;
    }

});

上面的代码看起来没有任何的问题,mongoose返回的是Object,我根据自己的业务新增属性,然后以JSON数据返回给用户。但是事实上新增加的属性不能被打印出来(通过console.log(JSON.stringify(data)))也不能返回给客户端,但是奇怪的是却可以打印出来console.log(data.isPraise),而且Object.prototype.hasOwnProperty()方法返回的也是true,说明确实已经增加了这个属性,为什么不会返回给请求方呢?
后面经过排查终于发现了问题的所在,实际上mongoose返回的数据并不是object,虽然你通过typeof判断类型是object,实际上是mongoose自己封装的一个对象,并且这个对象会对数据进行实时查询以保证其符合预定义的model,因为model中压根就没有isPraise属性,所以是无法增加的。
现在看来解决方法可以在model中预先定义isPraise字段,但是这个并不是没有业务都需要的,所以这种方法太糙了,果断放弃。幸好mongooes提供给我们函数来解决这个问题:lean()。代码如下:

Comment.findOne({
    id: commentId,
},null,{
    lean: true
}function (err,data) {
    if(){
        //如果用户点赞
        data.isPraise = true;
    }else{
        //如果用户未点赞
        data.isPraise = false;
    }

});

或者

Comment.findOne({
    id: commentId
}).lean().exec(function (err,data) {
    if(){
        //如果用户点赞
        data.isPraise = true;
    }else {
        //如果用户未点赞
        data.isPraise = false;
    }

});

通过上述方法就可以解决mongoose对返回数据的控制了,你就可以为所欲为了,当然如果不是这种业务,尽量建议少使用,毕竟最好还是使数据符合预先定义的model比较好。

这个lean妙啊,我就是回去schema新增...