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?)。 有谁知道如何解决这个问题? 任何帮助赞赏。
虽然你的代码不能处理一些错误的情况下,并使用错误的find
function,一般的stream程是典型的给你想做的工作。
- 如果除重复以外的其他错误,则不会调用callback,这可能会导致NodeJs应用程序中的下游问题
- 使用
findOne
而不是find
因为只有一个结果,因为这个键是唯一的。 否则,它将返回一个数组。 - 如果您的callback期望传统的
error
作为第一个参数,您可以直接将callback传递给findOne
函数,而不是引入匿名函数。 - 你最好也可以看看
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中提取第一个参数。