@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.
- package/dist/controllers/AbstractFastifyController.js +1 -1
- package/dist/regexs/QueryFilterRegex.js +1 -1
- package/dist/repository/AbstractMongoRepository.js +7 -7
- package/dist/repository/AbstractSqliteRepository.js +102 -51
- package/package.json +4 -4
- package/src/controllers/AbstractFastifyController.ts +1 -1
- package/src/regexs/QueryFilterRegex.ts +1 -1
- package/src/repository/AbstractMongoRepository.ts +8 -8
- package/src/repository/AbstractSqliteRepository.ts +144 -57
- package/tsconfig.tsbuildinfo +1 -1
- package/types/regexs/QueryFilterRegex.d.ts.map +1 -1
- package/types/repository/AbstractSqliteRepository.d.ts +2 -2
- package/types/repository/AbstractSqliteRepository.d.ts.map +1 -1
|
@@ -8,10 +8,13 @@ import type {
|
|
|
8
8
|
} from "@drax/crud-share";
|
|
9
9
|
import {randomUUID} from "node:crypto";
|
|
10
10
|
import {
|
|
11
|
-
SqlSort,
|
|
12
|
-
|
|
11
|
+
SqlSort,
|
|
12
|
+
SqlQueryFilter,
|
|
13
|
+
SqliteTableBuilder,
|
|
14
|
+
SqliteTableField,
|
|
15
|
+
SqliteErrorToValidationError
|
|
13
16
|
} from "@drax/common-back";
|
|
14
|
-
|
|
17
|
+
|
|
15
18
|
|
|
16
19
|
|
|
17
20
|
class AbstractSqliteRepository<T, C, U> implements IDraxCrud<T, C, U> {
|
|
@@ -195,31 +198,50 @@ class AbstractSqliteRepository<T, C, U> implements IDraxCrud<T, C, U> {
|
|
|
195
198
|
const offset = page > 1 ? (page - 1) * limit : 0
|
|
196
199
|
|
|
197
200
|
let where = ""
|
|
201
|
+
let params: any[] = []
|
|
202
|
+
|
|
203
|
+
// SEARCH
|
|
198
204
|
if (search && this.searchFields.length > 0) {
|
|
199
|
-
|
|
205
|
+
|
|
206
|
+
const searchConditions = this.searchFields
|
|
207
|
+
.map(field => `${field} LIKE ?`)
|
|
208
|
+
.join(" OR ")
|
|
209
|
+
|
|
210
|
+
where = ` WHERE (${searchConditions})`
|
|
211
|
+
|
|
212
|
+
params.push(...this.searchFields.map(() => `%${search}%`))
|
|
200
213
|
}
|
|
201
214
|
|
|
215
|
+
// FILTERS
|
|
202
216
|
if (filters.length > 0) {
|
|
203
|
-
|
|
217
|
+
|
|
218
|
+
const result = SqlQueryFilter.applyFilters(where, filters)
|
|
219
|
+
|
|
220
|
+
where = result.where
|
|
221
|
+
params.push(...result.params)
|
|
204
222
|
}
|
|
205
223
|
|
|
206
224
|
const sort = SqlSort.applySort(orderBy, order)
|
|
207
225
|
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
226
|
+
// COUNT
|
|
227
|
+
const rCount = this.db
|
|
228
|
+
.prepare(`SELECT COUNT(*) as count FROM ${this.tableName} ${where}`)
|
|
229
|
+
.get(params)
|
|
230
|
+
|
|
231
|
+
// DATA
|
|
232
|
+
const items = this.db
|
|
233
|
+
.prepare(`SELECT * FROM ${this.tableName} ${where} ${sort} LIMIT ? OFFSET ?`)
|
|
234
|
+
.all([...params, limit, offset]) as T[]
|
|
213
235
|
|
|
214
236
|
for (const item of items) {
|
|
215
237
|
await this.decorate(item)
|
|
216
238
|
}
|
|
217
239
|
|
|
218
240
|
return {
|
|
219
|
-
page
|
|
220
|
-
limit
|
|
241
|
+
page,
|
|
242
|
+
limit,
|
|
221
243
|
total: rCount.count,
|
|
222
|
-
items
|
|
244
|
+
items
|
|
223
245
|
}
|
|
224
246
|
}
|
|
225
247
|
|
|
@@ -229,24 +251,37 @@ class AbstractSqliteRepository<T, C, U> implements IDraxCrud<T, C, U> {
|
|
|
229
251
|
order = 'desc',
|
|
230
252
|
search = '',
|
|
231
253
|
filters = []
|
|
232
|
-
}: IDraxFindOptions): Promise<
|
|
233
|
-
|
|
254
|
+
}: IDraxFindOptions): Promise<T[]> {
|
|
234
255
|
|
|
235
256
|
let where = ""
|
|
257
|
+
let params: any[] = []
|
|
258
|
+
|
|
259
|
+
// SEARCH
|
|
236
260
|
if (search && this.searchFields.length > 0) {
|
|
237
|
-
|
|
261
|
+
|
|
262
|
+
const searchConditions = this.searchFields
|
|
263
|
+
.map(field => `${field} LIKE ?`)
|
|
264
|
+
.join(" OR ")
|
|
265
|
+
|
|
266
|
+
where = ` WHERE (${searchConditions})`
|
|
267
|
+
|
|
268
|
+
params.push(...this.searchFields.map(() => `%${search}%`))
|
|
238
269
|
}
|
|
239
270
|
|
|
271
|
+
// FILTERS
|
|
240
272
|
if (filters.length > 0) {
|
|
241
|
-
|
|
273
|
+
|
|
274
|
+
const result = SqlQueryFilter.applyFilters(where, filters)
|
|
275
|
+
|
|
276
|
+
where = result.where
|
|
277
|
+
params.push(...result.params)
|
|
242
278
|
}
|
|
243
279
|
|
|
244
280
|
const sort = SqlSort.applySort(orderBy, order)
|
|
245
281
|
|
|
246
|
-
const
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
FROM ${this.tableName} ${where} ${sort} LIMIT ? `).all([limit]) as T[];
|
|
282
|
+
const items = this.db
|
|
283
|
+
.prepare(`SELECT * FROM ${this.tableName} ${where} ${sort} LIMIT ?`)
|
|
284
|
+
.all([...params, limit]) as T[]
|
|
250
285
|
|
|
251
286
|
for (const item of items) {
|
|
252
287
|
await this.decorate(item)
|
|
@@ -264,13 +299,25 @@ class AbstractSqliteRepository<T, C, U> implements IDraxCrud<T, C, U> {
|
|
|
264
299
|
return items
|
|
265
300
|
}
|
|
266
301
|
|
|
267
|
-
async search(value: any, limit: number = 1000): Promise<
|
|
302
|
+
async search(value: any, limit: number = 1000): Promise<T[]> {
|
|
303
|
+
|
|
268
304
|
let where = ""
|
|
305
|
+
let params: any[] = []
|
|
306
|
+
|
|
269
307
|
if (value && this.searchFields.length > 0) {
|
|
270
|
-
|
|
308
|
+
|
|
309
|
+
const searchConditions = this.searchFields
|
|
310
|
+
.map(field => `${field} LIKE ?`)
|
|
311
|
+
.join(" OR ")
|
|
312
|
+
|
|
313
|
+
where = ` WHERE (${searchConditions})`
|
|
314
|
+
|
|
315
|
+
params.push(...this.searchFields.map(() => `%${value}%`))
|
|
271
316
|
}
|
|
272
|
-
|
|
273
|
-
|
|
317
|
+
|
|
318
|
+
const items = this.db
|
|
319
|
+
.prepare(`SELECT * FROM ${this.tableName} ${where} LIMIT ?`)
|
|
320
|
+
.all([...params, limit]) as T[]
|
|
274
321
|
|
|
275
322
|
for (const item of items) {
|
|
276
323
|
await this.decorate(item)
|
|
@@ -311,17 +358,32 @@ class AbstractSqliteRepository<T, C, U> implements IDraxCrud<T, C, U> {
|
|
|
311
358
|
}: IDraxFindOptions): Promise<T> {
|
|
312
359
|
|
|
313
360
|
let where = ""
|
|
361
|
+
let params: any[] = []
|
|
314
362
|
|
|
363
|
+
// SEARCH
|
|
315
364
|
if (search && this.searchFields.length > 0) {
|
|
316
|
-
|
|
365
|
+
|
|
366
|
+
const searchConditions = this.searchFields
|
|
367
|
+
.map(field => `${field} LIKE ?`)
|
|
368
|
+
.join(" OR ")
|
|
369
|
+
|
|
370
|
+
where = ` WHERE (${searchConditions})`
|
|
371
|
+
|
|
372
|
+
params.push(...this.searchFields.map(() => `%${search}%`))
|
|
317
373
|
}
|
|
318
374
|
|
|
375
|
+
// FILTERS
|
|
319
376
|
if (filters.length > 0) {
|
|
320
|
-
|
|
377
|
+
|
|
378
|
+
const result = SqlQueryFilter.applyFilters(where, filters)
|
|
379
|
+
|
|
380
|
+
where = result.where
|
|
381
|
+
params.push(...result.params)
|
|
321
382
|
}
|
|
322
383
|
|
|
323
|
-
const item = this.db
|
|
324
|
-
|
|
384
|
+
const item = this.db
|
|
385
|
+
.prepare(`SELECT * FROM ${this.tableName} ${where} LIMIT 1`)
|
|
386
|
+
.get(params)
|
|
325
387
|
|
|
326
388
|
if (item) {
|
|
327
389
|
await this.decorate(item)
|
|
@@ -331,17 +393,20 @@ class AbstractSqliteRepository<T, C, U> implements IDraxCrud<T, C, U> {
|
|
|
331
393
|
}
|
|
332
394
|
|
|
333
395
|
async groupBy({fields = [], filters = [], dateFormat = 'day'}: IDraxGroupByOptions): Promise<Array<any>> {
|
|
396
|
+
|
|
334
397
|
if (fields.length === 0) {
|
|
335
398
|
throw new Error("At least one field is required for groupBy")
|
|
336
399
|
}
|
|
337
400
|
|
|
338
|
-
// Construir la cláusula WHERE con los filtros
|
|
339
401
|
let where = ""
|
|
402
|
+
let params: any[] = []
|
|
403
|
+
|
|
340
404
|
if (filters.length > 0) {
|
|
341
|
-
|
|
405
|
+
const result = SqlQueryFilter.applyFilters(where, filters)
|
|
406
|
+
where = result.where
|
|
407
|
+
params.push(...result.params)
|
|
342
408
|
}
|
|
343
409
|
|
|
344
|
-
// Función para obtener el formato de fecha según SQLite
|
|
345
410
|
const getDateFormatSQL = (field: string, format: string): string => {
|
|
346
411
|
const formats: { [key: string]: string } = {
|
|
347
412
|
'year': `strftime('%Y', ${field})`,
|
|
@@ -354,46 +419,68 @@ class AbstractSqliteRepository<T, C, U> implements IDraxCrud<T, C, U> {
|
|
|
354
419
|
return formats[format] || formats['day']
|
|
355
420
|
}
|
|
356
421
|
|
|
357
|
-
// Determinar si cada campo es de fecha
|
|
358
422
|
const isDateField = (field: string): boolean => {
|
|
359
423
|
const tableField = this.tableFields.find(tf => tf.name === field)
|
|
360
|
-
return tableField
|
|
424
|
+
return tableField
|
|
425
|
+
? tableField.type === 'TEXT' && (field.includes('Date') || field.includes('date'))
|
|
426
|
+
: false
|
|
361
427
|
}
|
|
362
428
|
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
if (
|
|
366
|
-
|
|
429
|
+
const isNumericField = (field: string): boolean => {
|
|
430
|
+
const tableField = this.tableFields.find(tf => tf.name === field)
|
|
431
|
+
if (!tableField) return false
|
|
432
|
+
const type = tableField.type.toUpperCase()
|
|
433
|
+
return ['INTEGER', 'REAL', 'NUMERIC'].includes(type)
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
const selectParts: string[] = []
|
|
437
|
+
const groupParts: string[] = []
|
|
438
|
+
|
|
439
|
+
for (const field of fields) {
|
|
440
|
+
|
|
441
|
+
const tableField = this.tableFields.find(tf => tf.name === field)
|
|
442
|
+
|
|
443
|
+
if (!tableField) {
|
|
444
|
+
throw new Error(`Invalid field ${field}`)
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
if (isNumericField(field)) {
|
|
448
|
+
selectParts.push(`SUM(${field}) as ${field}`)
|
|
449
|
+
continue
|
|
367
450
|
}
|
|
368
|
-
return field
|
|
369
|
-
}).join(', ')
|
|
370
451
|
|
|
371
|
-
// Construir la cláusula GROUP BY
|
|
372
|
-
const groupByFields = fields.map(field => {
|
|
373
452
|
if (isDateField(field)) {
|
|
374
|
-
|
|
453
|
+
const formatted = getDateFormatSQL(field, dateFormat)
|
|
454
|
+
selectParts.push(`${formatted} as ${field}`)
|
|
455
|
+
groupParts.push(formatted)
|
|
456
|
+
continue
|
|
375
457
|
}
|
|
376
|
-
return field
|
|
377
|
-
}).join(', ')
|
|
378
458
|
|
|
379
|
-
|
|
459
|
+
selectParts.push(field)
|
|
460
|
+
groupParts.push(field)
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
const selectFields = selectParts.join(", ")
|
|
464
|
+
const groupByFields = groupParts.join(", ")
|
|
465
|
+
|
|
380
466
|
const query = `
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
467
|
+
SELECT ${selectFields}, COUNT(*) as count
|
|
468
|
+
FROM ${this.tableName}
|
|
469
|
+
${where}
|
|
470
|
+
${groupByFields ? `GROUP BY ${groupByFields}` : ''}
|
|
471
|
+
ORDER BY count DESC
|
|
472
|
+
`
|
|
387
473
|
|
|
388
474
|
try {
|
|
389
|
-
const result = this.db.prepare(query).all() as Array<any>
|
|
390
475
|
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
476
|
+
const result = this.db.prepare(query).all(params) as Array<any>
|
|
477
|
+
|
|
478
|
+
// for (const item of result) {
|
|
479
|
+
// await this.decorate(item)
|
|
480
|
+
// }
|
|
395
481
|
|
|
396
482
|
return result
|
|
483
|
+
|
|
397
484
|
} catch (e) {
|
|
398
485
|
console.error("GroupBy query error:", e)
|
|
399
486
|
throw e
|