There should be a way to define that a query like .findOne
should throw and error if there were not documents matching the query.
When using promises (bluebird) and mongoose, it would be nice to just expect that it is fufilled succesfully instead of having to make another check. It is a little bit against the way you would expect working with promises, when a query returns nothing, but does not throw and error.
Now:
User.findOne({_id: id}).then(function(user) {
if(!user) {
throw new Error('no user with that id')
}
// do something with user
res.send(user
}).catch(function(err) {
res.send({error: err})
})
Suggestion:
User.findOne({_id: id}, {explicit: true}).then(function(user) {
// do something with user
res.send(user
}).catch(function(err) {
res.send({error: err})
})
This way you can always assume that is is defined.
This would be trivial to implement as a plugin. I'm not inclined to add this to the core unless there's a lot of demand for it.
General idea for how the plugin would work (beware of bugs, haven't tested this at all)
function plugin(schema) {
schema.post('findOne', function(res, next) {
if (!res) {
return next(new Error('not found!'));
}
return next();
});
}
Okay, that is pretty cool!
I was creating it because I wanted to hear other people wanted it as well, but I will just create a plugin first :)
Post the plugin when you write it, I'd love to see it. I'd probably use it, because on ES6 projects with co I often find myself writing findByIdOrThrow
functions a lot :)
I know its an closed issue but i have a short question how i could handle a findMinOne without overriding the whole findOne.
Currently i'm using it like this which is working with the callback but its not working with the promise:
schema.static('findMinOne', function (conditions, callback) {
return this.find(conditions, function (err, data) {
if (!data || data.length <= 0) {
err = new Error('No docs found in schema "' + schema + '"!')
}
callback(err, data)
})
})
It would be nice if we could do something like that:
schema.static('findMinOne', function (conditions, callback) {
return this.find(conditions, callback)
})
schema.post('findMinOne', function (data, next) {
if (!data) {
return next(new Error('No docs found in schema "' + schema + '"!'))
}
next()
})
Could you tell me how i could handle this?
With promises:
const assert = require('assert');
schema.static('findMinOne', function(conditions) {
return this.find(conditions).then(res => assert.ok(res.length >= 1, 'No documents found'));
});
Thanks for your reply!
But then will execute the query and will not return the promise so its not working like i wan't it.
I would use it like this:
user
.findMinOne({
'email': '[email protected]'
})
.populate('groups')
.exec(function (err, user) {
// Here, where all other errors will came up, i will get the err if no user is found
}
Or did i missunderstood something?
That's a very good point. We really need to add support for middleware for statics and methods, and the ability to set internal options on queries. Until we have support for either of these, please use the below. It's a little clunky but it gets the job done.
async function run() {
await mongoose.connect(connectionString);
const schema = new Schema({ name: String });
schema.static('findMinOne', function (conditions, callback) {
var q = this.find();
q.findMinOne = true;
return q.find(conditions, callback);
});
schema.post('find', function(docs, next) {
if (this.findMinOne === true && docs.length < 1) {
return next(new Error('No docs found!'));
}
return next();
});
const M = mongoose.model('3298', schema);
await M.find();
// Will throw an error as expected
await M.findMinOne();
}
Thanks for your reply!
This should do it for now as you post it. I will use this until the middleware issue is done.
I wish I'd seen orFail
much sooner:
myModel.findById(theId).orFail( () => new Error('your document not found') )
.catch( new myModel() )
.then( lifeGoesOnFunction )
@tgerk thanks for your comment. For posterity, here's the docs for orFail()
in case someone else stumbles across this issue.
Most helpful comment
This would be trivial to implement as a plugin. I'm not inclined to add this to the core unless there's a lot of demand for it.
General idea for how the plugin would work (beware of bugs, haven't tested this at all)