UNPKG

nodejs-rigorous

Version:
125 lines (87 loc) 4.61 kB
const { RigorousError, errorTypes } = require('../../../factory/RigorousError/index'); const mongoose = require('mongoose'); const callbackResult = require('./callback_result'); const patch = require('./patch'); const helper_format_checker = require('../../../helpers/h_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; } function findResults(collectionName, query, maxNbResult, reverse, params) { const { selectAttributesReturned } = params; const { virtualReturnedFields } = params; return new Promise((resolve, reject) => { try { const op = mongoose.model(collectionName).find(query) .select(selectAttributesReturned) .limit(maxNbResult) .sort({ updated_at: (reverse ? -1 : 1) }); if (virtualReturnedFields) { virtualReturnedFields.forEach((virtualFieldReturned) => { op.populate(virtualFieldReturned); }); } op.exec((err, resultUnfiltered) => { callbackResult.exec(err, resultUnfiltered) .then((results) => { const resultJson = patch.leanWithId(results); return resolve(resultJson); }).catch((err2) => { return reject(err2); }); }); } catch (err) { reject(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(collectionName, lastPaginateId, queryParam, reverse) { let objectTargeted = null; if (lastPaginateId === rigorousConfig.PAGINATION_LAST_ID || lastPaginateId === rigorousConfig.PAGINATION_INITIAL_ID) { objectTargeted = await mongoose.model(collectionName).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(collectionName).findOne({ ...queryParam, _id: lastPaginateId, }) .exec(); } return objectTargeted; } /** * params.virtualReturnedFields = [{ path: 'owner', select: { xx: 1, yy: 1 } }] */ module.exports = async (collectionName, queryParam, maxNbResult, lastPaginateId, reverse, params) => { if (helper_format_checker.isNil(params)) { throw new RigorousError(errorTypes.RESPONSE_ERROR_OPERATION); } if (helper_format_checker.isNil(params.selectAttributesReturned) && helper_format_checker.isObjectEmpty(params.selectAttributesReturned)) { throw new RigorousError(errorTypes.RESPONSE_ERROR_OPERATION); } 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(collectionName, 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(collectionName, query, maxNbResult, reverse, params); return results; } catch (errResult) { throw errResult; } };