在for循环中使用Model.create()和save()

时间:2017-11-14 22:38:43

标签: javascript node.js mongodb mongoose mongoose-schema

嘿所以我对Javascript和Node很陌生,但是我遇到了困扰我一段时间的问题。

我有一个User模型和一个Image模型,我使用Multer上传一个图像数组, 试图遍历这个数组,为每个数组创建一个新的Image模型,然后取消该图像进入我用户的照片 。我让Multer成功填写了req.files。这是代码。

router.post("/users/:user/photos/upload", middle.isLoggedIn, upload.array("photos", 4), function(req, res) {
    User.findById(req.params.user, function(err, foundUser) {
        for(var i = 0, len = req.files.length; i < len; i++) {
            Image.create(req.files[i], function(err, newImage) {
                if(err) {
                    return console.log(err.message);
                }
                newImage.human = foundUser;
                newImage.save();
                console.log(newImage);
                foundUser.photos.unshift(newImage);
                foundUser.save();
            });
        }
        console.log(foundUser);
    });
});

console.log(foundUser);似乎在console.log(newImage);

之前执行并打印

用户模型

var mongoose                = require("mongoose"),
    passportLocalMongoose   = require("passport-local-mongoose");

var UserSchema = new mongoose.Schema({
    username: String,
    password: String,
    firstName: String,
    lastName: String,
    city: String,
    photos: [
        {
            type: mongoose.Schema.Types.ObjectId,
            ref: "Image"
        }    
    ]
});

HumanSchema.plugin(passportLocalMongoose);
module.exports = mongoose.model("User", UserSchema);

图像模型

var mongoose            = require("mongoose");

var ImageSchema = new mongoose.Schema({
    fieldname: String,
    originalname: String,
    mimetype: String,
    filename: String,
    destination: String,
    size: Number,
    path: String,
    human: {
        id: {
            type: mongoose.Schema.Types.ObjectId,
            ref: "Human"
        }
    }
});

module.exports = mongoose.model("Image", ImageSchema);

这是我的第一个stackoverflow问题,如果我没有发布足够的代码,请告诉我。

我认为它与Image.create()是异步的有关,我仍然在尝试更多地了解这个和承诺,但我仍然不完全理解它在我的代码中的相关性。

1 个答案:

答案 0 :(得分:1)

使用Mongoose's promise support

Promise.all允许您解析一系列承诺。

async / await控制Promise的流程。

由于可以按任何顺序执行的异步代码,我不确定您的代码是否结构化而没有串行循环。我不确定在不同时间在同一个对象上触发多个foundUser.save()会很好。长时间在内存中保存数据库对象也会打开更多的并发数据问题。

Bluebird承诺库包含一些额外的帮助程序,如Promise.each,它们会在下次启动之前连续完成承诺,这可能会在这里使用。

const Promise = require('bluebird')

router.post("/users/:user/photos/upload", middle.isLoggedIn, upload.array("photos", 4), async function(req, res, next) {
    try {
        let foundUser = await User.findById(req.params.user)
        await Promise.each(req.files, async file => {
            let newImage = await Image.create(file)
            newImage.human = foundUser;
            await newImage.save()
            console.log(newImage)
            foundUser.photos.unshift(newImage)
            await foundUser.save()
        }
        console.log(foundUser)
    }
    catch (err) {
        next(err)
    }
})

.map.reduce等其他方法有助于使Promises的标准数组/循环类型操作更容易。

原子更新

关于并发问题,您可以在MongoDB中执行的任何“原子”更新都是一件好事。因此,不是选择一些东西,在JS中修改它,然后将其保存回来,而是将更新发送给Mongo并让db服务器处理它。无论您将更新发送到数据库的顺序如何,它们都将始终更新最新的数据副本。

在这种情况下,使用findByIdAndUpdate$push可以在没有初始选择的情况下完成数组unshift(可以使其在位置0处推进,没有{{1}在mongo)。

如果您为用户模型添加方法以添加照片:

$unshift

所以代码看起来像

addPhoto(user_id, newImage){
    return User.findByIdAndUpdate(
        user_id,
        { $push: { photos: { $each: [newImage], $position: 0 } } } }
        { safe: true, new: true }
    )
}