nodejs-rigorous
Version:
Rigorous Framework
125 lines (87 loc) • 4.61 kB
JavaScript
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;
}
};