pager.js 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250
  1. const datefns = require('date-fns');
  2. const ObjectId = require('mongoose').Types.ObjectId;
  3. /**
  4. * Get table header of the model
  5. * @param {Model} model
  6. */
  7. function getHeaders(model, full) {
  8. let headers = Object.keys(model.schema.paths).filter((key) => {
  9. //TODO
  10. return key.substr(0, 1) != '_';
  11. }).map((key) => {
  12. let options = model.schema.path(key).options || {};
  13. return {
  14. data: key,
  15. title: options.desc || key,
  16. orderable: !(options.orderable === false),
  17. searchable: options.searchable || false,
  18. listingOrder: options.listingOrder || 1000,
  19. listing: options.listing,
  20. unit: options.unit,
  21. type: getType(options.type),
  22. }
  23. })
  24. if (!full) {
  25. headers = headers.filter(function (obj) {
  26. if (obj.listing === false) {
  27. return false;
  28. } else return true;
  29. });
  30. }
  31. headers = headers.sort((a, b) => {
  32. return a.listingOrder - b.listingOrder;
  33. });
  34. //console.table(headers);
  35. return headers;
  36. }
  37. function getType(typeFunc) {
  38. let type;
  39. switch (typeFunc) {
  40. case Number:
  41. type = "number";
  42. break;
  43. case String:
  44. type = 'string';
  45. break;
  46. case Date:
  47. type = 'date';
  48. break;
  49. case Boolean:
  50. type = 'boolean';
  51. break;
  52. default:
  53. type = "other";
  54. }
  55. return type;
  56. }
  57. function getHeadersBuilder(model) {
  58. return (req, res, next) => {
  59. (async () => {
  60. let headers = await getHeaders(model);
  61. res.json(headers);
  62. //res.json({ page, length, draw, recordsFiltered, recordsTotal, data: data.length });
  63. })().catch(next);
  64. }
  65. }
  66. /**
  67. *
  68. * @param {Model} model
  69. * @returns
  70. */
  71. function getListBuilder(model, populates, isOwnerFunc) {
  72. /**
  73. *
  74. * @param {import("express").Request} req
  75. * @param {import("express").Response} res
  76. * @param {import("express").NextFunction} next
  77. */
  78. function f(req, res, next) {
  79. (async () => {
  80. let { page, length, draw, search, filters } = req.query;
  81. //let paths = model.schema.paths;
  82. let schema = model.schema;
  83. let baseQuery = req.query.base || {};
  84. let query = Object.assign({}, baseQuery);
  85. //filters
  86. try {
  87. filters = JSON.parse(filters);
  88. //console.log('filters: ', filters);
  89. Object.keys(filters)
  90. .filter(data => schema.paths[data]) //filter not in schema
  91. .filter(data => {
  92. let value = filters[data];
  93. if (value === undefined) return false; //undefined
  94. if (Array.isArray(value) && value.length <= 0) return false;
  95. return true;
  96. }).forEach(data => {
  97. //console.log('data', data);
  98. let options = schema.path(data).options;
  99. //console.log('options', options);
  100. let value = filters[data];
  101. if (Array.isArray(value) && options.type == Date) {
  102. query[data] = {};
  103. if (value[0]) {
  104. query[data].$gte = datefns.startOfDay(new Date(value[0]));
  105. }
  106. if (value[1]) {
  107. query[data].$lte = datefns.endOfDay(new Date(value[1]));
  108. }
  109. } else if (Array.isArray(value)) {
  110. if (query[data]) {
  111. query[data].$in = value;
  112. } else {
  113. query[data] = { $in: value };
  114. }
  115. } else {
  116. query[data] = value;
  117. }
  118. })
  119. } catch (e) {
  120. console.warn(e);
  121. }
  122. //search
  123. if (search) { search = search.trim(); }
  124. if (search) {
  125. search = escapeRegExp(search);
  126. let match = { $regex: new RegExp(search, 'i') };
  127. let searchable = getHeaders(model).filter(h => h.searchable);
  128. if (searchable.length > 0) {
  129. query.$or = searchable.map(header => {
  130. let field = {};
  131. field[header.data] = match;
  132. return field;
  133. });
  134. if (ObjectId.isValid(search)) {
  135. query.$or.push({ _id: search }); // add id search
  136. }
  137. }
  138. }
  139. //console.log('search: ', search);
  140. //console.log('query : ', JSON.stringify(query));
  141. //pagination
  142. page = parseInt(page) || 1;
  143. if (page <= 0) page = 1;
  144. length = parseInt(length) || 20;
  145. if (length <= 0) length = 20;
  146. else if (length > 500) length = 500;
  147. draw = parseInt(draw) || 0;
  148. let start = (page - 1) * length
  149. let recordsTotal = await model.countDocuments(baseQuery);
  150. let recordsFiltered = await model.countDocuments(query);
  151. // console.log('baseQuery', baseQuery);
  152. // console.log('query', query);
  153. /** @type {Query} */
  154. let theQuery = model.find(query)
  155. .skip(start)
  156. .limit(length);
  157. //order
  158. let { orderBy, order } = req.query;
  159. if (orderBy) {
  160. if (order != 'asc' && order != 'desc') order = 'desc';
  161. let sort = {};
  162. sort[orderBy] = order;
  163. theQuery.sort(sort)
  164. }
  165. //populate
  166. if (populates) {
  167. populates.forEach(p => theQuery.populate(p))
  168. }
  169. let data = await theQuery.exec();
  170. data = data.map(item => item.toObject());
  171. if (isOwnerFunc) {
  172. data.forEach(item => {
  173. item.isOwner = isOwnerFunc(item, req);
  174. })
  175. }
  176. res.json({ page, length, draw, recordsFiltered, recordsTotal, data, filters, query });
  177. //res.json({ page, length, draw, recordsFiltered, recordsTotal, data: data.length });
  178. })().catch(next);
  179. }
  180. return f;
  181. }
  182. function escapeRegExp(str) {
  183. var len;
  184. var s;
  185. var i;
  186. var RE_CHARS = /[-\/\\^$*+?.()|[\]{}]/g; // eslint-disable-line no-useless-escape
  187. // Check if the string starts with a forward slash...
  188. if (str[0] === '/') {
  189. // Find the last forward slash...
  190. len = str.length;
  191. for (i = len - 1; i >= 0; i--) {
  192. if (str[i] === '/') {
  193. break;
  194. }
  195. }
  196. }
  197. // 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]`:
  198. if (i === void 0 || i <= 0) {
  199. return str.replace(RE_CHARS, '\\$&');
  200. }
  201. // We need to de-construct the string...
  202. s = str.substring(1, i);
  203. // Only escape the characters between the `/`:
  204. s = s.replace(RE_CHARS, '\\$&');
  205. // Reassemble:
  206. str = str[0] + s + str.substring(i);
  207. return str;
  208. }
  209. module.exports = { getHeaders, getListBuilder, getHeadersBuilder, escapeRegExp }