Using sequelize on my nodejs web app, I want to query posts using pagination (by date). Reading sequelize docs, they offer to use offset and limit.
Since I want to display the posts from new to old, I need to consider the date they were created. For example, if I limit the first query to 10 page, and before executing the second query a new post was created, the next query with offset of 10 will result a duplicate post from the last query.
How should I implement the pagination so it will support new entries?
The easiest way to do this is to use Sequelize's findAndCountAll
Post.findAndCountAll({
where: {...},
order: [...],
limit: 5,
offset: 0,
}).then(function (result) {
res.render(...);
});
Here, result has both the result of your query and count as result.rows and result.count. You can then increment the offset and use this for pagination.
Sequelize documentation for findAndCountAll
Try this:
const paginate = (query, { page, pageSize }) => {
const offset = page * pageSize;
const limit = pageSize;
return {
...query,
offset,
limit,
};
};
model.findAll(
paginate(
{
where: {}, // conditions
},
{ page, pageSize },
),
);
In order to avoid boilerplate code
If you want to have a stable pagination, don't paginate on row offset, since it's volatile, for the reason you mention.
You should aim for paginating on a value that is stable over time and use a where clause for filtering results. The best case would be if you have an auto-incrementing id, but the post date could also be reasonable.
Something like:
Post.findAll({
where: { createdDate: { $lt: previousDate },
limit: 10
})
You need to keep track of previousDate for this ofc. This approach also has some caveats, and you may need to combine it with client-side de-duplication.
Here is a blog post that probably has all the answers you need:
Pagination: You're (Probably) Doing It Wrong
With findAndCountAll here count is useful for pagination, from this total count we can limit as we want and also with async and await
let resAccidents = await ModalName.findAndCountAll({ where: { createdByID: employeeID }, offset: 0, limit: 10 });
this will return a count of total records as per where condition and 1st 10 records of it, then increase the value of offset to fetch further records.
You can simply do that
let limit = 10
let offset = 0 + (req.body.page - 1) * limit
Posts.findAndCountAll({
offset: offset,
limit: limit,
order: [
['date', 'ASC']
]
}).then(async result => {
return res.status(200).json({
status: true,
message: res.__('success'),
innerData: result
})
})
.catch(err => {
return validator.InvalidResponse(res, `${err}`)
})
Try this instead:
db.findAll({
offset: page_no,// your page number
limit:25,// your limit
This one solved my issue.
export const paginate = (query, schema) => {
let page = query.page ? query.page - 1 : 0;
page = page < 0 ? 0 : page;
let limit = parseInt(query.limit || 10);
limit = limit < 0 ? 10 : limit;
const offset = page * limit;
const where = {};
delete query.page;
delete query.limit;
Object.keys(schema).forEach((key) => {
schema[key] && query[key] ? (where[key] = query[key]) : null;
});
return {
where: where,
offset,
limit,
};
};
#Get()
findAll(#Query() query): unknown {
return this.model.findAll(paginate(query, {xx:1}));
}
/model?xx=yy&page=1&limit=5
var defered = Q.defer();
const offset = queryString.offset * queryString.limit;
const limit = queryString.limit;
var queryWhere = { class_id: { $ne: null }, section_id: { $ne: null } };
var searchClass = {};
var searchSection = {};
if (queryString) {
if (queryString.class && queryString.class !== "") {
searchClass = { class_id: { $eq: queryString.class } };
} else if (queryString.class && queryString.class === "") {
searchClass = { class_id: { $ne: null } };
}
if (queryString.section && queryString.section !== "") {
searchSection = { section_id: { $eq: queryString.section } };
} else if (queryString.section && queryString.section === "") {
searchSection = { section_id: { $ne: null } };
}
}
queryWhere = {
$and: [[searchClass], [searchSection]]
};
const schoolDB = require("../../db/models/tenant")(schema);
const Student = schoolDB.model("Student");
Student.findAll({
attributes: [
"id",
"first_name",
"last_name",
"profile_image_url",
"roll_number",
"emergency_contact_number"
],
offset: offset,
limit: limit,
where: queryWhere,
order: [["roll_number", "ASC"]]
})
.then(result => {
defered.resolve(result);
})
.catch(err => {
defered.reject(err);
});
Recommended using Sequelize's own operators
var defered = Q.defer();
const offset = queryString.offset * queryString.limit;
const limit = queryString.limit;
var queryWhere = { class_id: { $ne: null }, section_id: { $ne: null } };
var searchClass = {};
var searchSection = {};
if (queryString) {
if (queryString.class && queryString.class !== "") {
searchClass = { class_id: { $eq: queryString.class } };
} else if (queryString.class && queryString.class === "") {
searchClass = { class_id: { $ne: null } };
}
if (queryString.section && queryString.section !== "") {
searchSection = { section_id: { $eq: queryString.section } };
} else if (queryString.section && queryString.section === "") {
searchSection = { section_id: { $ne: null } };
}
}
queryWhere = {
$and: [[searchClass], [searchSection]]
};
const schoolDB = require("../../db/models/tenant")(schema);
const Student = schoolDB.model("Student");
Student.findAll({
attributes: [
"id",
"first_name",
"last_name",
"profile_image_url",
"roll_number",
"emergency_contact_number"
],
offset: offset,
limit: limit,
where: queryWhere,
order: [["roll_number", "ASC"]]
})
.then(result => {
defered.resolve(result);
})
.catch(err => {
defered.reject(err);
});
Related
i have a controller and i want to make pagination with 5 records per page. How can i do it with Nodejs i really need help.
const getPagination = (page, size) => {
const limit = size ? +size : 5; // Fetch 5 records
const offset = page ? page * limit : 0;// Start from page 0
return { limit, offset };
};
// Find all car with condition and how can i add pagination ?
export function findAllCar( req, res){
const name = req.query.name;
const color = req.query.color;
const brand = req.query.brand;
var condition = name ? {
name: { [Op.iLike]: `%${name}%` },
color: { [Op.iLike]: `%${color}%` },
brand: { [Op.iLike]: `%${brand}%` },
} : null;
Car.findAll({ where: condition })
.then(data => {
res.send(data);
})
.catch(err => {
res.status(500).send({
message:
err.message || "Some error occurred while retrieving CARS."
});
});
}
you need to add limit and offset inside the query.
var condition = name ? {
name: { [Op.iLike]: `%${name}%` },
color: { [Op.iLike]: `%${color}%` },
brand: { [Op.iLike]: `%${brand}%` },
} : null;
const paginate = (query, { page, pageSize }) => {
const offset = page * pageSize;
const limit = pageSize;
return {
...query,
offset,
limit,
};
};
model.findAll(
paginate(
{
where: condition
},
{ page, pageSize },
),
);
Also, you can refer to this here
you can use this
model.findAll({
limit: 5,
offset: 0,
where: {}, // conditions
});
I am trying to retrieve users based on role but with every role queried, I want to also retrieve admin too. I am not sure how to go about it. This is how I currently retrieve based on role
public async listAllStaffs(query: ListStaffsRequestQueryDTO) {
var conditions = { }
if (query.role) {
conditions = { role: query.role, 'hub.id': query.merchant };
} else {
conditions = {'hub.id': query.merchant };
}
const data = { page: query.page, limit: query.limit, conditions: conditions};
const all = await this.list(data);
const pagination = {
page: all.page,
limit: all.limit,
rowCount: all.rowCount,
pageCount: all.pageCount
};
const staffs = all.staffs;
return { staffs, pagination };
}
async list(query: PaginationQuery): Promise<any> {
const page = Number(query.page) - 1 || 0;
let limit = Number(query.limit) || 20;
const offset = page * limit;
const sort = query.sort || 'createdAt';
const archived = this.convertArchived(query.archived);
const conditions = {
...query.conditions,
...(!archived
? { deletedAt: undefined }
: { deletedAt: { $ne: undefined } })
};
const staffs = await this.model
.find(conditions)
.select(query.projections)
.skip(offset)
.limit(limit)
.sort(sort);
const totalDocuments = await this.model.countDocuments(conditions);
const result = {
staffs,
page: Number(page) + 1,
limit: Number(limit),
rowCount: Number(totalDocuments),
pageCount: Math.ceil(Number(totalDocuments) / limit)
};
return result;
}
So if role is operation for example, I want to also return users with admin
So you have both operation staff and admin staff returned.
Use $in operator, to look for users in multiple roles, like this:
if (query.role) {
conditions = { role: { "$in": [query.role, "admin"] }, 'hub.id': query.merchant
};
}
I use Mongoose Paginate V2 in nodeJs, but i have a question that how to make a regex filter with Boolean type?
My Code is
exports.allData = (req, res, next) => {
const { page, size, title, show } = req.query;
var condition = {}
if(title){
condition.title = { $regex: new RegExp(title), $options: "i" }
}
if(tayang){
condition.show = { $regex: new RegExp(show), $options: "true|false" }
}
const { limit, offset } = getPagination(page, size);
var options = {
populate: [
{
path: 'user',
select: 'username'
}],
sort: ({ createdAt: -1 })
};
Article.paginate(condition, { offset, limit, options })
.then((data) => {
res.send({
totalItems: data.totalDocs,
articles: data.docs,
totalPages: data.totalPages,
currentPage: data.page - 1,
});
})
.catch((err) => {
logger.error(req.method + ": " + req.originalUrl + ", message: " + err.message)
next(createError.InternalServerError())
});
};
I want to filter data "show" field is true or false with regex. Thanks before.
You can check if the show query param is present and use the $eq operator to construct the query.
if ('show' in req.query) {
condition.show = { $eq: show === 'true' }
}
I have to implement Nested orderby using sequelize but its not working below is my code
This whole code is working for me but I need to add sorting for a column reported_by which is present in Ticket table by reported_user_id name.
Below given images shows the hierarchy of database
TicketAssignment Table
Ticket Table
User table
TicketAssignment Table - ticket_id(FK) -> Ticket Table - id(PK)
Ticket Table - reported_user_id(FK) -> User Table - id(PK)
[let query = req.body.q;
let err, tickets;
let whereObj = {};
if (req.body.statusForTicket > 0) {
whereObj.status = 3;
} else {
whereObj.status = { [Op.in]: [0, 1, 2, 4, 5] };
}
if (req.user.role_id == 9) {
whereObj.suppport_company_id = req.user.company_id;
} else {
whereObj.engineer_user_id = req.user.id;
}
let whereObjInc = {};
if (query.trim() != "") {
whereObjInc.title = { [Op.like]: '%' + query.trim() + '%' };
}
if (req.body.date_from !== -1 && req.body.date_to !== -1) {
if (req.body.date_from != "") {
whereObjInc.createdAt = { [Op.gte]: req.body.date_from };
}
if (req.body.date_to != "") {
whereObjInc.createdAt = { [Op.lte]: req.body.date_from };
}
if (req.body.date_to != "" && req.body.date_from != "") {
whereObjInc.createdAt = { [Op.between]: [req.body.date_from, req.body.date_to] };
}
}
if (req.body.is_archive !== -1) {
whereObjInc.is_archive = req.body.is_archive;
}
let customOrder = [[order_by, order_by_ASC_DESC]];
if (order_by == 'title') {
customOrder = [[{ model: Ticket }, order_by, order_by_ASC_DESC]];
}
let orderByCondition = [[order_by, order_by_ASC_DESC]];
if (order_by == 'company') {
orderByCondition = [[{ model: Company, as: 'reported', }]];
//orderByCondition = [[{ model: Company, as: 'reported' }, 'company_name', order_by_ASC_DESC]];
}
if (order_by == 'accountable') {
orderByCondition = [[{ model: Company, as: 'accountable' }, 'company_name', order_by_ASC_DESC]];
}
if (order_by == 'reported_by') {
orderByCondition = [[{ model: Ticket},[{ model: User }, 'first_name', order_by_ASC_DESC]]];
}
let order_type = ['company', 'accountable', 'reported_by'];
if (order_type.includes(order_by)) {
[err, tickets] = await to(TicketAssignment.findAll({
where: whereObj,
include: [{ model: Ticket, where: whereObjInc }],
limit: limit,
offset: offset,
order: orderByCondition,
}));
}
Try it from your self. Try it from your self.
I'm using the Vue-table2 for rendering the table. https://github.com/ratiw/vuetable-2
<vuetable ref="vuetable"
:api-url= "apiurl"
:fields="fields">
</vuetable>
My Server Api Response doesn't have any Pagination response in it . The data returned by server is
{
"data":[
{
"id":22535,
"message":"Message1",
"message_type":"tag1",
"time":"2018-08-13T14:41:57Z",
"username":"rahuln"
},
{
"id":22534,
"message":"Message2",
"message_type":"tag2",
"time":"2018-08-13T14:02:27Z",
"username":"govindp"
},
..................
],
"error":null,
"success":true
}
This is the first time I'm Using Vue-js. How can i add the pagination into it and still using vue-table2.
Thanks In advance.
Since you don't have pagination values you must insert it we gonna trick vue-table like this
<template>
<div>
<vuetable ref="vuetable" api-url="/api/ahmed" :fields="fields" pagination-path="" #vuetable:pagination-data="onPaginationData" #vuetable:load-success="loadSuccess">
</vuetable>
<vuetable-pagination ref="pagination" #vuetable-pagination:change-page="onChangePage"></vuetable-pagination>
</div>
</template>
<script>
import Vuetable from 'vuetable-2/src/components/Vuetable'
import VuetablePagination from 'vuetable-2/src/components/VuetablePagination'
export default {
components: {
Vuetable,
VuetablePagination,
},
data() {
return {
fields: ['name', 'email', 'birthdate', 'nickname', 'gender', '__slot:actions'],
allData: false,
currentPage: 1,
}
},
mounted() {
},
methods: {
onPaginationData(paginationData) {
this.$refs.pagination.setPaginationData(paginationData)
},
loadSuccess(data) {
this.$refs.vuetable.$nextTick(()=>{
if (!this.allData) {
this.allData = data;
}
if (!data.data.per_page) {
data = this.setData(this.currentPage);
this.$refs.vuetable.loadSuccess(data);
}
})
},
setData(Page) {
var data = JSON.parse(JSON.stringify(this.allData));
var total = data.data.data.length;
var perPage = 10;
var currentPage = Page;
var lastPage = parseInt(total / perPage) + ((total % perPage) === 0 ? 0 : 1)
var from = parseInt((currentPage - 1) * perPage) + 1;
var to = from + perPage - 1;
to = to > total ? total : to;
console.log(from,to)
var newData = this.allData.data.data.filter(function(element, index) {
if (index >= from-1 && index <= to-1) {
console.log(index,from,to)
return true;
}
return false;
})
// console.log(newData)
// return newData;
data.data = {
"total": total,
"per_page": perPage,
"current_page": currentPage,
"last_page": lastPage,
"next_page_url": "",
"prev_page_url": null,
"from": from,
"to": to,
data: newData
}
// console.log(data)
this.currentPage = Page;
this.$refs.vuetable.loadSuccess(data);
return data;
},
onChangePage(page) {
this.setData(page);
}
}
}
</script>