如何使用mongoose将多重引用保存到MongoDB中的一个文档中?

时间:2019-04-04 20:49:05

标签: node.js mongodb mongoose

我正在尝试将多个标签保存到我刚刚创建的特定产品中。我成功地将标签推入了产品集合,但是当我尝试保存它时,我遇到了一个错误:无法多次并行保存()相同的文档。

我正在使用mongoDB,node和Mongoose。

这是我的模式


var tagSchema = mongoose.Schema({
    tagname: String,
});

var tag = mongoose.model('Tag', tagSchema);


var Item = new Schema({
    title : String,
    description : String,
    price : Number,
    tags:  [{
        type: mongoose.Schema.Types.ObjectId,
        ref: "Tag"
    }]
});

var product = mongoose.model('Product', Item);

这是我尝试在数据库中创建具有关联标签的新产品时来自node.js的代码(来自HTML表单)


product.create(req.body.product, function(err, newitem){
            if(err){
                console.log(err);
            }else{

             console.log(req.body.tags); // gives ["fun","cheap"] from the form
             req.body.tags.forEach(function(newtag){
             tag.findOne({tagname : newtag},function(err, finalcast){


             newitem.tags.push(finalcast); 
             console.log(newitem); // gives the product with the tag collections in it.

               });
              newitem.save(); // error Can't save() the same doc multiple times in parallel.
              console.log(newitem); // the product collection has an empty tags[] 

               });


                 res.redirect("/");

            }

        });


如何一次将标签集合直接保存在产品中?最好先创建产品,然后使用push逐个插入标签?

非常感谢您的帮助!

4 个答案:

答案 0 :(得分:1)

问题可能是因为您试图通过推入对象而不是_id

newitem.tags.push(finalcast);

尝试像这样推送对象的_id:

newitem.tags.push(finalcast._id);

答案 1 :(得分:0)

我认为这与Asynchrone JS有关,但我不确定。循环似乎不是一个好主意...

如何轻松地将多个标签文档保存到一个产品文档中?

答案 2 :(得分:0)

好几个小时后,我找到了解决方案。我真的不知道这是否是一个好方法,但是它有效。我在forEach中插入一个if(),仅在完成forEach循环时保存:

 req.body.tags.forEach(function(newtag, index){
                    tag.findOne({tagname : newtag},function(err, finalcast){

                    newitem.tags.push(finalcast); 

                    if(index + 1 == req.body.tags.length){
                    newitem.save();
                    res.redirect("/");

                    }
               });

答案 3 :(得分:0)

您正在forEach循环中一次执行所有操作,这使得很难知道何时完成数据库操作。这也意味着您正在实际完成操作之前触发res.redirect。我会为此使用异步/等待。我写得很快,还没有测试,但我认为它应该可以工作:

const tagSchema = mongoose.Schema({
  tagname: String
})

const Tag = mongoose.model('Tag', tagSchema)

const itemSchema = new Schema({
  title: String,
  description: String,
  price: Number,
  tags: [{
    type: mongoose.Schema.Types.ObjectId,
    ref: "Tag"
  }]
})

const Product = mongoose.model('Product', itemSchema)


const createProduct = async (req, res) => {
  const { tags, product } = req.body

  try {
    const product = new Product(product)

    for (let i = 0; i < tags.length; i++) {
      const tagName = tags[i]
      const existingTag = await Tag.findOne({ tagname: tagName })

      if (!existingTag) {
        await new Tag({ tagname: tagName}).save()
      }
    }

    await product.save()

    res.redirect('/') 

  } catch (error) {
    console.log(error)
  }
}

在代码中,我做了几件事:

  • 使用async / await一次执行一件事情,而不是同步执行。
  • 清理代码:缩进等有点混乱(我使用了StandardJS样式)
  • 对模型使用pascal大小写,因为它们是对象构造函数。模型之所以以大写字母开头是有原因的。
  • 使用for循环代替forEach,因为forEach与异步功能不兼容。