NodeJS + Mongoose:更新Mongoose模型的所有字段

12

我正在使用Node、MongoDB和Mongoose构建一个API。有一件事让我感到困扰,就是似乎不能同时设置多个字段:

app.put('/record/:id', function(req, res) {
  Record.findById(req.params.id, function(err, doc) {
    if (!err) {
      doc.update(req.params);
      doc.save();
...

然而,似乎您必须编写更新查询并在模型对象上运行它,而不是在文档对象上运行它。除非您想分配单个属性并在最后运行save()。

有没有不必编写Mongo查询就可以实现这一点的方法?

8个回答

12

jsaak的答案很好,但无法用于嵌套对象。我通过搜索和设置嵌套对象来详细解释了他的答案。

我将这些函数添加到一个utility.js文件中。

var _ = require('underscore');

exports.updateDocument = function(doc, SchemaTarget, data) {
    for (var field in SchemaTarget.schema.paths) {
       if ((field !== '_id') && (field !== '__v')) {
            var newValue = getObjValue(field, data);
            console.log('data[' + field + '] = ' + newValue);
            if (newValue !== undefined) {
                setObjValue(field, doc, newValue);
          }  
       }  
    }
    return doc;
};

function getObjValue(field, data) {
    return _.reduce(field.split("."), function(obj, f) { 
        if(obj) return obj[f];
    }, data);
}

function setObjValue(field, data, value) {
  var fieldArr = field.split('.');
  return _.reduce(fieldArr, function(o, f, i) {
     if(i == fieldArr.length-1) {
          o[f] = value;
     } else {
          if(!o[f]) o[f] = {};
     }
     return o[f];
  }, data);
}

实现为:

var util = require('./utility');

app.put('/record/:id', function(req, res) {
  Record.findById(req.params.id, function(err, doc) {
    if (!err) {
      utils.updateDocument(doc, Record, req.params);
      doc.save();
    ...

事后看来,你不需要使用下划线库来进行.reduce操作。 - justin
我一直在尝试更新一个对象数组中的一个项目。因为某种原因,markModified方法不起作用。你的方法很有效。 - Aaron Ogle
感谢 @justin!你让我的一天变得美好!很棒的回答,应该将其标记为“正确答案”,@james。你的答案解决了我更新嵌套对象的问题。我只有一个注释要添加。你将“util”定义为包含方法的变量,然后在使用它时将其作为“utils”。 - RandomUser
谢谢,这很棒,但我必须问一个问题:如果字段包含updatedDate,则我还必须通过updatedDate = new Date()在模式中更新日期,那么该如何做,应该在utility.js文件的哪里添加? - Prachi Varaiya

11

也许自从这个问题首次被提出以来情况已经有所改变,但是你可以使用set方法在Mongoose中更新多个路径,例如:

// object
doc.set({
  path  : value,
  path2 : {
    path  : value
  }
});
doc.save();

参考资料


8
根据此文档,不建议使用直接更新的方式:http://mongoosejs.com/docs/2.7.x/docs/updating-documents.html 我是这样解决的:
  Book.findOne({isbn: req.params.isbn}, function (err, book){
     if (err) {
        res.send(422,'update failed');
     } else {
        //update fields
        for (var field in Book.schema.paths) {
           if ((field !== '_id') && (field !== '__v')) {
              if (req.body[field] !== undefined) {
                 book[field] = req.body[field];
              }  
           }  
        }  
        book.save();
     }
  });

完美运作,我已经制作了一个通用函数,可以在任何地方使用: - Deminetix
1
工作得很好,直到你有嵌套对象。Justin有更完整的方法。 - Aaron Ogle
想象一下这种情况,当一个字段被明确地留空(清除)时,它不会被您的代码更新,旧值将被保留。 - badr slaoui

3

如果您想更新整个文档,可以根据其id删除文档,然后重新存储整个对象。该对象必须包含mongo文档中每个字段的数据。

这里是一个例子。

mongoDBCollectionObject.findOneAndRemove({ // -- it will delete the entire document
  _id: req.body.fieldsdata._id  // here fiedsdata is exact copy with modification  of previous data
  }, function(err, data) {
  var newFieldsData = new mongoDBCollectionObject(fieldsdata); //-- fieldsdata updated data
  newFieldsData.save(function(err, data) { // save document to that collection with updated data
    if (err) {
      console.log(err);
    } else
        res.json({
          success: true
          });
    });
  })


你能提供一些示例、提示或文档来完成这个吗? - dakab
@dakab:我已经更新了帖子,并附上了示例代码。你可以参考一下。 - vikash5472

2
为了澄清问题,看起来您正在使用请求参数来查找和更新给定的文档。
有没有办法在不编写Mongo查询的情况下完成这个任务?
显然的答案是使用请求中的值来更新模型对象。这就是您建议的方法...
除非您想分配单个属性并在最后运行save()。
但似乎您不想这样做?听起来您想直接从Request对象中更新模型对象?
如果您真的想这样做,可以这样做。只需循环遍历req.params并在适当的位置设置doc值即可。
for(var i in req.params) {
  if(req.params[i] != doc[i]){
    doc[i] = req.params[i];
  }
}

这应该很简单。但是,如果您在模型对象上有大量的验证代码,那么您只想这样做。模型的整个重点是您不希望在数据库中获取随机数据。上面的行将通用地“设置”正确的值,但您肯定需要在简单的for循环周围包含身份验证、授权和验证代码。

1
尝试在不使用find的情况下更新集合,就像这样:
Record.update({_id:req.params.id}, {$set: { field: request.field }}, {upsert: true}, function(err{...})

选项 upsert 如果文档不存在则创建。


我很惊讶!你的解决方案解决了mongoError:maximum call stack exceeded问题。 - CodecPM

1
如果您有一个新对象并且想在数据库中更新整个对象,可以像这样一次性更新多个字段:
  1. 查找对象
  2. 获取所有模式路径(字段)
  3. 保存新对象。

SomeModel.findOne({ 'id': 'yourid' },function (err, oldObject) {
 if (err) return handleError(err);

 // get all schema paths (fields)
 SomeModel.schema.eachPath(function(path) {

     // leave __id and __v alone
     if (path != '_id' && path != '__v') {
      // update the data from new object
      oldObject[path] = newObject[path];
     }
 })

 oldObject.save(function(err) {
  if (err)
  console.log(err)
 });
})


0
一个整洁而清晰的方法是使用asyncawaitfindOneAndRemove以及create。以下是示例代码。
try {
            let resp = await this.findOneAndRemove({ _id: req.body._id });
            let entry = await this.create(req.body);

    } catch (err) {

    }

不要忘记将整个函数标记为 async


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接