Optional chaining of methods like sort, limit, skip - mongodb

I'm passing the query params and there could be any combination of sort, limit or skip for the Mongoose Query.
What I've thought of is to create mutliple mongoose queries based on what params have been passed. So if only sort is passed then the resulting query will have Document.find({}).sort({}) and incase only limit is passed then query would be Document.find({}).limit({})
Should I write something like this -
router.get('/', (req, res) => {
if(req.query.sortByPrice) {
Property.find({})
.sort({ price: req.query.sortByPrice === 'asc' ? 1 : -1 })
.populate('user_id', 'name')
.exec((err, properties) => {
if (err)
return res
.status(404)
.json({ error: "Can't get user details!" });
res.status(200).json(properties);
});
}
if(req.query.limit) {
Property.find({})
.limit(req.query.limit)
.populate('user_id', 'name')
.exec((err, properties) => {
if (err)
return res
.status(404)
.json({ error: "Can't get user details!" });
res.status(200).json(properties);
});
}
});

You can create a variable options from your request body and pass it as the third argument to the .find() query, no need to write redundant code with if-else block.
Secnd argument to .find() query is projection, so don't forget to pass an empty object there.
Try this :
let options={};
if(req.query.sortByPrice){
options.sort = {
price: req.query.sortByPrice === 'asc' ? 1 : -1
}
}
if(req.query.limit){
options.limit = req.query.limit
}
Property.find({},{},options)
.populate('user_id', 'name')
.exec((err, properties) => {
if (err)
return res
.status(404)
.json({ error: "Can't get user details!" });
res.status(200).json(properties);
return;
});
Note: Dont forget to return after res.status().json(), otherwise you might get error cant set headers after they are sent . if you try to send response again.

Related

Mongoose: Defining 404 status for not finding a document doesnt work

I,m learning MongoDB and mongoose and now I have a problem in defining a 404 status for my route handler. Here is the code:
app.get('/users/:id', async (req, res) => {
const _id = req.params.id
try {
const user = await User.findById(_id)
if (!user) {
return res.status(404).send()
}
res.send(user)
} catch (error) {
res.status(500).send()
}
})
Now if I give it an id that doesn't exist, it doesn't give me 404 Not Found status. it only executes the catch block which is not what I want.
I would appreciate it if you tell me where I made mistake or tell me a way to get error handling for that.
Thanks
The problem
As you can see in the log
CastError: Cast to ObjectId failed for value "6082d50a2c89db3164" at path "_id" for model "User"
It means : the value you provide to findById function ("6082d50a2c89db3164") is not a valid ObjectId.Then the catch block is executed.
Suggestion
1. Validate the parameter before query in database
I understand that you're trying to provide some id that doesn't exist in the database to test. But IMHO, there a difference between 2 cases :
you provide a valid id, and this id cannot be found in the database. It should return 404 in this case
you provide an invalid id in the request, it could be a string like "6082d50a2c89db3164", or even "#Q*&$(##*" or anything we could imagine. For this case, it could be better if we validate the input (req.params._id) to ensure that the format is valid. The code will be something like this:
app.get('/users/:id', async (req, res) => {
const _id = req.params.id;
// validate params
if(!isValidateObjectId(_id)) { // the function we need to write
res.status(200).send("Invalid params"); // you can define your status and message
return;
}
// good params, get user from database
try {
const user = await User.findById(_id)
if (!user) {
return res.status(404).send()
}
res.send(user)
} catch (error) {
res.status(500).send()
}
})
2. Use findOne() method instead of findById
If you want a simpler solution, don't use findById because the function expects a valid ObjectId. We can use findOne() method :
app.get('/users/:id', async (req, res) => {
const _id = req.params.id
try {
const user = await User.findOne({_id : _id})
if (!user) {
return res.status(404).send()
}
res.send(user)
} catch (error) {
res.status(500).send()
}
})
(IMHO, the first solution is better though..)
Some helpful link :
https://docs.mongodb.com/manual/reference/method/ObjectId/
Can I determine if a string is a MongoDB ObjectID?
https://mongoosejs.com/docs/api.html#model_Model.findOne

How do i determine if mongodb is returning empty results

I designed an API to fetch users from my mongodb in MEAN stack using express and I need to determine if mongodb is returning an empty array. Below is my code:
router.get('/user/:name', function (req, res) {
users
.find({ name: { $regex: '.*' + req.params.name + '.*' } })
.exec(function (err, user) {
if (err) {
console.log(err)
} else if (user === null) {
console.log('user not found')
} else {
res.json(user)
console.log('user found')
}
})
})
I tried querying such that the results would be empty but the console still logs "user found." Please help!
You are using find. This function always returns an array, if no user is found it returns an empty array, which is never equal to null.
If you use the function findOne then a document or null will be returned.
Check the length of the result instead:
} else if (user.length === 0) {
If you want to retrieve only one document, you can use findOne:
users.findOne({name: {$regex: ".*"+req.params.name+".*"}}).exec( // ...
In that case your checks do not need to be changed.

In mongo how to get the current position of the record in the table with the total records for pagination?

I'm trying to return create a paginated list. I used graphql to query the data. With my query, I pass the number of records I need (In a variable named first) and the ID of the last fetched record (In a varible called after). Now I managed to write a query (Note that I used mongoose) to fetch the records. Now what I need to do is get the relavant information to perform the pagination like hasNextPage, hasPreviousPage, currentPage and totalPages.
To get most of these information I need to get the total number of records in the database. To do that I need to send another db request.
I also need to know the position of the record in the table. No idea how.
Here's the query:
new Promise((resolve, reject) =>
Company.where('_id')
.gt(after)
.limit(first)
.lean()
.exec((error, doc) => {
if (error) {
reject(error);
}
resolve({
edges: doc,
pageInfo: {
hasNextPage: '...',
hasPreviousPage: '...',
currentPage: '...',
totalPages: '...'
}
});
}))
Any idea how to do this efficiently?
you can try this module mongoose-paginate
here what i uses, for pagination,
var current = req.query.filter.current;
var limit = req.query.filter.limit;
console.log('params.query.filter.current',current);
var skip = Number(limit)*Number(current)-Number(limit);
console.log('skip::',skip);
Cours.find({'attributes.version.status': true}).skip(skip).limit(limit).sort({_id:'asc'}).exec(function (err, resulta) {
if (err) {
console.log('erreur trouverCours');
res.json({
protecteds: err
});
}
console.log('cours ::', resulta);
res.json({
"data": resulta
});
});

Mongoose - always return null instead of an error when no result found?

Why mongoose always return null instead of an error when no result found?
Person.findOne({ 'name': 'Ghost' }, function (err, person) {
console.log(err); // null
if (err) return handleError(err);
});
The person 'Ghost' does not exist in my db so I am expecting an error but I get null instead. Why? How can get get an error instead?
Because mongoose only return an error when there is an error, not finding any result is not an error. You can handle the response if you got null, like :
Person.findOne({ 'name': 'Ghost' }, function (err, person) {
if(person === null) {
console.log('No results found');
}
if (err) return handleError(err);
});
or
Person.findOne({ 'name': 'Ghost' }, function (err, person) {
if (err) {
return handleError(err);
} else if (person) {
return person;
} else {
return 'No results found';
 }
});
Mongoose use err for internal errors like failing validation etc. If query complete successfully (with results or not) err will contain null.
What documentation says:
Anywhere a callback is passed to a query in Mongoose, the callback
follows the pattern callback(error, results). What results is depends
on the operation: For findOne() it is a potentially-null single
document, find() a list of documents, count() the number of documents,
update() the number of documents affected, etc. The API docs for
Models provide more detail on what is passed to the callbacks.

How can I sort data in descending order using sails-js?

I want my data sort by "createdAt" column . Sometime in ascending order and also descending order also. I am using mongo db .
Currently, I have created app which have feature like Users data can be sorted by admin request. Admin can view data in ascending order or by descending order.
I have tried option by using sort parameter which gives me data every time in same order. How can I change as admin per admin request.
Please review below that I have tried.
var options = {
limit: request.body.limit || undefined,
skip: request.body.skip || undefined,
sort: request.body.sort || "createdAt",
where: request.body.where || undefined
};
Users.find(options, function (err, Users) {
if (Users === undefined) {
return response.json({message: "No data Found"});
}
else if (err) {
return response.json({message: err});
} else {
return response.json({data: Users});
}
});
try this
var myQuery = Users.find(options);
// if you want to sort in descending order
myQuery.sort('createdAt DESC');
// if you want to sort in ascending order
myQuery.sort('createdAt ASC')
myQuery.exec(function callBack(err,results){
console.log(results)
});
You have to do something like this:
.sort({ createdAt: 'desc' })
In your case:
var options = {};
options[request.body.sort || "createdAt"] = 'desc'; // or 'asc'
var options = {
limit: request.body.limit || undefined,
skip: request.body.skip || undefined,
sort: request.body.sort || "createdAt desc", // Please mention here, desc or asc followed by space with column name
where: request.body.where || undefined
};
Users.find(options, function (err, Users) {
if (Users === undefined) {
return response.json({message: "No data Found"});
}
else if (err) {
return response.json({message: err});
} else {
return response.json({data: Users});
}
});
Better to use promises. Keep the code simple and easy to understand.
let condition = {
someField: 'value',
someOtherField: 'value'
}
let limit = 10;
let skip = 10;
Users.find(condition)
.limit(limit)
.skip(skip)
.sort({createdAt: 1})
.then(result => {
// do whatever you need to do with the result here
console.log(result)
})
For descending sort, change the sort clause to
.sort({createdAt: 0})
in my case working code below
var posts =Posts.find();
posts.sort('createdAt DESC');
posts.exec(function(err, success) {
if(err) {
res.status(500);
return res.view('500', {data: err});
}
res.json(success)
})