1

我正在尝试使用 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 查找文档?


问:

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

4

1 回答 1

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 钩子,因为它直接调用 mongo 驱动程序。

于 2013-01-03T11:38:11.000 回答