const datefns = require('date-fns'); const ObjectId = require('mongoose').Types.ObjectId; /** * Get table header of the model * @param {Model} model */ function getHeaders(model, full) { let headers = Object.keys(model.schema.paths).filter((key) => { //TODO return key.substring(0, 1) != '_'; }).map((key) => { let options = model.schema.path(key).options || {}; return { data: key, title: options.desc || key, orderable: !(options.orderable === false), searchable: options.searchable || false, listingOrder: options.listingOrder || 1000, listing: options.listing, unit: options.unit, type: getType(options.type), } }) if (!full) { headers = headers.filter(function (obj) { if (obj.listing === false) { return false; } else return true; }); } headers = headers.sort((a, b) => { return a.listingOrder - b.listingOrder; }); //console.table(headers); return headers; } function getType(typeFunc) { let type; switch (typeFunc) { case Number: type = "number"; break; case String: type = 'string'; break; case Date: type = 'date'; break; case Boolean: type = 'boolean'; break; default: type = "other"; } return type; } /** * * @param {Model} model * @returns */ async function getListBuilder(params, model, populates) { let { page, length, search, filters } = params; //let paths = model.schema.paths; let schema = model.schema; let baseQuery = params.base || {}; let query = Object.assign({}, baseQuery); //filters try { // filters = JSON.parse(filters); //console.log('filters: ', filters); Object.keys(filters) .filter(data => schema.paths[data]) //filter not in schema .filter(data => { let value = filters[data]; if (value === undefined) return false; //undefined if (Array.isArray(value) && value.length <= 0) return false; return true; }).forEach(data => { //console.log('data', data); let options = schema.path(data).options; //console.log('options', options); let value = filters[data]; if (Array.isArray(value) && options.type == Date) { query[data] = {}; if (value[0]) { query[data].$gte = datefns.startOfDay(new Date(value[0])); } if (value[1]) { query[data].$lte = datefns.endOfDay(new Date(value[1])); } } else if (Array.isArray(value)) { if (query[data]) { query[data].$in = value; } else { query[data] = { $in: value }; } } else { query[data] = value; } }) } catch (e) { console.warn(e); } //search if (search) { search = search.trim(); } if (search) { search = escapeRegExp(search); let match = { $regex: new RegExp(search, 'i') }; let searchable = getHeaders(model).filter(h => h.searchable); if (searchable.length > 0) { query.$or = searchable.map(header => { let field = {}; field[header.data] = match; return field; }); if (ObjectId.isValid(search)) { query.$or.push({ _id: search }); // add id search } } } //console.log('search: ', search); //console.log('query : ', JSON.stringify(query)); //pagination page = parseInt(page) || 1; if (page <= 0) page = 1; length = parseInt(length) || 30; if (length <= 0) length = 30; else if (length > 500) length = 500; let start = (page - 1) * length let recordsTotal = await model.countDocuments(baseQuery); let recordsFiltered = await model.countDocuments(query); // console.log('baseQuery', baseQuery); // console.log('query', query); /** @type {Query} */ let theQuery = model.find(query) .skip(start) .limit(length); //order let { orderBy, order } = params; if (orderBy) { if (order != 'asc' && order != 'desc') order = 'desc'; let sort = {}; sort[orderBy] = order; theQuery.sort(sort) } //populate if (populates) { populates.forEach(p => theQuery.populate(p)) } let data = await theQuery.exec(); data = data.map(item => item.toObject()); return { page, length, recordsFiltered, recordsTotal, data, filters, query } } function escapeRegExp(str) { var len; var s; var i; var RE_CHARS = /[-\/\\^$*+?.()|[\]{}]/g; // eslint-disable-line no-useless-escape // Check if the string starts with a forward slash... if (str[0] === '/') { // Find the last forward slash... len = str.length; for (i = len - 1; i >= 0; i--) { if (str[i] === '/') { break; } } } // If we searched the string to no avail or if the first letter is not `/`, assume that the string is not of the form `/[...]/[guimy]`: if (i === void 0 || i <= 0) { return str.replace(RE_CHARS, '\\$&'); } // We need to de-construct the string... s = str.substring(1, i); // Only escape the characters between the `/`: s = s.replace(RE_CHARS, '\\$&'); // Reassemble: str = str[0] + s + str.substring(i); return str; } module.exports = { getHeaders, getListBuilder, escapeRegExp }