UNPKG

node-rigorous

Version:
118 lines (81 loc) 4.25 kB
const mongoose = require('mongoose'); const RigorousError = require('../../../facades/RigorousError'); const errorsMessages = require('../../../etc/errorsMessages'); const patch = require('./patch'); const helper_format_checker = require('../../../helpers/format_checker'); const rigorousConfig = require('../../../config'); /** * Smart Efficient Pagination: https://arpitbhayani.me/techie/fast-and-efficient-pagination-in-mongodb.html * (Skip is to CPU intensive: https://stackoverflow.com/questions/5539955/how-to-paginate-with-mongoose-in-node-js/23640287#23640287) */ function checkPaginationFilter(objectFilter) { if (!helper_format_checker.isNil(objectFilter._id)) { throw new RigorousError('This field is not needed in multiple result (do findOne) ! So do not use it because it is used in query of pagination'); } return objectFilter; } async function findResults(modelName, query, maxNbResult, reverse, params) { const { selectAttributesReturned } = params; const { virtualReturnedFields } = params; try { const op = mongoose.model(modelName).find(query) .select(selectAttributesReturned) .limit(maxNbResult) .sort({ updated_at: (reverse ? -1 : 1) }); if (virtualReturnedFields) { virtualReturnedFields.forEach((virtualFieldReturned) => { op.populate(virtualFieldReturned); }); } const results = await op.exec(); const resultJson = patch.leanWithId(results); return resultJson; } catch (err) { throw err; } } function buildPaginateQuery(objectTargeted, lastPaginateId, queryParam, reverse) { const query = {}; Object.keys(queryParam).forEach((key) => { query[key] = queryParam[key]; }); let include = ''; if (lastPaginateId === rigorousConfig.PAGINATION_LAST_ID || lastPaginateId === rigorousConfig.PAGINATION_INITIAL_ID) { include = 'e'; } query.updated_at = { [reverse ? `$lt${include}` : `$gt${include}`]: objectTargeted.updated_at }; return query; } async function buildObjectTargeted(modelName, lastPaginateId, queryParam, reverse) { let objectTargeted = null; if (lastPaginateId === rigorousConfig.PAGINATION_LAST_ID || lastPaginateId === rigorousConfig.PAGINATION_INITIAL_ID) { objectTargeted = await mongoose.model(modelName).findOne({ ...queryParam, updated_at: { [reverse ? '$lte' : '$gte']: reverse ? new Date(9900, 1, 1).getTime() : new Date(1, 1, 1).getTime() }, // handle lastPaginateId = initial or final }) .sort({ updated_at: (reverse ? -1 : 1) }) .exec(); } else { objectTargeted = await mongoose.model(modelName).findOne({ ...queryParam, _id: lastPaginateId, }) .exec(); } return objectTargeted; } /** * params.virtualReturnedFields = [{ path: 'owner', select: { xx: 1, yy: 1 } }] */ module.exports = async (modelName, queryParam, maxNbResult, lastPaginateId, reverse, params) => { if (helper_format_checker.isNil(params)) { throw new RigorousError(errorsMessages.OperationError); } if (helper_format_checker.isNil(params.selectAttributesReturned) && helper_format_checker.isObjectEmpty(params.selectAttributesReturned)) { throw new RigorousError(errorsMessages.OperationError); } checkPaginationFilter(queryParam); try { // We do not skip (=offset) for scalability reason: https://stackoverflow.com/questions/5539955/how-to-paginate-with-mongoose-in-node-js const objectTargeted = await buildObjectTargeted(modelName, lastPaginateId, queryParam, reverse); if (!objectTargeted) { // no data found (not error but it means initial query is empty, for exemple query all swits of lists but this list is empty) return []; } const query = buildPaginateQuery(objectTargeted, lastPaginateId, queryParam, reverse); const results = await findResults(modelName, query, maxNbResult, reverse, params); return results; } catch (errResult) { throw new RigorousError(errorsMessages.OperationError, errResult); } };