.save()和使用update()之间的猫鼬差异

Edw*_*Sun 41 javascript mongoose mongodb node.js

要修改mongoose中现有条目中的字段,使用之间有什么区别

model = new Model([...])
model.field = 'new value';
model.save();
Run Code Online (Sandbox Code Playgroud)

还有这个

Model.update({[...]}, {$set: {field: 'new value'});
Run Code Online (Sandbox Code Playgroud)

我问这个问题的原因是因为有人建议我昨天发布的一个问题:NodeJS和Mongo - 当多个用户同时发送请求时出现意外行为.这个人建议使用更新而不是保存,我还不完全确定它为什么会有所作为.

谢谢!

Nei*_*unn 96

首先是两个概念.您的应用程序是客户端,Mongodb是服务器.

主要的区别在于,.save()在您的客户端代码中已经有了一个对象,或者在您将其写回之前必须从服务器检索数据,并且您正在回写整个事情.

另一方面.update(),要求将数据从服务器加载到客户端.所有的交互都发生在服务器端而没有检索到客户端.update().因此,当您向现有文档添加内容时,这种方式非常有效.

此外,还有一个multi参数.update()允许在多个与查询条件匹配的文档上执行操作.

在使用便捷方法时,有些东西在.update()用作通话时会松动,但某些操作的好处是你必须承担的"权衡".有关此内容以及可用选项的详细信息,请参阅文档.

简而言之.save()就是客户端接口,.update()是服务器端.

  • 这是一个很好的答案.您提供了*context*信息,这正是高级指南和教程中通常缺少的信息...... (9认同)
  • **重要注意事项**`.save()`不****_write back**all thing**_"; 它区分文档,只发送实际更改的字段.所以可以用来对数据库进行_tiny_更改,就像`.update()`一样. (6认同)
  • 要知道的另一个有用的事情是 save() 触发默认验证,而 update 需要您打开 runValidators。但即使在这种情况下,runValidators 也只有在您使用某些关键字时才会生效 (http://mongoosejs.com/docs/validation.html#update-validators):“最后一个细节值得注意:更新验证器仅在 $set 上运行和 $unset 操作(以及 >= 4.8.0 中的 $push 和 $addToSet)。例如,无论 number 的值如何,下面的更新都会成功,因为更新验证器会忽略 $inc。 (2认同)
  • @ iss42在这里。https://github.com/Automattic/mongoose/blob/b5b3053fa2207b4bdea0092db5350d455accfbb1/benchmarks/benchjs/update.js#L204-L207 (2认同)

Tam*_*lyn 33

一些差异:

  • 如其他地方所述,updatefind后续更有效,save因为它避免加载整个文档.
  • Mongoose update转换为MongoDB,update但Mongoose save转换为MongoDB insert(用于新文档)或update.
  • 重要的是要注意,在save,猫鼬内部的diff文件,只发送实际变化的领域.这对原子性有好处.
  • 默认情况下,验证不会运行,update但可以启用它.
  • 中间件API(prepost钩子)是不同的.

  • 给你的帽子提示 (2认同)

Ale*_*raj 16

Mongoose上有一个名为Middleware的有用功能.有'pre'和'post'中间件.当您执行"保存"时,中间件会被执行,但在"更新"期间不会执行.例如,如果要在每次修改密码时在User模式中哈希密码,可以使用pre来执行以下操作.另一个有用的示例是为每个文档设置lastModified.该文档可以在http://mongoosejs.com/docs/middleware.html找到

UserSchema.pre('save', function(next) {
var user = this;
// only hash the password if it has been modified (or is new)
if (!user.isModified('password')) {
    console.log('password not modified');
    return next();
}
console.log('password modified');
// generate a salt
bcrypt.genSalt(10, function(err, salt) {
    if (err) {
        return next(err);
    }

    // hash the password along with our new salt
    bcrypt.hash(user.password, salt, function(err, hash) {
        if (err) {
            return next(err);
        }
        // override the cleartext password with the hashed one
        user.password = hash;
        next();
    });
});
});
Run Code Online (Sandbox Code Playgroud)

  • 从Mongoose 4.0开始,还支持查询中间件(例如,用于"更新"). (3认同)
  • +1感谢`user.isModified('password')`位.这让我发疯了.每当我试图保存某些东西时,密码就会改变. (2认同)

Dan*_*iel 10

一个不能掉以轻心的细节:并发

如前所述,在执行 a 时doc.save(),您必须先将文档加载到内存中,然后对其进行修改,最后doc.save()是对 MongoDB 服务器的更改。

当以这种方式同时编辑文档时会出现问题:

  • 人 A 加载文档 (v1)
  • 人 B 加载文档 (v1)
  • 人 B 保存对文档的更改(现在是 v2)
  • 人员 A 将更改保存到过时 (v1) 文档
  • 人 A 会看到 Mongoose 抛出一个 VersionError,因为自上次从集合中加载以来文档发生了变化

在进行原子操作时,并发性不是问题Model.updateOne(),因为该操作完全在 MongoDB 服务器中完成,该服务器执行一定程度的并发控制

因此,要小心!