【问题标题】:mongoosejs upsert with on save hookmongoosejs upsert 与保存挂钩
【发布时间】:2013-01-05 01:09:42
【问题描述】:

我正在尝试使用 mongoose 将数据添加到我的 mongo 数据库中,但是很可能大部分数据已经在数据库中,只有少数字段需要更新。需要保存记录的创建时间和上次更新时间。

我第一次尝试解决这个问题包括使用 Model.save 函数,因为我的模型被称为服务器,并且数据是来自外部 http 服务的对象,它指定了数据中唯一的 _id。

var instance = new Server(data);
instance.save(function(err){
  if(err)
    console.log(err);
});

也是我的预保存钩子:

ServerSchema.pre('save', function(next) {
  this.lastseen = Date.now();

  if (!this.isNew) 
    return next() //if the entry isn't new, lets not change the date registred
  this.registered = Date.now();
  next() //Don't forget this!
})

这里的问题是,在重复的_id 上,保存阻塞,错误E11000 duplicate key error index...

现在这很有意义,因为 save 仅在文档​​ instance 未使用 new 运算符创建时进行更新。


所以在我的下一次尝试中,我添加了代码来尝试查找文档,然后使用 underscore.js 的 _.extend 将新文档与在数据库中找到的文档合并,然后将其保存到数据库中。这种方法的问题在于,它需要为正在处理的每个数据块额外调用数据库。


我的第三次尝试将Model.findByIdAndUpdate{upsert:true} 结合使用,就在数据库中存储数据而言,这是可行的,但架构默认值和我的pre-save 挂钩未触发。


第四次尝试使用@aheckmann 在此要点中建议的代码:https://gist.github.com/2764948

var server = new Server();
server.init(ping);
server.save(function(err){
  if(err) {
    console.log("DB Error: ",err);
    return res.send('DB Error')
  }

  //if server approved, tell the inworld server to sync textures
  if(server.approved)
    res.send('success')
  else
    res.send('skip')

  user.servers.addToSet(ping._id); //add the server to the user's list
  user.save(function(err, usr){
    if(err)
      console.log("DB Error: ", err);
  })
})

同样,pre-save 钩子没有被触发。我是否理解唯一使用钩子插入的方法是首先尝试使用 findById 查找文档?


问:

有没有一种方法可以根据主唯一键“更新”插入或更新,而不会对每个数据块进行多次数据库调用?有没有我忽略的方法或明显的事实?

【问题讨论】:

  • this.lastseen = Date.now; 有效吗?不应该是this.lastseen = Date.now();吗?
  • @Leonid Beschastny 是的,我认为你是对的。

标签: mongodb mongoose


【解决方案1】:

我不认为你可以通过少于两次调用 DB 来做到这一点,除非你放弃 mongoose 部分并直接使用 mongo 驱动程序。但是您可以创建一个静态方法来为您完成所有工作:

ServerSchema.statics.findOrCreate(function(doc, next) {
  this.findById(doc._id, function(err, res) {
    res || (res = new this);
    _.extend(res, doc); // add new data to the document
    next(err, res); // if (err != null) then something went wrong
  });
});

findByIdAndUpdate 不会触发 presave 钩子,因为它直接调用 mongo 驱动程序。

【讨论】:

    猜你喜欢
    • 2023-03-06
    • 1970-01-01
    • 2019-05-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-05-18
    • 1970-01-01
    相关资源
    最近更新 更多