将猫鼬查询结果存储在另一个猫鼬查询中

Posted

技术标签:

【中文标题】将猫鼬查询结果存储在另一个猫鼬查询中【英文标题】:Store mongoose Query result inside another mongoose query 【发布时间】:2017-05-08 04:04:46 【问题描述】:

我对 nodeJs 和 mongodb 还很陌生。我在查询猫鼬对象时遇到了一些问题。我有 2 个模型

用户模型:

var mongoose = require('mongoose');
var bcrypt = require('bcrypt');
var gravatar = require('gravatar');
var Schema = mongoose.Schema;
var SendSchema = require('./Send').schema;
var TravelSchema = require('./Travel').schema;

var UserSchema = new Schema(
    name: String,
    email:type: String, required: true, unique:true,
    phone: type: String, required: true, unique:true,
    password: type:String,required:true,
    token: String,
    is_admin : Boolean,
    sendings : [SendSchema],
    travels : [TravelSchema],
    created_at : Date,
    updated_at : Date,
    image_url: String
)

UserSchema.pre('save',function(next)
    var user = this;
    if (this.isModified('password')||this.isNew)
        bcrypt.genSalt(10,function(err,salt)
            if(err)
                return next(err);
            
            bcrypt.hash(user.password,salt,function(err,hash)
                if(err)
                    return next(err);
                
                user.password = hash;
                next();
            );
        );
     else 
        return next();
    
);

UserSchema.pre('save', function(next) 
    var currentDate = new Date();
    this.updated_at = currentDate;
    if (!this.created_at)
    this.created_at = currentDate;
    next();
);


UserSchema.methods.comparePassword = function (pw,cb) 
    bcrypt.compare(pw,this.password,function(err,isMatch)
        if(err)
            return cb(err);
        
        cb(null,isMatch);
    );
;


module.exports = mongoose.model('User',UserSchema);

和旅行模式:

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

var TravelSchema = new Schema(
    travelling_from:String,
    travelling_to:String,
    amount:String,
    date:Date,
    created_at: Date,
    updated_at: Date,
    traveller : type:Schema.Types.ObjectId ,ref:'User'
);

TravelSchema.pre('save', function(next) 
    var currentDate = new Date();
    this.updated_at = currentDate;
    if (!this.created_at)
    this.created_at = currentDate;
    next();
);

module.exports = mongoose.model('Travel',TravelSchema);

现在我正在使用快速路线查询猫鼬模型,如下所示:

router.post('/travellers',passport.authenticate('jwt',session:false), function(req, res, next) 
    var pickup_location = req.body.pickup_location;
    var delivery_location = req.body.delivery_location;
    var date = req.body.date;
    var sender = req.user._id;
    var senders = [];
    var travellers =[];

    Travel.find('date':date,function (err,travels) 
        if(err) console.error(err.message);;
        async.forEach(travels,function (travel,callback) 
            User.findById(travel.traveller,function (err,user) 
                if(err) throw err;
                data = 
                    name:user.name,
                    email:user.email,
                    phone:user.phone,
                    image_url:user.image_url,
                    type:'traveller'
                ;
                console.log(data);
                travellers.push(data);
                callback();
            );
        ,function (err) 
            if(err) console.error(err.message);;
        );
    );
    console.log(travellers);
    res.json(travellers);
);

当我在 res.json() 查询完成后尝试访问 traveler 数组时,我得到一个空响应,而当我 console.log() 数据在查询期间正确打印时,有人可以帮助我完成这个新的异步范式,我已经敲了 2 天了。

【问题讨论】:

res.json 必须进入async.forEach 回调。但我想这可以使用参考人口更容易解决,因为用户存在于 Travel 的 traveller 属性中。 OT:为什么这个方法是POST?你显然没有改变任何数据? 我使用 post 是因为我需要从 post 请求中存在的 date 属性中获取数据。 【参考方案1】:

添加async.series API,它将一次运行一个函数,等待它调用其任务回调,最后当所有任务完成时,它将运行callback(最终回调)。

例如:

router.post('/travellers',
    passport.authenticate('jwt',  "session": false ), function(req, res, next) 
    var pickup_location = req.body.pickup_location;
    var delivery_location = req.body.delivery_location;
    var date = req.body.date;
    var sender = req.user._id;
    var locals = 
        travellers: [],
        senders: []
    ;

    async.series([
        // Load travels first
        function(callback) 
            Travel.find( "date": date , function (err, travels) 
                if (err) return callback(err);
                locals.travels = travels;
                callback();
            );
        ,
        // Load users (won't be called before task 1's "task callback" has been called)
        function(callback) 
            async.forEach(locals.travels, function (travel, callback) 
                User.findById(travel.traveller, function (err, user) 
                    if (err) return callback(err);
                    data = 
                        "name": user.name,
                        "email": user.email,
                        "phone": user.phone,
                        "image_url": user.image_url,
                        "type": "traveller"
                    ;
                    console.log(data);
                    local.travellers.push(data);
                    callback();
                );
            , function (err) 
                if (err) return callback(err);
                            callback();
            );
        
    ], function(err)  /* This function gets called after 
          the two tasks have called their "task callbacks" */
        if (err) return next(err);
        //Here locals will be populated with `travellers` and `senders`
        //Just like in the previous example
        console.log(locals);
        console.log(locals.travellers);
        res.json(locals.travellers);
    );
);

另一种方法是在聚合框架中使用 $lookup 运算符,您可以在其中运行如下聚合操作:

router.post('/travellers',
    passport.authenticate('jwt', session: false ), function(req, res, next) 
    var pickup_location = req.body.pickup_location;
    var delivery_location = req.body.delivery_location;
    var date = req.body.date;

    Travel.aggregate([
         "$match":  "date": date  ,
        
            "$lookup": 
                "from": "users",
                "localField": "traveller",
                "foreignField": "_id",
                "as": "traveller"
            
        ,
         "$unwind": "$traveller" ,
        
            "$group": 
                "_id": null,
                "travellers":  
                    "$push": 
                        "name": "$traveller.name",
                        "email": "$traveller.email",
                        "phone": "$traveller.phone",
                        "image_url": "$traveller.image_url",
                        "type": "traveller"
                                   
                
            
        
    ], function(err, results)  
        if (err) return next(err);
        console.log(results);
        console.log(results[0].travellers);
        res.json(locals[0].travellers);
    );
);

【讨论】:

我能够通过您的回答完成它,只需一个修改我们需要在 async.foreach() 函数中的 function(err) 中添加 callback(),谢谢! @VedantRathore 不错。我添加了缺少的callback()

以上是关于将猫鼬查询结果存储在另一个猫鼬查询中的主要内容,如果未能解决你的问题,请参考以下文章

从精益查询的结果创建猫鼬模型

向猫鼬查找查询结果添加字段

向猫鼬查找查询结果添加字段

无法将猫鼬虚拟与打字稿一起使用

无法将猫鼬虚拟与打字稿一起使用

将猫鼬列从对象更改为 ObjectId