pager.js 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  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.substring(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. /**
  58. *
  59. * @param {Model} model
  60. * @returns
  61. */
  62. async function getListBuilder(params, model, populates) {
  63. let { page, length, search, filters } = params;
  64. //let paths = model.schema.paths;
  65. let schema = model.schema;
  66. let baseQuery = params.base || {};
  67. let query = Object.assign({}, baseQuery);
  68. //filters
  69. try {
  70. // filters = JSON.parse(filters);
  71. //console.log('filters: ', filters);
  72. Object.keys(filters)
  73. .filter(data => schema.paths[data]) //filter not in schema
  74. .filter(data => {
  75. let value = filters[data];
  76. if (value === undefined) return false; //undefined
  77. if (Array.isArray(value) && value.length <= 0) return false;
  78. return true;
  79. }).forEach(data => {
  80. //console.log('data', data);
  81. let options = schema.path(data).options;
  82. //console.log('options', options);
  83. let value = filters[data];
  84. if (Array.isArray(value) && options.type == Date) {
  85. query[data] = {};
  86. if (value[0]) {
  87. query[data].$gte = datefns.startOfDay(new Date(value[0]));
  88. }
  89. if (value[1]) {
  90. query[data].$lte = datefns.endOfDay(new Date(value[1]));
  91. }
  92. } else if (Array.isArray(value)) {
  93. if (query[data]) {
  94. query[data].$in = value;
  95. } else {
  96. query[data] = { $in: value };
  97. }
  98. } else {
  99. query[data] = value;
  100. }
  101. })
  102. } catch (e) {
  103. console.warn(e);
  104. }
  105. //search
  106. if (search) { search = search.trim(); }
  107. if (search) {
  108. search = escapeRegExp(search);
  109. let match = { $regex: new RegExp(search, 'i') };
  110. let searchable = getHeaders(model).filter(h => h.searchable);
  111. if (searchable.length > 0) {
  112. query.$or = searchable.map(header => {
  113. let field = {};
  114. field[header.data] = match;
  115. return field;
  116. });
  117. if (ObjectId.isValid(search)) {
  118. query.$or.push({ _id: search }); // add id search
  119. }
  120. }
  121. }
  122. //console.log('search: ', search);
  123. //console.log('query : ', JSON.stringify(query));
  124. //pagination
  125. page = parseInt(page) || 1;
  126. if (page <= 0) page = 1;
  127. length = parseInt(length) || 30;
  128. if (length <= 0) length = 30;
  129. else if (length > 500) length = 500;
  130. let start = (page - 1) * length
  131. let recordsTotal = await model.countDocuments(baseQuery);
  132. let recordsFiltered = await model.countDocuments(query);
  133. // console.log('baseQuery', baseQuery);
  134. // console.log('query', query);
  135. /** @type {Query} */
  136. let theQuery = model.find(query)
  137. .skip(start)
  138. .limit(length);
  139. //order
  140. let { orderBy, order } = params;
  141. if (orderBy) {
  142. if (order != 'asc' && order != 'desc') order = 'desc';
  143. let sort = {};
  144. sort[orderBy] = order;
  145. theQuery.sort(sort)
  146. }
  147. //populate
  148. if (populates) {
  149. populates.forEach(p => theQuery.populate(p))
  150. }
  151. let data = await theQuery.exec();
  152. data = data.map(item => item.toObject());
  153. return { page, length, recordsFiltered, recordsTotal, data, filters, query }
  154. }
  155. function escapeRegExp(str) {
  156. var len;
  157. var s;
  158. var i;
  159. var RE_CHARS = /[-\/\\^$*+?.()|[\]{}]/g; // eslint-disable-line no-useless-escape
  160. // Check if the string starts with a forward slash...
  161. if (str[0] === '/') {
  162. // Find the last forward slash...
  163. len = str.length;
  164. for (i = len - 1; i >= 0; i--) {
  165. if (str[i] === '/') {
  166. break;
  167. }
  168. }
  169. }
  170. // 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]`:
  171. if (i === void 0 || i <= 0) {
  172. return str.replace(RE_CHARS, '\\$&');
  173. }
  174. // We need to de-construct the string...
  175. s = str.substring(1, i);
  176. // Only escape the characters between the `/`:
  177. s = s.replace(RE_CHARS, '\\$&');
  178. // Reassemble:
  179. str = str[0] + s + str.substring(i);
  180. return str;
  181. }
  182. module.exports = { getHeaders, getListBuilder, escapeRegExp }