@drax/crud-back 2.9.0 → 2.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -43,7 +43,7 @@ class AbstractFastifyController extends CommonController {
43
43
  const filters = [];
44
44
  filterArray.forEach((filter) => {
45
45
  const [field, operator, value] = filter.split(";");
46
- if (field && operator && (value !== undefined && value !== '')) {
46
+ if (field && operator && (operator === 'empty' || (value !== undefined && value !== ''))) {
47
47
  filters.push({ field, operator, value });
48
48
  }
49
49
  });
@@ -1,3 +1,3 @@
1
- const QueryFilterRegex = /^(?:[a-zA-Z0-9_.\-]+;(?:eq|like|ne|in|nin|gt|gte|lt|lte);[a-zA-Z0-9_.\-:\., áéíóúÁÉÍÓÚ]*)(?:\|[a-zA-Z0-9_.\-]+;(?:eq|like|ne|in|nin|gt|gte|lt|lte);[a-zA-Z0-9_.\-:\., áéíóúÁÉÍÓÚ]*)*$/;
1
+ const QueryFilterRegex = /^(?:[a-zA-Z0-9_.\-]+;(?:eq|like|ne|in|nin|gt|gte|lt|lte|empty);[a-zA-Z0-9_.\-:\., áéíóúÁÉÍÓÚ]*)(?:\|[a-zA-Z0-9_.\-]+;(?:eq|like|ne|in|nin|gt|gte|lt|lte|empty);[a-zA-Z0-9_.\-:\., áéíóúÁÉÍÓÚ]*)*$/;
2
2
  export default QueryFilterRegex;
3
3
  export { QueryFilterRegex };
@@ -133,7 +133,7 @@ class AbstractMongoRepository {
133
133
  else if (value) {
134
134
  query['$or'] = this._searchFields.map(field => ({ [field]: new RegExp(value.toString(), 'i') }));
135
135
  }
136
- MongooseQueryFilter.applyFilters(query, filters);
136
+ MongooseQueryFilter.applyFilters(query, filters, this._model);
137
137
  const items = await this._model
138
138
  .find(query)
139
139
  .limit(limit)
@@ -152,7 +152,7 @@ class AbstractMongoRepository {
152
152
  query['$or'] = this._searchFields.map(field => ({ [field]: new RegExp(search.toString(), 'i') }));
153
153
  }
154
154
  }
155
- MongooseQueryFilter.applyFilters(query, filters);
155
+ MongooseQueryFilter.applyFilters(query, filters, this._model);
156
156
  // console.log("Paginate Query", query)
157
157
  const sort = MongooseSort.applySort(orderBy, order);
158
158
  const populate = this._populateFields;
@@ -176,7 +176,7 @@ class AbstractMongoRepository {
176
176
  query['$or'] = this._searchFields.map(field => ({ [field]: new RegExp(search.toString(), 'i') }));
177
177
  }
178
178
  }
179
- MongooseQueryFilter.applyFilters(query, filters);
179
+ MongooseQueryFilter.applyFilters(query, filters, this._model);
180
180
  const item = this._model
181
181
  .findOne(query)
182
182
  .populate(this._populateFields)
@@ -194,7 +194,7 @@ class AbstractMongoRepository {
194
194
  query['$or'] = this._searchFields.map(field => ({ [field]: new RegExp(search.toString(), 'i') }));
195
195
  }
196
196
  }
197
- MongooseQueryFilter.applyFilters(query, filters);
197
+ MongooseQueryFilter.applyFilters(query, filters, this._model);
198
198
  const sort = MongooseSort.applySort(orderBy, order);
199
199
  const items = await this._model
200
200
  .find(query)
@@ -212,14 +212,14 @@ class AbstractMongoRepository {
212
212
  { name: new RegExp(search, 'i') },
213
213
  ];
214
214
  }
215
- MongooseQueryFilter.applyFilters(query, filters);
215
+ MongooseQueryFilter.applyFilters(query, filters, this._model);
216
216
  const sort = MongooseSort.applySort(orderBy, order);
217
217
  return this._model.find(query).limit(limit).sort(sort).cursor();
218
218
  }
219
219
  async groupBy({ fields = [], filters = [], dateFormat = 'day' }) {
220
220
  const query = {};
221
- MongooseQueryFilter.applyFilters(query, filters);
222
- // console.log("groupBy Query", query)
221
+ MongooseQueryFilter.applyFilters(query, filters, this._model);
222
+ // console.log("groupBy Query", query)
223
223
  // Obtener el schema para identificar campos de referencia y fechas
224
224
  const schema = this._model.schema;
225
225
  // Construir el objeto de agrupación dinámicamente
@@ -132,41 +132,61 @@ class AbstractSqliteRepository {
132
132
  async paginate({ page = 1, limit = 5, orderBy = '', order = 'desc', search = '', filters = [] }) {
133
133
  const offset = page > 1 ? (page - 1) * limit : 0;
134
134
  let where = "";
135
+ let params = [];
136
+ // SEARCH
135
137
  if (search && this.searchFields.length > 0) {
136
- where = ` WHERE ${this.searchFields.map(field => `${field} LIKE '%${search}%'`).join(" OR ")}`;
138
+ const searchConditions = this.searchFields
139
+ .map(field => `${field} LIKE ?`)
140
+ .join(" OR ");
141
+ where = ` WHERE (${searchConditions})`;
142
+ params.push(...this.searchFields.map(() => `%${search}%`));
137
143
  }
144
+ // FILTERS
138
145
  if (filters.length > 0) {
139
- where = SqlQueryFilter.applyFilters(where, filters);
146
+ const result = SqlQueryFilter.applyFilters(where, filters);
147
+ where = result.where;
148
+ params.push(...result.params);
140
149
  }
141
150
  const sort = SqlSort.applySort(orderBy, order);
142
- const rCount = this.db.prepare(`SELECT COUNT(*) as count
143
- FROM ${this.tableName} ${where}`).get();
144
- const items = this.db.prepare(`SELECT *
145
- FROM ${this.tableName} ${where} ${sort} LIMIT ?
146
- OFFSET ? `).all([limit, offset]);
151
+ // COUNT
152
+ const rCount = this.db
153
+ .prepare(`SELECT COUNT(*) as count FROM ${this.tableName} ${where}`)
154
+ .get(params);
155
+ // DATA
156
+ const items = this.db
157
+ .prepare(`SELECT * FROM ${this.tableName} ${where} ${sort} LIMIT ? OFFSET ?`)
158
+ .all([...params, limit, offset]);
147
159
  for (const item of items) {
148
160
  await this.decorate(item);
149
161
  }
150
162
  return {
151
- page: page,
152
- limit: limit,
163
+ page,
164
+ limit,
153
165
  total: rCount.count,
154
- items: items
166
+ items
155
167
  };
156
168
  }
157
169
  async find({ limit = 5, orderBy = '', order = 'desc', search = '', filters = [] }) {
158
170
  let where = "";
171
+ let params = [];
172
+ // SEARCH
159
173
  if (search && this.searchFields.length > 0) {
160
- where = ` WHERE ${this.searchFields.map(field => `${field} LIKE '%${search}%'`).join(" OR ")}`;
174
+ const searchConditions = this.searchFields
175
+ .map(field => `${field} LIKE ?`)
176
+ .join(" OR ");
177
+ where = ` WHERE (${searchConditions})`;
178
+ params.push(...this.searchFields.map(() => `%${search}%`));
161
179
  }
180
+ // FILTERS
162
181
  if (filters.length > 0) {
163
- where = SqlQueryFilter.applyFilters(where, filters);
182
+ const result = SqlQueryFilter.applyFilters(where, filters);
183
+ where = result.where;
184
+ params.push(...result.params);
164
185
  }
165
186
  const sort = SqlSort.applySort(orderBy, order);
166
- const rCount = this.db.prepare(`SELECT COUNT(*) as count
167
- FROM ${this.tableName} ${where}`).get();
168
- const items = this.db.prepare(`SELECT *
169
- FROM ${this.tableName} ${where} ${sort} LIMIT ? `).all([limit]);
187
+ const items = this.db
188
+ .prepare(`SELECT * FROM ${this.tableName} ${where} ${sort} LIMIT ?`)
189
+ .all([...params, limit]);
170
190
  for (const item of items) {
171
191
  await this.decorate(item);
172
192
  }
@@ -182,11 +202,17 @@ class AbstractSqliteRepository {
182
202
  }
183
203
  async search(value, limit = 1000) {
184
204
  let where = "";
205
+ let params = [];
185
206
  if (value && this.searchFields.length > 0) {
186
- where = ` WHERE ${this.searchFields.map(field => `${field} LIKE '%${value}%'`).join(" OR ")}`;
207
+ const searchConditions = this.searchFields
208
+ .map(field => `${field} LIKE ?`)
209
+ .join(" OR ");
210
+ where = ` WHERE (${searchConditions})`;
211
+ params.push(...this.searchFields.map(() => `%${value}%`));
187
212
  }
188
- const items = this.db.prepare(`SELECT *
189
- FROM ${this.tableName} ${where} LIMIT ${limit}`).all();
213
+ const items = this.db
214
+ .prepare(`SELECT * FROM ${this.tableName} ${where} LIMIT ?`)
215
+ .all([...params, limit]);
190
216
  for (const item of items) {
191
217
  await this.decorate(item);
192
218
  }
@@ -217,14 +243,24 @@ class AbstractSqliteRepository {
217
243
  }
218
244
  async findOne({ search = '', filters = [] }) {
219
245
  let where = "";
246
+ let params = [];
247
+ // SEARCH
220
248
  if (search && this.searchFields.length > 0) {
221
- where = ` WHERE ${this.searchFields.map(field => `${field} LIKE '%${search}%'`).join(" OR ")}`;
249
+ const searchConditions = this.searchFields
250
+ .map(field => `${field} LIKE ?`)
251
+ .join(" OR ");
252
+ where = ` WHERE (${searchConditions})`;
253
+ params.push(...this.searchFields.map(() => `%${search}%`));
222
254
  }
255
+ // FILTERS
223
256
  if (filters.length > 0) {
224
- where = SqlQueryFilter.applyFilters(where, filters);
257
+ const result = SqlQueryFilter.applyFilters(where, filters);
258
+ where = result.where;
259
+ params.push(...result.params);
225
260
  }
226
- const item = this.db.prepare(`SELECT *
227
- FROM ${this.tableName} ${where} LIMIT 1`).get();
261
+ const item = this.db
262
+ .prepare(`SELECT * FROM ${this.tableName} ${where} LIMIT 1`)
263
+ .get(params);
228
264
  if (item) {
229
265
  await this.decorate(item);
230
266
  }
@@ -234,12 +270,13 @@ class AbstractSqliteRepository {
234
270
  if (fields.length === 0) {
235
271
  throw new Error("At least one field is required for groupBy");
236
272
  }
237
- // Construir la cláusula WHERE con los filtros
238
273
  let where = "";
274
+ let params = [];
239
275
  if (filters.length > 0) {
240
- where = SqlQueryFilter.applyFilters(where, filters);
276
+ const result = SqlQueryFilter.applyFilters(where, filters);
277
+ where = result.where;
278
+ params.push(...result.params);
241
279
  }
242
- // Función para obtener el formato de fecha según SQLite
243
280
  const getDateFormatSQL = (field, format) => {
244
281
  const formats = {
245
282
  'year': `strftime('%Y', ${field})`,
@@ -251,39 +288,53 @@ class AbstractSqliteRepository {
251
288
  };
252
289
  return formats[format] || formats['day'];
253
290
  };
254
- // Determinar si cada campo es de fecha
255
291
  const isDateField = (field) => {
256
292
  const tableField = this.tableFields.find(tf => tf.name === field);
257
- return tableField ? tableField.type === 'TEXT' && (field.includes('Date') || field.includes('date')) : false;
293
+ return tableField
294
+ ? tableField.type === 'TEXT' && (field.includes('Date') || field.includes('date'))
295
+ : false;
258
296
  };
259
- // Construir los campos SELECT con formato de fecha si aplica
260
- const selectFields = fields.map(field => {
261
- if (isDateField(field)) {
262
- return `${getDateFormatSQL(field, dateFormat)} as ${field}`;
297
+ const isNumericField = (field) => {
298
+ const tableField = this.tableFields.find(tf => tf.name === field);
299
+ if (!tableField)
300
+ return false;
301
+ const type = tableField.type.toUpperCase();
302
+ return ['INTEGER', 'REAL', 'NUMERIC'].includes(type);
303
+ };
304
+ const selectParts = [];
305
+ const groupParts = [];
306
+ for (const field of fields) {
307
+ const tableField = this.tableFields.find(tf => tf.name === field);
308
+ if (!tableField) {
309
+ throw new Error(`Invalid field ${field}`);
310
+ }
311
+ if (isNumericField(field)) {
312
+ selectParts.push(`SUM(${field}) as ${field}`);
313
+ continue;
263
314
  }
264
- return field;
265
- }).join(', ');
266
- // Construir la cláusula GROUP BY
267
- const groupByFields = fields.map(field => {
268
315
  if (isDateField(field)) {
269
- return getDateFormatSQL(field, dateFormat);
316
+ const formatted = getDateFormatSQL(field, dateFormat);
317
+ selectParts.push(`${formatted} as ${field}`);
318
+ groupParts.push(formatted);
319
+ continue;
270
320
  }
271
- return field;
272
- }).join(', ');
273
- // Construir y ejecutar la query
321
+ selectParts.push(field);
322
+ groupParts.push(field);
323
+ }
324
+ const selectFields = selectParts.join(", ");
325
+ const groupByFields = groupParts.join(", ");
274
326
  const query = `
275
- SELECT ${selectFields}, COUNT(*) as count
276
- FROM ${this.tableName}
277
- ${where}
278
- GROUP BY ${groupByFields}
279
- ORDER BY count DESC
280
- `;
327
+ SELECT ${selectFields}, COUNT(*) as count
328
+ FROM ${this.tableName}
329
+ ${where}
330
+ ${groupByFields ? `GROUP BY ${groupByFields}` : ''}
331
+ ORDER BY count DESC
332
+ `;
281
333
  try {
282
- const result = this.db.prepare(query).all();
283
- // Decorar los items si tienen campos de población
284
- for (const item of result) {
285
- await this.decorate(item);
286
- }
334
+ const result = this.db.prepare(query).all(params);
335
+ // for (const item of result) {
336
+ // await this.decorate(item)
337
+ // }
287
338
  return result;
288
339
  }
289
340
  catch (e) {
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "2.9.0",
6
+ "version": "2.11.0",
7
7
  "description": "Crud utils across modules",
8
8
  "main": "dist/index.js",
9
9
  "types": "types/index.d.ts",
@@ -22,10 +22,10 @@
22
22
  "author": "Cristian Incarnato & Drax Team",
23
23
  "license": "ISC",
24
24
  "dependencies": {
25
- "@drax/common-back": "^2.8.0",
25
+ "@drax/common-back": "^2.11.0",
26
26
  "@drax/common-share": "^2.0.0",
27
27
  "@drax/identity-share": "^2.0.0",
28
- "@drax/media-back": "^2.9.0",
28
+ "@drax/media-back": "^2.11.0",
29
29
  "@graphql-tools/load-files": "^7.0.0",
30
30
  "@graphql-tools/merge": "^9.0.4",
31
31
  "mongoose": "^8.23.0",
@@ -46,5 +46,5 @@
46
46
  "tsc-alias": "^1.8.10",
47
47
  "typescript": "^5.9.3"
48
48
  },
49
- "gitHead": "2bc9b59a45c762bd32403dce14db2693be34dcc7"
49
+ "gitHead": "8919d31d4d9512e48ac461b0876dc85a5849daea"
50
50
  }
@@ -114,7 +114,7 @@ class AbstractFastifyController<T, C, U> extends CommonController {
114
114
  filterArray.forEach((filter) => {
115
115
  const [field, operator, value] = filter.split(";")
116
116
 
117
- if(field && operator && (value !== undefined && value !== '') ) {
117
+ if(field && operator && ( operator === 'empty' || (value !== undefined && value !== '')) ) {
118
118
  filters.push({field, operator, value})
119
119
  }
120
120
 
@@ -1,4 +1,4 @@
1
- const QueryFilterRegex = /^(?:[a-zA-Z0-9_.\-]+;(?:eq|like|ne|in|nin|gt|gte|lt|lte);[a-zA-Z0-9_.\-:\., áéíóúÁÉÍÓÚ]*)(?:\|[a-zA-Z0-9_.\-]+;(?:eq|like|ne|in|nin|gt|gte|lt|lte);[a-zA-Z0-9_.\-:\., áéíóúÁÉÍÓÚ]*)*$/
1
+ const QueryFilterRegex = /^(?:[a-zA-Z0-9_.\-]+;(?:eq|like|ne|in|nin|gt|gte|lt|lte|empty);[a-zA-Z0-9_.\-:\., áéíóúÁÉÍÓÚ]*)(?:\|[a-zA-Z0-9_.\-]+;(?:eq|like|ne|in|nin|gt|gte|lt|lte|empty);[a-zA-Z0-9_.\-:\., áéíóúÁÉÍÓÚ]*)*$/
2
2
 
3
3
  export default QueryFilterRegex
4
4
  export {QueryFilterRegex}
@@ -184,7 +184,7 @@ class AbstractMongoRepository<T, C, U> implements IDraxCrud<T, C, U> {
184
184
  query['$or'] = this._searchFields.map(field => ({[field]: new RegExp(value.toString(), 'i')}))
185
185
  }
186
186
 
187
- MongooseQueryFilter.applyFilters(query, filters)
187
+ MongooseQueryFilter.applyFilters(query, filters, this._model)
188
188
 
189
189
  const items = await this._model
190
190
  .find(query)
@@ -217,7 +217,7 @@ class AbstractMongoRepository<T, C, U> implements IDraxCrud<T, C, U> {
217
217
  }
218
218
  }
219
219
 
220
- MongooseQueryFilter.applyFilters(query, filters)
220
+ MongooseQueryFilter.applyFilters(query, filters, this._model)
221
221
 
222
222
  // console.log("Paginate Query", query)
223
223
 
@@ -249,7 +249,7 @@ class AbstractMongoRepository<T, C, U> implements IDraxCrud<T, C, U> {
249
249
  }
250
250
  }
251
251
 
252
- MongooseQueryFilter.applyFilters(query, filters)
252
+ MongooseQueryFilter.applyFilters(query, filters, this._model)
253
253
 
254
254
  const item = this._model
255
255
  .findOne(query)
@@ -278,7 +278,7 @@ class AbstractMongoRepository<T, C, U> implements IDraxCrud<T, C, U> {
278
278
  }
279
279
  }
280
280
 
281
- MongooseQueryFilter.applyFilters(query, filters)
281
+ MongooseQueryFilter.applyFilters(query, filters, this._model)
282
282
 
283
283
  const sort = MongooseSort.applySort(orderBy, order)
284
284
  const items = await this._model
@@ -309,9 +309,9 @@ class AbstractMongoRepository<T, C, U> implements IDraxCrud<T, C, U> {
309
309
  ]
310
310
  }
311
311
 
312
- MongooseQueryFilter.applyFilters(query, filters)
312
+ MongooseQueryFilter.applyFilters(query, filters, this._model)
313
313
 
314
- const sort = MongooseSort.applySort(orderBy, order)
314
+ const sort = MongooseSort.applySort(orderBy, order,)
315
315
 
316
316
  return this._model.find(query).limit(limit).sort(sort).cursor() as Cursor<T>;
317
317
  }
@@ -320,8 +320,8 @@ class AbstractMongoRepository<T, C, U> implements IDraxCrud<T, C, U> {
320
320
 
321
321
  const query = {}
322
322
 
323
- MongooseQueryFilter.applyFilters(query, filters)
324
- // console.log("groupBy Query", query)
323
+ MongooseQueryFilter.applyFilters(query, filters, this._model)
324
+ // console.log("groupBy Query", query)
325
325
 
326
326
  // Obtener el schema para identificar campos de referencia y fechas
327
327
  const schema = this._model.schema