MongoDB Node findone how to handle no results? - node.js

Im using the npm mongodb driver with node.
I have
collection.findOne({query}, function(err, result) {
//do something
}
The problem is say I dont have any results, err is still null whether I find a result or don't. How would I know that there were no results found with the query?
I've also tried
info = collection.findOne(....
But the info is just undefined (it looked asynchronous so I didn't think it was the way to go anyway..)

Not finding any records isn't an error condition, so what you want to look for is the lack of a value in result. Since any matching documents will always be "truthy", you can simply use a simple if (result) check. E.g.,
collection.findOne({query}, function(err, result) {
if (err) { /* handle err */ }
if (result) {
// we have a result
} else {
// we don't
}
}

All of these answers below are outdated. findOne is deprecated. Lastest 2.1 documentation proposes to use
find(query).limit(1).next(function(err, doc){
// handle data
})

Simply as:
collection.findOne({query}, function(err, result) {
if (!result) {
// Resolve your query here
}
}

nowadays - since node 8 - you can do this inside an async function:
async function func() {
try {
const result = await db.collection('xxx').findOne({query});
if (!result) {
// no result
} else {
// do something with result
}
} catch (err) {
// error occured
}
}

If result is null then mongo didn't find a document matching your query. Have tried the query from the mongo shell?

collection.findOne({query}, function(err, result) {
if (err) { /* handle err */ }
if (result.length === 0) {
// we don't have result
}
}

Related

Mongoose How to show multiple results?

How to display multiple results with one query? I looked through mongoose's documentation. Function '.findByID()' return one 1 document. I am using it here:
router.route ('/friends/:id').get((req,res) => {
Friend.findById(req.params.id, (err,friend) => {
if (err) console.log(err);
else res.json(friend);
});
});
Now i would like to display all results which have in my second column "UserMain" by specified value. Mongoose offers functions like 'updateMany()' or 'deleteMany()' but I can not see 'findMany()' anywhere. I tried to do it in the that way, by passing just second columns value but nothing has happend.
Thank you for your time.
EDIT #1:
1
EDIT #2:
router.route ('/friends/:UserMain').get((req,res) => {
Friend.find({UserMain: req.params.UserMain}, function (err, friends) {
if(err) {
console.log(err)
} else {
res.json(friends)
}
})
});
EDIT #3 2
Try this:
router.route ('/friends/:UserMain').get((req,res) => {
Friend.find({UserMain: req.params.UserMain}, function (err, friends) {
if(err) {
console.log(err)
} else {
res.json(friends)
}
})
});
To retrieve many document within your database use find method which take as first parameter an object which represent clause with which to find documents that match that condition
Friend.find({ user_main: request.params.UserMain}, function (err, friends) {
if(err) {
console.log(err)
} else {
res.json(friend)
}
})

Return 404 code in proper way instead empty array

I have an quite simple application the idea is that someone has unique code which value are stored in one mongo collection in other we are keeping some data which we need to return if the key was found in first collection.
As probably you have noticed I'm using NodeJS with MongoDB and Mongoose, Express.
I have a problem with method bellow:
exports.getCompanyByKey = function(req, res) {
console.log(req.params.keyvalue);
var query = Company.where({keyValue : req.params.keyvalue});
query.findOne(function(err, company){
if(err){
res.send(err);
}else{
SampleData.findOne({}, function(err, sample_data){
if(err)
res.send(err);
res.json(sample_data);
});
}
});
};
The problem is that it will always return the data beause it's not throwing an error but empty array - so is there any other good and proper way as it should be don to throw 404 error without statement such as if(length<0) res.status(404).send('Error message).
I simply want to minimalize amount of if statements.
Maybe there is some other way to write implementation od error handling for mongoose which in general instead returning empty array will give us error code with message?
It's not exactly clear what you're asking, but if you want to make an error condition out of something that is not normally an error, then an if statement (or some other test like that) is required to test for that specific condition.
You could make your own function for querying that turns an empty response into an error and you could "hide" the if condition in that function if you want, but it's still an if condition that tests for your specific condition.
So, to return a 404 if the array is empty, you would just add an if statement (as you already appear to know):
exports.getCompanyByKey = function(req, res) {
console.log(req.params.keyvalue);
var query = Company.where({keyValue : req.params.keyvalue});
query.findOne(function(err, company){
if(err){
res.status(500).send(err);
} else {
SampleData.findOne({}, function(err, sample_data){
if(err) {
res.status(500).send(err);
} else {
if (sample_data.length) {
res.json(sample_data);
} else {
res.status(404).send("no data");
}
}
});
}
});
};
FYI, you also need to make sure you are properly setting a status code when there's an error and that you are never sending multiple responses to the same request (even when there's an error). I've also fixed several cases of those issues in your code.
This could likely be written cleaner and responses consolidated by using the promise interface to your database and send an error in one .catch().
For example, you could simplify your code by creating a utility function for .findOne() that detects and sends an error response automatically:
function findOne(res, db, q, cb) {
db.findOne(q, function(err, data) {
if (err) {
res.status(500).send(err);
cb(err);
} else if (!q.length) {
res.status(404).send("no data");
cb(new Error("no data"));
} else {
cb(null, data);
}
});
}
Then, your function could be simplified to this:
exports.getCompanyByKey = function(req, res) {
var query = Company.where({keyValue : req.params.keyvalue});
query.findOne(function(err, company){
if(err){
res.status(500).send(err);
} else {
findOne(res, SampleData, {}, function(err, sample_data) {
// any error response has already been sent
if (!err) {
res.json(sample_data);
}
});
}
});
};
Again, this would be better to use your Db's promise interface.

Show entire MongoDB contents in Node.js API

First off, don't worry, it's a tiny data set - I realise it wouldn't be wise to dump an entire production DB to a single screen via an API... I just need to get a JSON dump of entire (small) DB to return via an API endpoint in a Node.js application.
My application does successfully return single records with this code:
MongoClient.connect("mongodb://localhost:27017/search", function (err, db) {
if(err) throw err;
db.collection('results', function(err, collection) {
// search for match that "begins with" searchterm
collection.findOne({'string':new RegExp('^' + searchterm, 'i')}, function(err, items){
// get result
var result;
if (items == null || items.result == null){
result = "";
}
else {
result = items.result;
}
// return result
res.send(result);
});
});
});
So I know Node is talking to Mongo successfully, but how can I tweak this query/code to basically return what you get when you execute the following on the MongoDB command line:
$ db.results.find()
This is snippet.
model.find({}).exec(function (err, result) {
if (err) {console.error(err); return;}
else return result;
});
First use your predefined model and call find. the logic is to place a empty object {} essentially rendering . select all from this model.
Make sense?
Exactly as you've described it.
collection.find({}).exec((err, result) => {
if (err) {
console.log(err);
return;
}
if (result.length > 0) {
// We check that the length is > 0 because using .find() will always
// return an array, even an empty one. So just checking if it exists
// will yield a false positive
res.send(result);
// Could also just use `return result;`
});
Thanks guys, I appreciate your answers pointing me in the right direction, in terms of using {} as the query. Here is the code that eventually worked for me:
db.collection('results', function(err, collection) {
collection.find({}).toArray(function(err, docs) {
res.send(docs);
});
});
The crucial element being the toArray(...) part.

mongoose: detect if document inserted is a duplicate and if so, return the existing document

This is my code:
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);
}
});
If I detect that a duplicate is trying to be inserted, i block it. However, when that happens, i want to return the existing document. As you can see I have implemented a string of callbacks, but this is ugly and its unpredictable (ie. how do i know which callback will be called? How do i pass in the right one?). Does anyone know how to solve this problem? Any help appreciated.
While your code doesn't handle a few error cases, and uses the wrong find function, the general flow is typical giving the work you want to do.
If there are errors other than the duplicate, the callback isn't called, which likely will cause downstream issues in your NodeJs application
use findOne rather than find as there will be only one result given the key is unique. Otherwise, it will return an array.
If your callback expected the traditional error as the first argument, you could directly pass the callback to the findOne function rather than introducing an anonymous function.
You also might want to look at findOneAndUpdate eventually, depending on what your final schema and logic will be.
As mentioned, you might be able to use findOneAndUpdate, but with additional cost.
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);
}
There's still a callback of course, but it will write the data again if the duplicate is found. Whether that's an issue is really dependent on use cases.
I've done a little clean-up of your code... but it's really quite simple and the callback should be clear. The callback to the function always receives either the newly saved document or the one that was matched as a duplicate. It's the responsibility of the function calling saveNewValue to check for an error and properly handle it. You'll see how I've also made certain that the callback is called regardless of type of error and is always called with the result in a consistent way.
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);
});
}
Alternatively, you could use the promise pattern. This example is using 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
});
I really like WiredPrairie's answer, but his promise implementation is way too complicated.
So, I decided to add my own promise implementation.
Mongoose 3.8.x
If you're using latest Mongoose 3.8.x then there is no need to use any other promise module, because since 3.8.0 model .create() method returns a 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() also returns a promise, so there is no need for callbacks or any additional casting here.
And if you don't normally use promises in your code, here is callback version of it:
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);
}
Previous versions of Mongoose
If you're using any Mongoose version prior to 3.8.0, then you may need some help from when module:
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;
}
});
}
I'm using nodefn.call helper function to turn callback-styled .save() method into a promise. Mongoose team promised to add promises support to it in Mongoose 4.x.
Then I'm using .spread helper method to extract the first argument from .save() callback.

Retrieving all records asynchronously then performing an action

In a node.js server, using the mongodb native driver, I want to retrieve records from a cursor and then output them as JSON. I have this (simplified)
var ans = {ids: []};
cursor.each(function(err, doc) {
if (doc) {
ans.ids.push(doc.tag);
}
});
cursor.count(function(err, result) {
ans.count = result;
res.send(JSON.stringify(ans));
});
and the result is something like {ids:[], count: 3}. In other words the query appears to run without returning any records. I assume that this is because the data's already been sent before the cursor.each callbacks have run. How do I re-structure this to make sure the sending happens after the iterating?
I have found the answer. The example for cursor.each says "If the item is null then the cursor is exhausted/empty and closed", so: (error handling omitted)
var ans = {ids: []};
cursor.each(function(err, doc) {
if (doc) {
ans.ids.push(doc.tag);
}
else {
cursor.count(function(err, result) {
ans.count = result;
res.send(JSON.stringify(ans));
});
}
});

Resources