Confusion in Query API of mongoose - node.js

Can anyone please tell me why the given below code(PART-1) works fine whereas gives error that posts.skip is not a function? in PART-2
I am wondering that why in PART-1 code it returned Query instance whereas I am getting an array of posts.
PART-1
const query = MyModel.find(); // `query` is an instance of `Query`
query.setOptions({ lean : true });
query.collection(MyModel.collection);
query.where('age').gte(21).exec(callback);
PART-2
const posts = await PostModel.find({});
posts.skip((page - 1) * 10).limit(10);
Basically I am trying to refactor this code so that I don't have to each time use pagination logic and achieve that by writing a pagination util function where the function will have first params a Query instance of mongoose
getAllPost: async (req, res, next) => {
const pagination = req.query.pagination ? parseInt(req.query.pagination) : 10
const currentPage = req.query.page ? parseInt(req.query.page) : 1
try {
const posts = await PostModel.find({}).lean()
.skip((currentPage - 1) * pagination)
.limit(pagination)
.populate('userId', ['name.firstName', 'name.lastName', 'email', 'isAdmin'])
.sort({ updatedAt: -1 })
.exec()
if (!posts.length) {
return res.status(HttpStatus.NOT_FOUND).json({ message: 'No posts found' })
}
return res.status(HttpStatus.OK).json({ posts: posts })
} catch (error) {
HANDLER.handleError(res, error)
}
},
Util function like: (For example)
module.exports = {
paginate: async (req, query, next) => {
const options= {
pagination: req.query.pagination ? parseInt(req.query.pagination) : 10,
currentPage: req.query.page ? parseInt(req.query.page) : 1
}
query.skip((currentPage-1)*pagination).limit(pagination)
query.exec((err, result)=>{ return result; })
}
}

I finally fixed this by implementing this paginate helper:
i.e /utils/paginate.js
module.exports = {
paginate: (req) => {
const query = {}
const pagination = req.query.pagination ? parseInt(req.query.pagination) : 10
const currentPage = req.query.page ? parseInt(req.query.page) : 1
query.skip = (currentPage - 1) * pagination
query.limit = pagination
return query
}
}
and use this helper function to paginate, by apssing as an argument to .find() in mongoose, for example:
const helper = require('../utils/paginate');
getAllPost: async (req, res, next) => {
try {
const posts = await PostModel.find({}, {}, helper.pagnate(req)).lean()
.populate('userId', ['name.firstName', 'name.lastName', 'email', 'isAdmin'])
.sort({ updatedAt: -1 })
.exec()
if (!posts.length) {
return res.status(HttpStatus.NOT_FOUND).json({ message: 'No posts found' })
}
return res.status(HttpStatus.OK).json({ posts: posts })
} catch (error) {
HANDLER.handleError(res, error)
}
}

Related

How to use Quick sort in MERN Stack project

I have got a task where I have to use Quicksort instead of using the (MongoDB) mongoose default sort method.
I could not find any solution on how to implement it.
Current code:
export const getProduct = expressAsyncHandler(async (req, res) => {
const pageSize = req.headers.referer === DASHBOARD_SCREEN_ROUTE ? 12 : 6;
const page = Number(req.query.pageNumber) || 1;
const sortOrder =
order === 'lowest'
? { price: 1 }
: order === 'highest'
? { price: -1 }
: order === 'toprated'
? { rating: -1 }
: { _id: -1 };
const products = await Product.find({})
.sort(sortOrder)
.skip(pageSize * (page - 1))
.limit(pageSize);
try {
res.send({ products });
} catch (error) {
console.log(error);
res.status(512).send(error.message);
}
});
Instead of .sort method, I have to use quick sort there.

ReferenceError: Cannot access 'savedPost' before initialization

My POST method which throws the ReferenceError:
postsRouter.post('/:postNumber', async (req, res, next) => {
const postDate = new Date().toUTCString()
const post = new Post({
...req.body,
poster: req.body.poster
? (req.body.poster.includes(config.AP) ? req.body.poster.replace(config.AP, '') : req.body.poster)
: 'nonamer',
date: postDate,
IP: req.socket.remoteAddress,
admin: req.body.poster.includes(config.AP) ? true : false
})
try {
const [savedPost, updatedBubble] = await Promise.all([
/* save() doesn't save the post with the new postnumber which it should (see the pre('save')
function in the next code block), it's only in the post that is being returned while saving. */
post.save().catch(e => { console.log('Task 1 failed!'); next(e) }),
// req.params.postNumber is the postnumber of the bubble a.k.a thread being replied to.
Post.findOneAndUpdate(
{ postNumber: req.params.postNumber },
{ $push: { replies: savedPost.postNumber } })
.catch(e => { console.log('Task 2 failed!'); next(e) })
])
res.status(201).json(savedPost)
console.log('!!!!! ' + req.socket.remoteAddress + ' saved a post: ' + JSON.stringify(savedPost))
} catch (e) {
next(e)
}
})
Also this mess of a code is in my Post model (which utilizes Counter model):
// Increase postnumber in counter collection's sole document, and insert it into new post.
postSchema.pre('save', function(next) {
let post = this
const ccc = Counter.findByIdAndUpdate(
{ '_id': 'postNumCounter' },
{ '$inc': { 'currentPostNum': 1 }}, // Works
{ new: true },
// inserting postnumber not working, fix
function(error, counter) {
post.postNumber = counter.currentPostNum
}
)
// Not working either
post.postNumber = ccc.currentPostNum
next()
})
My goal is I simply want to grab the currentPostNum after incrementing it, and insert that into the post before saving it.
Solved myself. I made the pre('save') function asynchronous as well, and ditched Promise.all in the controller POST method. Now everything works in tandem.
postSchema.pre('save', async function (next) {
let post = this
const counter = await Counter.findByIdAndUpdate(
{ '_id': 'postNumCounter' },
{ '$inc': { 'currentPostNum': 1 } }, // Works
{ new: true }
)
post.postNumber = counter.currentPostNum
next()
})

I want fetch the documents in reverse order from collection in mongoose

I want to fetch the latest doc from the collection first in mongoose. I am using mongoose with nodejs/expressjs
Also, I am doing pagination on the server side so I can't reverse this after fetching.
exports.getProjects = (req, res, next) => {
const page = +req.query.page || 1;
let totalItems;
Project
.find()
.countDocuments()
.then(numProducts => {
totalItems = numProducts;
return Project.find()
.skip((page - 1) * ITEMS_PER_PAGE)
.limit(ITEMS_PER_PAGE)
})
.then(result => {
return res.status(200).json({
projects: result,
currentPage: page,
itemCount: totalItems,
itemsPerPage: ITEMS_PER_PAGE,
});
})
.catch(err => {
console.log(err);
})
};
You can include sort in your query . you did not mention any attribute eligible for sorting like date, or any number. you can use aggregation as below
const projectscursor = await Project.aggregate([
{$match:Query},
{$sort:{field:1}},
{$skip:number},
{$limit:somenumber}
])
I guess you're looking for sort() function in mongoose. You can simply sort your collection by using any of the following techniques.
Project.find({}).sort('test').exec(function(err, docs) { ... });
Project.find({}).sort([['date', -1]]).exec(function(err, docs) { ... });
Project.find({}).sort({test: 1}).exec(function(err, docs) { ... });
Project.find({}, null, {sort: {date: 1}}, function(err, docs) { ... });
Try one of the upper techniques according to your requirements with the following code for proper pagination.
app.get('/projects', async (req, res) => {
// destructure page and limit and set default values
const { page = 1, limit = 10 } = req.query;
try {
// execute query with page and limit values
const projects = await Project.find()
.sort({ field : criteria})
.limit(limit * 1)
.skip((page - 1) * limit)
.exec();
// get total documents in the Project collection
const count = await Project.countDocuments();
// return response with posts, total pages, and current page
res.json({
projects,
totalPages: Math.ceil(count / limit),
currentPage: page
});
} catch (err) {
console.error(err.message);
}
});
Where
criteria can be "asc", "desc", "ascending", "descending", 1, or -1

incorrect response in angular

i'm new at nodejs & angular
here is my restAPI function :
exports.getPlanningStages = async (req, res, next) => {
const currentPage = req.query.page || 1;
const perPage = 2;
try {
const totalItems = await Planningstage.find().countDocuments();
const planningstages = await Planningstage.find()
.populate('creator')
.sort({ createdAt: -1 })
.skip((currentPage - 1) * perPage)
.limit(perPage);
res.status(200).json({
planningstages
});
} catch (err) {
if (!err.statusCode) {
err.statusCode = 500;
}
next(err);
}
};
at the console i see the correct response json array
the angular code is
planningstage.service.ts
private ps: Planningstage[] = [];
getPlanningStages() {
return this.httpService
.get<Planningstage[]>(
"http://localhost:8080/planningstage/getPlanningStage"
);
}
planningstage.component.ts
ngOnInit() {
this.fetch();
}
fetch() {
this.psService.getPlanningStages().subscribe(resData => {
console.log(resData);
});
the response at the browser
browser response
how can i read the currect response at my angular code
thank's
It seems your problem is with this line,
res.status(200).json({
planningstages
});
instead of above line, make like this
res.status(200).json(planningstages);

How to read `JSON` object in Angular

I have a API in my Node app like this:
exports.getPlanningStages = async (req, res, next) => {
const currentPage = req.query.page || 1;
const perPage = 10;
try {
const totalItems = await Planningstage.find().countDocuments();
const planningstages = await Planningstage.find()
.populate("creator")
.sort({ createdAt: -1 })
.skip((currentPage - 1) * perPage)
.limit(perPage);
// res.status(200).json(planningstages);
res.status(200).json({
planningstages,
totalItems: totalItems,
});
} catch (err) {
if (!err.statusCode) {
err.statusCode = 500;
}
next(err);
}
};
And this is my HTTP request in Angular:
fetch() {
this.psService.getPlanningStages().subscribe((resData) => {
console.log(resData);
});
}
This is the response of my request: console.log
How can I split this json into two separate variables?
Try using ES6 Destructuring assignment feature like this:
fetch() {
this.psService.getPlanningStages().subscribe((resData) => {
const [planningstages, totalitems] = resData;
});
}

Resources