Spaces:
Running
Running
| class APIFeatures { | |
| constructor(query, queryString) { | |
| this.query = query; | |
| this.queryString = queryString; | |
| } | |
| filter() { | |
| // eslint-disable-next-line node/no-unsupported-features/es-syntax | |
| const queryObj = { ...this.queryString }; | |
| const excludedFields = ['page', 'sort', 'limit', 'fields']; | |
| excludedFields.forEach((el) => delete queryObj[el]); | |
| // Advanced filtering | |
| let queryStr = JSON.stringify(queryObj); | |
| queryStr = queryStr.replace(/\b(gte|gt|lte|lt)\b/g, (match) => `$${match}`); | |
| const parsedQuery = JSON.parse(queryStr); | |
| // If the user searches by name → use regex for partial match | |
| if (parsedQuery.nameAr) { | |
| parsedQuery.nameAr = { $regex: parsedQuery.nameAr, $options: 'i' }; // 'i' = ignore case | |
| } | |
| this.query = this.query.find(parsedQuery); | |
| return this; | |
| } | |
| sort() { | |
| if (this.queryString.sort) { | |
| const sortBy = this.queryString.sort.split(',').join(' '); | |
| this.query = this.query.sort(sortBy); | |
| } else { | |
| // Default: sort by newest first, with _id as tiebreaker for stable pagination | |
| // This ensures consistent ordering when multiple products have the same createdAt | |
| this.query = this.query.sort('-createdAt _id'); | |
| } | |
| return this; | |
| } | |
| limitFields() { | |
| if (this.queryString.fields) { | |
| const fields = this.queryString.fields.split(',').join(' '); | |
| this.query = this.query.select(fields); | |
| } else { | |
| // FIXME: exclude __v field by default | |
| this.query = this.query.select('-__v'); | |
| } | |
| return this; | |
| } | |
| paginate() { | |
| const page = this.queryString.page * 1 || 1; | |
| const limit = this.queryString.limit * 1 || 100; | |
| const skip = (page - 1) * limit; | |
| this.query = this.query.skip(skip).limit(limit); | |
| return this; | |
| } | |
| } | |
| module.exports = APIFeatures; | |