mongoose:检测插入的文件是否重复,如果是的话,返回现有的文件

这是我的代码:

var thisValue = new models.Value({ id:id, title:title //this is a unique value }); console.log(thisValue); thisValue.save(function(err, product, numberAffected) { if (err) { if (err.code === 11000) { //error for dupes console.error('Duplicate blocked!'); models.Value.find({title:title}, function(err, docs) { callback(docs) //this is ugly }); } return; } console.log('Value saved:', product); if (callback) { callback(product); } }); 

如果我检测到一个重复试图插入,我阻止它。 但是,当发生这种情况时,我想返回现有的文档。 正如你所看到的,我已经实现了一系列的callback,但是这是丑陋的,它是不可预知的(即,我怎么知道哪个callback会被调用?我如何传递正确的callback?)。 有谁知道如何解决这个问题? 任何帮助赞赏。

虽然你的代码不能处理一些错误的情况下,并使用错误的findfunction,一般的stream程是典型的给你想做的工作。

  1. 如果除重复以外的其他错误,则不会调用callback,这可能会导致NodeJs应用程序中的下游问题
  2. 使用findOne而不是find因为只有一个结果,因为这个键是唯一的。 否则,它将返回一个数组。
  3. 如果您的callback期望传统的error作为第一个参数,您可以直接将callback传递给findOne函数,而不是引入匿名函数。
  4. 你最好也可以看看findOneAndUpdate ,这取决于你的最终模式和逻辑是什么。

如上所述,您可能可以使用findOneAndUpdate ,但需要额外的成本。

 function save(id, title, callback) { Value.findOneAndUpdate( {id: id, title: title}, /* query */ {id: id, title: title}, /* update */ { upsert: true}, /* create if it doesn't exist */ callback); } 

当然还有一个callback,但是如果find了重复的话,它会再次写入数据。 这个问题是否真的依赖于用例。

我已经完成了一些代码的清理工作,但实际上很简单,callback应该很清楚。 对函数的callback总是接收新保存的文档或者匹配的文档。 调用saveNewValue的函数负责检查错误并正确处理它。 你会看到我如何确定callback被调用,而不pipe错误types如何,并且总是以一致的方式调用结果。

 function saveNewValue(id, title, callback) { if (!callback) { throw new Error("callback required"); } var thisValue = new models.Value({ id:id, title:title //this is a unique value }); thisValue.save(function(err, product) { if (err) { if (err.code === 11000) { //error for dupes return models.Value.findOne({title:title}, callback); } } callback(err, product); }); } 

或者,您可以使用承诺模式。 这个例子是使用when.js。

 var when = require('when'); function saveNewValue(id, title) { var deferred = when.defer(); var thisValue = new models.Value({ id:id, title:title //this is a unique value }); thisValue.save(function(err, product) { if (err) { if (err.code === 11000) { //error for dupes return models.Value.findOne({title:title}, function(err, val) { if (err) { return deferred.reject(err); } return deferred.resolve(val); }); } return deferred.reject(err); } return deferred.resolve(product); }); return deferred.promise; } saveNewValue('123', 'my title').then(function(doc) { // success }, function(err) { // failure }); 

我真的很喜欢WiredPrairie的答案 ,但他的诺言实现太复杂了。

所以,我决定添加我自己的诺言实现。

mongoose3.8.x

如果你使用最新的Mongoose 3.8.x那么就不需要使用任何其他的promise模块,因为3.8.0 model .create()方法返回一个promise:

 function saveNewValue(id, title) { return models.Value.create({ id:id, title:title //this is a unique value }).then(null, function(err) { if (err.code === 11000) { return models.Value.findOne({title:title}).exec() } else { throw err; } }); } saveNewValue('123', 'my title').then(function(doc) { // success console.log('success', doc); }, function(err) { // failure console.log('failure', err); }); 

models.Value.findOne({title:title}).exec()也返回一个承诺,所以在这里不需要callback或者任何额外的转换。

如果你在你的代码中通常不使用promise,这里是它的callback版本:

 function saveNewValue(id, title, callback) { models.Value.create({ id:id, title:title //this is a unique value }).then(null, function(err) { if (err.code === 11000) { return models.Value.findOne({title:title}).exec() } else { throw err; } }).onResolve(callback); } 

以前版本的Mongoose

如果你使用3.8.0之前的任何Mongoose版本,那么你可能需要模块的帮助:

 var when = require('when'), nodefn = require('when/node/function'); function saveNewValue(id, title) { var thisValue = new models.Value({ id:id, title:title //this is a unique value }); var promise = nodefn.call(thisValue.save.bind(thisValue)); return promise.spread(function(product, numAffected) { return product; }).otherwise(function(err) { if (err.code === 11000) { return models.Value.findOne({title:title}).exec() } else { throw err; } }); } 

我正在使用nodefn.call帮助函数将callback式.save()方法转换为promise。 Mongoose团队承诺在Mongoose 4.x增加promise的支持。

然后我使用.spread帮助器方法从.save()callback中提取第一个参数。