mongoosejs upsert with save hook

时间:2013-01-03 05:02:57

标签: mongodb mongoose

我尝试使用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...

现在这是有道理的,因为只有在使用new运算符创建文档instance时才进行更新。


因此,在我的下一次尝试中,我添加了代码以尝试查找文档,然后使用underscore.js' s _.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来查找文档?


问:

有没有办法让#34; upsert"基于主唯一键插入或更新,而不是每个数据块进行多个数据库调用?是否有一种方法,或明显的事实,我在俯视?

1 个答案:

答案 0 :(得分:4)

我不认为你可以用少于两次调用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 hook,因为它直接调用mongo驱动程序。