dobo 2.18.2 → 2.19.1
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/index.js +30 -6
- package/lib/collect-models.js +2 -2
- package/lib/factory/model/_util.js +22 -46
- package/lib/factory/model/build.js +2 -1
- package/lib/factory/model/clear-record.js +2 -1
- package/lib/factory/model/count-record.js +2 -1
- package/lib/factory/model/create-aggregate.js +2 -1
- package/lib/factory/model/create-histogram.js +2 -1
- package/lib/factory/model/create-record.js +3 -7
- package/lib/factory/model/drop.js +2 -1
- package/lib/factory/model/exists.js +2 -1
- package/lib/factory/model/find-all-record.js +32 -42
- package/lib/factory/model/find-one-record.js +3 -2
- package/lib/factory/model/find-record.js +31 -42
- package/lib/factory/model/get-record.js +20 -29
- package/lib/factory/model/remove-record.js +3 -7
- package/lib/factory/model/update-record.js +3 -7
- package/lib/factory/model/upsert-record.js +3 -7
- package/package.json +1 -1
- package/wiki/CHANGES.md +13 -0
package/index.js
CHANGED
|
@@ -539,23 +539,47 @@ async function factory (pkgName) {
|
|
|
539
539
|
return nql(sanitized).parse()
|
|
540
540
|
}
|
|
541
541
|
|
|
542
|
-
|
|
543
|
-
const {
|
|
542
|
+
parseAny = (query, model) => {
|
|
543
|
+
const { isEmpty } = this.app.lib._
|
|
544
|
+
let q = {}
|
|
545
|
+
if (!model) throw this.error('invalidQuery')
|
|
546
|
+
let scanables = [...model.scanables]
|
|
547
|
+
if (scanables.length === 0) scanables = [...model.sortables]
|
|
548
|
+
const fields = scanables.filter(f => {
|
|
549
|
+
const field = find(model.properties, { name: f, type: 'string' })
|
|
550
|
+
return !!field
|
|
551
|
+
})
|
|
552
|
+
const parts = fields.map(f => {
|
|
553
|
+
if (query[0] === '*') return `${f}:~$'${query.replaceAll('*', '')}'`
|
|
554
|
+
if (query[query.length - 1] === '*') return `${f}:~^'${query.replaceAll('*', '')}'`
|
|
555
|
+
return `${f}:~'${query.replaceAll('*', '')}'`
|
|
556
|
+
})
|
|
557
|
+
if (parts.length === 1) q = this.parseNql(parts[0])
|
|
558
|
+
else if (parts.length > 1) q = this.parseNql(parts.join(','))
|
|
559
|
+
if (isEmpty(q)) throw this.error('invalidQuery')
|
|
560
|
+
return q
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
parseQuery = (query, model, silent = true) => {
|
|
564
|
+
const { isEmpty, isPlainObject, trim } = this.app.lib._
|
|
544
565
|
let result = {}
|
|
545
566
|
if (isPlainObject(query)) result = query
|
|
546
567
|
else {
|
|
547
568
|
query = trim(query)
|
|
569
|
+
if (isEmpty(query)) return result
|
|
548
570
|
try {
|
|
549
571
|
if (query.startsWith('{')) result = JSON.parse(query)
|
|
550
|
-
else result = this.parseNql(query)
|
|
572
|
+
else if (query.includes(':')) result = this.parseNql(query)
|
|
573
|
+
else result = this.parseAny(query, model)
|
|
551
574
|
} catch (err) {
|
|
552
575
|
if (silent) return {}
|
|
553
576
|
throw err
|
|
554
577
|
}
|
|
555
578
|
}
|
|
579
|
+
let strQ = this.replaceRegexInJson(result)
|
|
580
|
+
if (model && model.driver.idField.name !== 'id') strQ = strQ.replaceAll('"id"', `"${model.driver.idField.name}"`)
|
|
556
581
|
try {
|
|
557
|
-
|
|
558
|
-
if (text.includes('["__REGEXP__",')) result = this.reviveRegexInJson(text)
|
|
582
|
+
result = this.reviveRegexInJson(strQ)
|
|
559
583
|
} catch (err) {}
|
|
560
584
|
return result
|
|
561
585
|
}
|
|
@@ -575,7 +599,7 @@ async function factory (pkgName) {
|
|
|
575
599
|
const { isPlainObject } = this.app.lib._
|
|
576
600
|
if (isPlainObject(input)) input = JSON.stringify(input)
|
|
577
601
|
const result = JSON.parse(input, (key, value) => {
|
|
578
|
-
if (Array.isArray(value) && value[0] === '__REGEXP__') return
|
|
602
|
+
if (Array.isArray(value) && value[0] === '__REGEXP__') return new RegExp(value[1], value[2])
|
|
579
603
|
return value
|
|
580
604
|
})
|
|
581
605
|
return returnObject ? result : JSON.stringify(result)
|
package/lib/collect-models.js
CHANGED
|
@@ -238,7 +238,7 @@ async function createSchema (item) {
|
|
|
238
238
|
const { readConfig } = this.app.bajo
|
|
239
239
|
const { fastGlob } = this.app.lib
|
|
240
240
|
const { find, isPlainObject, orderBy, get, cloneDeep } = this.app.lib._
|
|
241
|
-
const { mergeObjectsByKey, defaultsDeep } = this.app.lib.aneka
|
|
241
|
+
const { mergeObjectsByKey, defaultsDeep, parseObject } = this.app.lib.aneka
|
|
242
242
|
if (item.file && !item.base) item.base = path.basename(item.file, path.extname(item.file))
|
|
243
243
|
item.attachment = item.attachment ?? true
|
|
244
244
|
const feats = item.features ?? []
|
|
@@ -269,7 +269,7 @@ async function createSchema (item) {
|
|
|
269
269
|
item.cache = item.cache ?? item.connection.options.cache ?? defCache
|
|
270
270
|
if (item.cache === true) item.cache = get(item, 'connection.options.cache', defCache)
|
|
271
271
|
else if (item.cache === false) item.cache = { ttlDur: 0 }
|
|
272
|
-
item.cache = defaultsDeep(get(this, `app.bajoCache.config.dobo.${item.name}`), item.cache)
|
|
272
|
+
item.cache = parseObject(defaultsDeep(get(this, `app.bajoCache.config.dobo.${item.name}`), item.cache))
|
|
273
273
|
await findAllProps.call(this, item, props, indexes)
|
|
274
274
|
await findAllFeats.call(this, item, feats, indexes)
|
|
275
275
|
await findAllIndexes.call(this, item, indexes)
|
|
@@ -65,6 +65,16 @@ export async function getFilterAndOptions (filter = {}, options = {}, action) {
|
|
|
65
65
|
const { runModelHook } = this.app.dobo
|
|
66
66
|
const nFilter = cloneDeep(filter || {})
|
|
67
67
|
const nOptions = cloneOptions.call(this, options)
|
|
68
|
+
if (options.noMagic) {
|
|
69
|
+
nOptions.noModelHook = true
|
|
70
|
+
nOptions.noHook = true
|
|
71
|
+
nOptions.noDynHook = true
|
|
72
|
+
nOptions.noValidation = true
|
|
73
|
+
nOptions.noCache = true
|
|
74
|
+
nOptions.throwNotFound = false
|
|
75
|
+
delete options.noMagic
|
|
76
|
+
delete nOptions.noMagic
|
|
77
|
+
}
|
|
68
78
|
nOptions.action = action
|
|
69
79
|
nOptions.dataOnly = false
|
|
70
80
|
nOptions.truncateString = nOptions.truncateString ?? false
|
|
@@ -77,7 +87,6 @@ export async function getFilterAndOptions (filter = {}, options = {}, action) {
|
|
|
77
87
|
if (!nOptions.noModelHook) await runModelHook(this, 'beforeBuilSearch', nFilter.search, nOptions)
|
|
78
88
|
nFilter.search = buildFilterSearch.call(this, nFilter) ?? {}
|
|
79
89
|
if (!nOptions.noModelHook) await runModelHook(this, 'afterBuildSearch', nFilter.search, nOptions)
|
|
80
|
-
if (this.driver.idField.name !== 'id') replaceIdInQuerySearch.call(this, nFilter)
|
|
81
90
|
const { limit, page, skip, sort } = preparePagination.call(this, nFilter, nOptions)
|
|
82
91
|
nFilter.limit = limit
|
|
83
92
|
nFilter.page = page
|
|
@@ -190,7 +199,7 @@ export async function getSingleRef (record = {}, options = {}) {
|
|
|
190
199
|
let query = {}
|
|
191
200
|
query[ref.field] = record[prop.name]
|
|
192
201
|
if (ref.field === 'id') query[ref.field] = this.sanitizeId(query[ref.field])
|
|
193
|
-
if (ref.query) query = { $and: [query, parseQuery(ref.query)] }
|
|
202
|
+
if (ref.query) query = { $and: [query, parseQuery(ref.query, rModel)] }
|
|
194
203
|
const filter = { query }
|
|
195
204
|
const resp = await _getRef.call(this, { ref, rModel, prop, key, options, filter })
|
|
196
205
|
if (!resp) continue
|
|
@@ -228,7 +237,7 @@ export async function getMultiRefs (records = [], options = {}) {
|
|
|
228
237
|
matches = uniq(without(matches, undefined, null, NaN))
|
|
229
238
|
let query = {}
|
|
230
239
|
query[ref.field] = { $in: matches }
|
|
231
|
-
if (ref.query) query = { $and: [query, parseQuery(ref.query)] }
|
|
240
|
+
if (ref.query) query = { $and: [query, parseQuery(ref.query, rModel)] }
|
|
232
241
|
const filter = { query, limit: matches.length }
|
|
233
242
|
const resp = await _getRef.call(this, { ref, rModel, prop, key, options, filter })
|
|
234
243
|
if (!resp) continue
|
|
@@ -247,35 +256,9 @@ export async function getMultiRefs (records = [], options = {}) {
|
|
|
247
256
|
}
|
|
248
257
|
|
|
249
258
|
export function buildFilterQuery (filter = {}) {
|
|
250
|
-
const {
|
|
251
|
-
const
|
|
252
|
-
|
|
253
|
-
let q = {}
|
|
254
|
-
if (isString(query)) {
|
|
255
|
-
try {
|
|
256
|
-
query = trim(query)
|
|
257
|
-
if (query.startsWith('{')) q = JSON.parse(query) // JSON formatted query
|
|
258
|
-
else if (query.includes(':')) q = parseNql(query) // NQL
|
|
259
|
-
else {
|
|
260
|
-
let scanables = [...this.scanables]
|
|
261
|
-
if (scanables.length === 0) scanables = [...this.sortables]
|
|
262
|
-
const fields = scanables.filter(f => {
|
|
263
|
-
const field = find(this.properties, { name: f, type: 'string' })
|
|
264
|
-
return !!field
|
|
265
|
-
})
|
|
266
|
-
const parts = fields.map(f => {
|
|
267
|
-
if (query[0] === '*') return `${f}:~$'${query.replaceAll('*', '')}'`
|
|
268
|
-
if (query[query.length - 1] === '*') return `${f}:~^'${query.replaceAll('*', '')}'`
|
|
269
|
-
return `${f}:~'${query.replaceAll('*', '')}'`
|
|
270
|
-
})
|
|
271
|
-
if (parts.length === 1) q = parseNql(parts[0])
|
|
272
|
-
else if (parts.length > 1) q = parseNql(parts.join(','))
|
|
273
|
-
}
|
|
274
|
-
} catch (err) {
|
|
275
|
-
this.plugin.error('invalidQuery', { orgMessage: err.message })
|
|
276
|
-
}
|
|
277
|
-
} else if (isPlainObject(query)) q = query
|
|
278
|
-
return sanitizeQuery.call(this, q)
|
|
259
|
+
const { parseQuery } = this.app.dobo
|
|
260
|
+
const query = parseQuery(filter.query ?? {}, this, false)
|
|
261
|
+
return sanitizeQuery.call(this, query)
|
|
279
262
|
}
|
|
280
263
|
|
|
281
264
|
function sanitizeQuery (query = {}, parent) {
|
|
@@ -346,7 +329,7 @@ export function buildFilterSearch (filter = {}) {
|
|
|
346
329
|
items[part.field].push(...part.value.split(' ').filter(v => ![''].includes(v)))
|
|
347
330
|
}
|
|
348
331
|
}
|
|
349
|
-
|
|
332
|
+
let s = {}
|
|
350
333
|
for (const index of this.indexes.filter(i => i.type === 'fulltext')) {
|
|
351
334
|
for (const f of index.fields) {
|
|
352
335
|
const value = []
|
|
@@ -357,22 +340,15 @@ export function buildFilterSearch (filter = {}) {
|
|
|
357
340
|
}
|
|
358
341
|
}
|
|
359
342
|
if (has(items, '*')) s['*'] = items['*']
|
|
343
|
+
if (this.driver.idField.name !== 'id') {
|
|
344
|
+
const search = JSON.stringify(s).replaceAll('"id"', `"${this.driver.idField.name}"`)
|
|
345
|
+
try {
|
|
346
|
+
s = JSON.parse(search)
|
|
347
|
+
} catch (err) {}
|
|
348
|
+
}
|
|
360
349
|
return s
|
|
361
350
|
}
|
|
362
351
|
|
|
363
|
-
export function replaceIdInQuerySearch (filter) {
|
|
364
|
-
const { replaceRegexInJson, reviveRegexInJson } = this.app.dobo
|
|
365
|
-
const query = replaceRegexInJson(filter.query).replaceAll('"id"', `"${this.driver.idField.name}"`)
|
|
366
|
-
try {
|
|
367
|
-
filter.query = reviveRegexInJson(query)
|
|
368
|
-
} catch (err) {}
|
|
369
|
-
// search
|
|
370
|
-
const search = JSON.stringify(filter.search ?? {}).replaceAll('"id"', `"${this.driver.idField.name}"`)
|
|
371
|
-
try {
|
|
372
|
-
filter.search = JSON.parse(search)
|
|
373
|
-
} catch (err) {}
|
|
374
|
-
}
|
|
375
|
-
|
|
376
352
|
/**
|
|
377
353
|
* Prepare records pagination:
|
|
378
354
|
* - making sure records limit is obeyed
|
|
@@ -2,7 +2,8 @@ import { getFilterAndOptions, execHook, execModelHook } from './_util.js'
|
|
|
2
2
|
const action = 'build'
|
|
3
3
|
|
|
4
4
|
async function build (opts = {}) {
|
|
5
|
-
|
|
5
|
+
opts.dataOnly = opts.dataOnly ?? true
|
|
6
|
+
const { dataOnly } = opts
|
|
6
7
|
const { options } = await getFilterAndOptions.call(this, null, opts, action)
|
|
7
8
|
await execHook.call(this, 'beforeBuildModel', options)
|
|
8
9
|
await execModelHook.call(this, 'beforeBuildModel', options)
|
|
@@ -4,7 +4,8 @@ const action = 'clearRecord'
|
|
|
4
4
|
async function clearRecord (...args) {
|
|
5
5
|
if (args.length === 0) return this.action(action, ...args)
|
|
6
6
|
const [opts = {}] = args
|
|
7
|
-
|
|
7
|
+
opts.dataOnly = opts.dataOnly ?? true
|
|
8
|
+
const { dataOnly } = opts
|
|
8
9
|
const { options } = await getFilterAndOptions.call(this, null, opts, action)
|
|
9
10
|
await execHook.call(this, 'beforeClearRecord', options)
|
|
10
11
|
await execModelHook.call(this, 'beforeClearRecord', options)
|
|
@@ -5,7 +5,8 @@ async function countRecord (...args) {
|
|
|
5
5
|
const { getDefaultValues } = this.app.dobo
|
|
6
6
|
if (args.length === 0) return this.action(action, ...args)
|
|
7
7
|
const [params = {}, opts = {}] = args
|
|
8
|
-
|
|
8
|
+
opts.dataOnly = opts.dataOnly ?? true
|
|
9
|
+
const { dataOnly } = opts
|
|
9
10
|
const { filter, options } = await getFilterAndOptions.call(this, params, opts, action)
|
|
10
11
|
const { hardCap, t } = getDefaultValues(options)
|
|
11
12
|
await execHook.call(this, 'beforeCountRecord', options)
|
|
@@ -4,7 +4,8 @@ const action = 'createAggregate'
|
|
|
4
4
|
async function createAggregate (...args) {
|
|
5
5
|
if (args.length === 0) return this.action(action, ...args)
|
|
6
6
|
const [_filter = {}, params = {}, opts = {}] = args
|
|
7
|
-
|
|
7
|
+
opts.dataOnly = opts.dataOnly ?? true
|
|
8
|
+
const { dataOnly } = opts
|
|
8
9
|
const { filter, options } = await getFilterAndOptions.call(this, _filter, opts, action)
|
|
9
10
|
const { getDefaultValues } = this.app.dobo
|
|
10
11
|
await execHook.call(this, 'beforeCreateAggregate', filter, params, options)
|
|
@@ -4,7 +4,8 @@ const action = 'createHistogram'
|
|
|
4
4
|
async function createHistogram (...args) {
|
|
5
5
|
if (args.length === 0) return this.action(action, ...args)
|
|
6
6
|
const [_filter = {}, params = {}, opts = {}] = args
|
|
7
|
-
|
|
7
|
+
opts.dataOnly = opts.dataOnly ?? true
|
|
8
|
+
const { dataOnly } = opts
|
|
8
9
|
const { filter, options } = await getFilterAndOptions.call(this, _filter, opts, action)
|
|
9
10
|
const { getDefaultValues } = this.app.dobo
|
|
10
11
|
await execHook.call(this, 'beforeCreateHistogram', filter, params, options)
|
|
@@ -8,9 +8,9 @@ async function createRecord (...args) {
|
|
|
8
8
|
const [body = {}, opts = {}] = args
|
|
9
9
|
const { getDefaultValues } = this.app.dobo
|
|
10
10
|
const { isSet } = this.app.lib.aneka
|
|
11
|
-
const { runHook } = this.app.bajo
|
|
12
11
|
const { cloneDeep, get } = this.app.lib._
|
|
13
|
-
|
|
12
|
+
opts.dataOnly = opts.dataOnly ?? true
|
|
13
|
+
const { dataOnly } = opts
|
|
14
14
|
const { options } = await getFilterAndOptions.call(this, null, opts, action)
|
|
15
15
|
const { truncateString, noResult, noBodySanitizer, noResultSanitizer, noValidation } = options
|
|
16
16
|
const extFields = get(options, 'validation.extFields', [])
|
|
@@ -20,10 +20,7 @@ async function createRecord (...args) {
|
|
|
20
20
|
await execDynHook.call(this, 'beforeCreateRecord', input, options)
|
|
21
21
|
if (!noValidation) await execValidation.call(this, input, options)
|
|
22
22
|
let result = options.record ?? (await this.driver._createRecord(this, input, options)) ?? {}
|
|
23
|
-
if (noResult)
|
|
24
|
-
await runHook('cache:clear', this, 'create', body, null, options)
|
|
25
|
-
return
|
|
26
|
-
}
|
|
23
|
+
if (noResult) return
|
|
27
24
|
result = result ?? {}
|
|
28
25
|
const { warnings } = getDefaultValues(options)
|
|
29
26
|
if (!warnings) delete result.warnings
|
|
@@ -33,7 +30,6 @@ async function createRecord (...args) {
|
|
|
33
30
|
await execDynHook.call(this, 'afterCreateRecord', input, result, options)
|
|
34
31
|
await execModelHook.call(this, 'afterCreateRecord', input, result, options)
|
|
35
32
|
await execHook.call(this, 'afterCreateRecord', input, result, options)
|
|
36
|
-
await runHook('cache:clear', this, 'create', body, result, options)
|
|
37
33
|
return dataOnly ? result.data : result
|
|
38
34
|
}
|
|
39
35
|
|
|
@@ -2,7 +2,8 @@ import { getFilterAndOptions, execHook, execModelHook } from './_util.js'
|
|
|
2
2
|
const action = 'drop'
|
|
3
3
|
|
|
4
4
|
async function drop (opts = {}) {
|
|
5
|
-
|
|
5
|
+
opts.dataOnly = opts.dataOnly ?? true
|
|
6
|
+
const { dataOnly } = opts
|
|
6
7
|
const { options } = await getFilterAndOptions.call(this, null, opts, action)
|
|
7
8
|
await execHook.call(this, 'beforeDropModel', options)
|
|
8
9
|
await execModelHook.call(this, 'beforeDropModel', options)
|
|
@@ -8,7 +8,8 @@ const action = 'modelExists'
|
|
|
8
8
|
* @returns {Object}
|
|
9
9
|
*/
|
|
10
10
|
async function isExists (opts = {}) {
|
|
11
|
-
|
|
11
|
+
opts.dataOnly = opts.dataOnly ?? true
|
|
12
|
+
const { dataOnly } = opts
|
|
12
13
|
const { options } = await getFilterAndOptions.call(this, null, opts, action)
|
|
13
14
|
await execHook.call(this, 'beforeModelExists', options)
|
|
14
15
|
await execModelHook.call(this, 'beforeModelExists', options)
|
|
@@ -4,63 +4,53 @@ const action = 'findAllRecord'
|
|
|
4
4
|
async function native (...args) {
|
|
5
5
|
const { isSet } = this.app.lib.aneka
|
|
6
6
|
const { getDefaultValues, t } = this.app.dobo
|
|
7
|
-
const {
|
|
7
|
+
const { pick, omit } = this.app.lib._
|
|
8
8
|
const [params = {}, opts = {}] = args
|
|
9
|
-
|
|
10
|
-
const {
|
|
9
|
+
opts.dataOnly = opts.dataOnly ?? true
|
|
10
|
+
const { dataOnly } = opts
|
|
11
11
|
const { filter, options } = await getFilterAndOptions.call(this, params, opts, action)
|
|
12
12
|
const { hardCap, warnings } = getDefaultValues(options)
|
|
13
13
|
if (dataOnly) options.count = false
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
14
|
+
const { noResultSanitizer } = options
|
|
15
|
+
try {
|
|
16
|
+
await execHook.call(this, 'beforeFindRecord', filter, options)
|
|
17
|
+
await execModelHook.call(this, 'beforeFindRecord', filter, options)
|
|
18
|
+
await execDynHook.call(this, 'beforeFindRecord', filter, options)
|
|
19
|
+
let result = options.record ?? (await this.driver._findAllRecord(this, filter, options)) ?? {}
|
|
20
|
+
result.limit = filter.limit
|
|
21
|
+
result.filter = pick(filter, ['query', 'match', 'sort'])
|
|
22
|
+
result.warnings = result.warnings ?? []
|
|
23
|
+
if (!options.count) result = omit(result, ['count', 'pages'])
|
|
24
|
+
else if (options.count && result.count > hardCap) {
|
|
25
|
+
result.warnings.push(t('hardCapWarning%s%s', result.count, hardCap))
|
|
26
|
+
result.count = hardCap
|
|
27
|
+
result.hardCapped = true
|
|
28
|
+
}
|
|
29
|
+
result.pages = options.count ? Math.ceil(result.count / filter.limit) : undefined
|
|
30
|
+
if (!warnings) delete result.warnings
|
|
31
|
+
|
|
32
|
+
if (!noResultSanitizer) {
|
|
23
33
|
for (const idx in result.data) {
|
|
24
34
|
result.data[idx] = await this.sanitizeRecord(result.data[idx], options)
|
|
25
35
|
}
|
|
26
|
-
result.cached = true
|
|
27
|
-
return dataOnly ? result.data : result
|
|
28
36
|
}
|
|
37
|
+
if (isSet(options.refs)) await getMultiRefs.call(this, result.data, options)
|
|
38
|
+
await execDynHook.call(this, 'afterFindRecord', filter, result, options)
|
|
39
|
+
await execModelHook.call(this, 'afterFindRecord', filter, result, options)
|
|
40
|
+
await execHook.call(this, 'afterFindRecord', filter, result, options)
|
|
41
|
+
return dataOnly ? result.data : result
|
|
42
|
+
} catch (err) {
|
|
43
|
+
if (err.code === 'cachedResult') return err.data
|
|
44
|
+
throw err
|
|
29
45
|
}
|
|
30
|
-
let result = options.record ?? (await this.driver._findAllRecord(this, filter, options)) ?? {}
|
|
31
|
-
result.limit = filter.limit
|
|
32
|
-
result.filter = pick(filter, ['query', 'match', 'sort'])
|
|
33
|
-
result.warnings = result.warnings ?? []
|
|
34
|
-
if (!options.count) result = omit(result, ['count', 'pages'])
|
|
35
|
-
else if (options.count && result.count > hardCap) {
|
|
36
|
-
result.warnings.push(t('hardCapWarning%s%s', result.count, hardCap))
|
|
37
|
-
result.count = hardCap
|
|
38
|
-
result.hardCapped = true
|
|
39
|
-
}
|
|
40
|
-
result.pages = options.count ? Math.ceil(result.count / filter.limit) : undefined
|
|
41
|
-
if (!warnings) delete result.warnings
|
|
42
|
-
|
|
43
|
-
if (!noResultSanitizer) {
|
|
44
|
-
for (const idx in result.data) {
|
|
45
|
-
result.data[idx] = await this.sanitizeRecord(result.data[idx], options)
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
if (isSet(options.refs)) await getMultiRefs.call(this, result.data, options)
|
|
49
|
-
await execDynHook.call(this, 'afterFindRecord', filter, result, options)
|
|
50
|
-
await execModelHook.call(this, 'afterFindRecord', filter, result, options)
|
|
51
|
-
await execHook.call(this, 'afterFindRecord', filter, result, options)
|
|
52
|
-
if (!noCache && setCache) {
|
|
53
|
-
await setCache({ model: this, filter: cacheFilter, result, options })
|
|
54
|
-
result.cached = false
|
|
55
|
-
}
|
|
56
|
-
return dataOnly ? result.data : result
|
|
57
46
|
}
|
|
58
47
|
|
|
59
48
|
async function loop (...args) {
|
|
60
49
|
const { cloneDeep } = this.app.lib._
|
|
61
50
|
const { getDefaultValues } = this.app.dobo
|
|
62
51
|
const [params = {}, opts = {}] = args
|
|
63
|
-
|
|
52
|
+
opts.dataOnly = opts.dataOnly ?? true
|
|
53
|
+
const { dataOnly } = opts
|
|
64
54
|
const { filter, options } = await getFilterAndOptions.call(this, params, opts, action)
|
|
65
55
|
const { maxLimit, hardCap, warnings: showWarnings } = getDefaultValues(options)
|
|
66
56
|
const nFilter = cloneDeep(filter || {})
|
|
@@ -7,7 +7,8 @@ async function findOneRecord (...args) {
|
|
|
7
7
|
const { getDefaultValues } = this.app.dobo
|
|
8
8
|
const [params = {}, opts = {}] = args
|
|
9
9
|
const { cloneDeep } = this.app.lib._
|
|
10
|
-
|
|
10
|
+
opts.dataOnly = opts.dataOnly ?? true
|
|
11
|
+
const { dataOnly } = opts
|
|
11
12
|
if (dataOnly) opts.count = false
|
|
12
13
|
const nFilter = cloneDeep(params || {})
|
|
13
14
|
const nOptions = cloneOptions.call(this, opts)
|
|
@@ -17,7 +18,7 @@ async function findOneRecord (...args) {
|
|
|
17
18
|
const { warnings } = getDefaultValues(nOptions)
|
|
18
19
|
const resp = await this.findRecord(nFilter, nOptions)
|
|
19
20
|
const data = resp.data[0]
|
|
20
|
-
const result = { data,
|
|
21
|
+
const result = { data, warnings: resp.warnings }
|
|
21
22
|
if (!warnings) delete result.warnings
|
|
22
23
|
return dataOnly ? data : result
|
|
23
24
|
}
|
|
@@ -69,56 +69,45 @@ async function findRecord (...args) {
|
|
|
69
69
|
const { getDefaultValues, t } = this.app.dobo
|
|
70
70
|
const [params = {}, opts = {}] = args
|
|
71
71
|
const { isSet } = this.app.lib.aneka
|
|
72
|
-
const {
|
|
73
|
-
|
|
74
|
-
const {
|
|
72
|
+
const { pick, omit } = this.app.lib._
|
|
73
|
+
opts.dataOnly = opts.dataOnly ?? true
|
|
74
|
+
const { dataOnly } = opts
|
|
75
75
|
const { filter, options } = await getFilterAndOptions.call(this, params, opts, action)
|
|
76
76
|
const { hardCap, warnings } = getDefaultValues(options)
|
|
77
77
|
if (dataOnly) options.count = false
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
78
|
+
const { noResultSanitizer } = options
|
|
79
|
+
try {
|
|
80
|
+
await execHook.call(this, 'beforeFindRecord', filter, options)
|
|
81
|
+
await execModelHook.call(this, 'beforeFindRecord', filter, options)
|
|
82
|
+
await execDynHook.call(this, 'beforeFindRecord', filter, options)
|
|
83
|
+
let result = options.record ?? (await this.driver._findRecord(this, filter, options)) ?? {}
|
|
84
|
+
result.page = filter.page
|
|
85
|
+
result.limit = filter.limit
|
|
86
|
+
result.filter = pick(filter, ['query', 'match', 'sort'])
|
|
87
|
+
result.warnings = result.warnings ?? []
|
|
88
|
+
if (!options.count) result = omit(result, ['count', 'pages'])
|
|
89
|
+
else if (options.count && result.count > hardCap) {
|
|
90
|
+
result.warnings.push(t('hardCapWarning%s%s', result.count, hardCap))
|
|
91
|
+
result.count = hardCap
|
|
92
|
+
result.hardCapped = true
|
|
93
|
+
}
|
|
94
|
+
result.pages = options.count ? Math.ceil(result.count / filter.limit) : undefined
|
|
95
|
+
if (!warnings) delete result.warnings
|
|
96
|
+
|
|
97
|
+
if (!noResultSanitizer) {
|
|
87
98
|
for (const idx in result.data) {
|
|
88
99
|
result.data[idx] = await this.sanitizeRecord(result.data[idx], options)
|
|
89
100
|
}
|
|
90
|
-
result.cached = true
|
|
91
|
-
return dataOnly ? result.data : result
|
|
92
101
|
}
|
|
102
|
+
if (isSet(options.refs)) await getMultiRefs.call(this, result.data, options)
|
|
103
|
+
await execDynHook.call(this, 'afterFindRecord', filter, result, options)
|
|
104
|
+
await execModelHook.call(this, 'afterFindRecord', filter, result, options)
|
|
105
|
+
await execHook.call(this, 'afterFindRecord', filter, result, options)
|
|
106
|
+
return dataOnly ? result.data : result
|
|
107
|
+
} catch (err) {
|
|
108
|
+
if (err.code === 'cachedResult') return err.data
|
|
109
|
+
throw err
|
|
93
110
|
}
|
|
94
|
-
let result = options.record ?? (await this.driver._findRecord(this, filter, options)) ?? {}
|
|
95
|
-
result.page = filter.page
|
|
96
|
-
result.limit = filter.limit
|
|
97
|
-
result.filter = pick(filter, ['query', 'match', 'sort'])
|
|
98
|
-
result.warnings = result.warnings ?? []
|
|
99
|
-
if (!options.count) result = omit(result, ['count', 'pages'])
|
|
100
|
-
else if (options.count && result.count > hardCap) {
|
|
101
|
-
result.warnings.push(t('hardCapWarning%s%s', result.count, hardCap))
|
|
102
|
-
result.count = hardCap
|
|
103
|
-
result.hardCapped = true
|
|
104
|
-
}
|
|
105
|
-
result.pages = options.count ? Math.ceil(result.count / filter.limit) : undefined
|
|
106
|
-
if (!warnings) delete result.warnings
|
|
107
|
-
|
|
108
|
-
if (!noResultSanitizer) {
|
|
109
|
-
for (const idx in result.data) {
|
|
110
|
-
result.data[idx] = await this.sanitizeRecord(result.data[idx], options)
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
if (isSet(options.refs)) await getMultiRefs.call(this, result.data, options)
|
|
114
|
-
await execDynHook.call(this, 'afterFindRecord', filter, result, options)
|
|
115
|
-
await execModelHook.call(this, 'afterFindRecord', filter, result, options)
|
|
116
|
-
await execHook.call(this, 'afterFindRecord', filter, result, options)
|
|
117
|
-
if (!noCache && setCache) {
|
|
118
|
-
await setCache({ model: this, filter: cacheFilter, result, options })
|
|
119
|
-
result.cached = false
|
|
120
|
-
}
|
|
121
|
-
return dataOnly ? result.data : result
|
|
122
111
|
}
|
|
123
112
|
|
|
124
113
|
export default findRecord
|
|
@@ -48,38 +48,29 @@ async function getRecord (...args) {
|
|
|
48
48
|
const { getDefaultValues } = this.app.dobo
|
|
49
49
|
const { isEmpty } = this.app.lib._
|
|
50
50
|
const { isSet } = this.app.lib.aneka
|
|
51
|
-
|
|
52
|
-
const { dataOnly
|
|
51
|
+
opts.dataOnly = opts.dataOnly ?? true
|
|
52
|
+
const { dataOnly } = opts
|
|
53
53
|
const { options } = await getFilterAndOptions.call(this, null, opts, action)
|
|
54
|
-
|
|
55
|
-
if (!this.cacheable || !options.record) noCache = true
|
|
54
|
+
const { noResultSanitizer } = options
|
|
56
55
|
id = this.sanitizeId(id)
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
const
|
|
63
|
-
if (
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
56
|
+
try {
|
|
57
|
+
await execHook.call(this, 'beforeGetRecord', id, options)
|
|
58
|
+
await execModelHook.call(this, 'beforeGetRecord', id, options)
|
|
59
|
+
await execDynHook.call(this, 'beforeGetRecord', id, options)
|
|
60
|
+
const result = options.record ?? (await this.driver._getRecord(this, id, options)) ?? {}
|
|
61
|
+
const { warnings } = getDefaultValues(options)
|
|
62
|
+
if (!warnings) delete result.warnings
|
|
63
|
+
if (isEmpty(result.data) && !options.throwNotFound) return dataOnly ? undefined : { data: undefined }
|
|
64
|
+
if (!noResultSanitizer) result.data = await this.sanitizeRecord(result.data, options)
|
|
65
|
+
if (isSet(options.refs)) await getSingleRef.call(this, result.data, options)
|
|
66
|
+
await execDynHook.call(this, 'afterGetRecord', id, result, options)
|
|
67
|
+
await execModelHook.call(this, 'afterGetRecord', id, result, options)
|
|
68
|
+
await execHook.call(this, 'afterGetRecord', id, result, options)
|
|
69
|
+
return dataOnly ? result.data : result
|
|
70
|
+
} catch (err) {
|
|
71
|
+
if (err.code === 'cachedResult') return err.data
|
|
72
|
+
throw err
|
|
68
73
|
}
|
|
69
|
-
const result = options.record ?? (await this.driver._getRecord(this, id, options)) ?? {}
|
|
70
|
-
const { warnings } = getDefaultValues(options)
|
|
71
|
-
if (!warnings) delete result.warnings
|
|
72
|
-
if (isEmpty(result.data) && !options.throwNotFound) return dataOnly ? undefined : { data: undefined }
|
|
73
|
-
if (!noResultSanitizer) result.data = await this.sanitizeRecord(result.data, options)
|
|
74
|
-
if (isSet(options.refs)) await getSingleRef.call(this, result.data, options)
|
|
75
|
-
await execDynHook.call(this, 'afterGetRecord', id, result, options)
|
|
76
|
-
await execModelHook.call(this, 'afterGetRecord', id, result, options)
|
|
77
|
-
await execHook.call(this, 'afterGetRecord', id, result, options)
|
|
78
|
-
if (!noCache && setCache) {
|
|
79
|
-
await setCache({ model: this, id, result, options })
|
|
80
|
-
result.cached = false
|
|
81
|
-
}
|
|
82
|
-
return dataOnly ? result.data : result
|
|
83
74
|
}
|
|
84
75
|
|
|
85
76
|
export default getRecord
|
|
@@ -36,8 +36,8 @@ async function removeRecord (...args) {
|
|
|
36
36
|
let [id, opts = {}] = args
|
|
37
37
|
const { getDefaultValues } = this.app.dobo
|
|
38
38
|
const { isSet } = this.app.lib.aneka
|
|
39
|
-
|
|
40
|
-
const { dataOnly
|
|
39
|
+
opts.dataOnly = opts.dataOnly ?? true
|
|
40
|
+
const { dataOnly } = opts
|
|
41
41
|
const { options } = await getFilterAndOptions.call(this, null, opts, action)
|
|
42
42
|
const { noResult, noResultSanitizer } = options
|
|
43
43
|
id = this.sanitizeId(id)
|
|
@@ -45,10 +45,7 @@ async function removeRecord (...args) {
|
|
|
45
45
|
await execModelHook.call(this, 'beforeRemoveRecord', id, options)
|
|
46
46
|
await execDynHook.call(this, 'beforeRemoveRecord', id, options)
|
|
47
47
|
const result = options.record ?? (await this.driver._removeRecord(this, id, options)) ?? {}
|
|
48
|
-
if (noResult)
|
|
49
|
-
await runHook('cache:clear', this, 'remove', id)
|
|
50
|
-
return
|
|
51
|
-
}
|
|
48
|
+
if (noResult) return
|
|
52
49
|
const { warnings } = getDefaultValues(options)
|
|
53
50
|
if (!warnings) delete result.warnings
|
|
54
51
|
if (!noResultSanitizer) result.oldData = await this.sanitizeRecord(result.oldData, options)
|
|
@@ -57,7 +54,6 @@ async function removeRecord (...args) {
|
|
|
57
54
|
await execDynHook.call(this, 'afterRemoveRecord', id, result, options)
|
|
58
55
|
await execModelHook.call(this, 'afterRemoveRecord', id, result, options)
|
|
59
56
|
await execHook.call(this, 'afterRemoveRecord', id, result, options)
|
|
60
|
-
await runHook('cache:clear', this, 'remove', id, result, options)
|
|
61
57
|
return dataOnly ? result.oldData : result
|
|
62
58
|
}
|
|
63
59
|
|
|
@@ -49,9 +49,9 @@ async function updateRecord (...args) {
|
|
|
49
49
|
let [id, body = {}, opts = {}] = args
|
|
50
50
|
const { getDefaultValues } = this.app.dobo
|
|
51
51
|
const { isSet } = this.app.lib.aneka
|
|
52
|
-
const { runHook } = this.app.bajo
|
|
53
52
|
const { cloneDeep, get, omit } = this.app.lib._
|
|
54
|
-
|
|
53
|
+
opts.dataOnly = opts.dataOnly ?? true
|
|
54
|
+
const { dataOnly } = opts
|
|
55
55
|
const { options } = await getFilterAndOptions.call(this, null, opts, action)
|
|
56
56
|
options.partial = options.partial ?? true
|
|
57
57
|
const { truncateString, noResult, noBodySanitizer, noResultSanitizer, noValidation, partial } = options
|
|
@@ -67,10 +67,7 @@ async function updateRecord (...args) {
|
|
|
67
67
|
await execDynHook.call(this, 'beforeUpdateRecord', id, input, options)
|
|
68
68
|
if (!noValidation) await execValidation.call(this, input, options)
|
|
69
69
|
const result = options.record ?? (await this.driver._updateRecord(this, id, input, options)) ?? {}
|
|
70
|
-
if (noResult)
|
|
71
|
-
await runHook('cache:clear', this, 'update', id, body)
|
|
72
|
-
return
|
|
73
|
-
}
|
|
70
|
+
if (noResult) return
|
|
74
71
|
const { warnings } = getDefaultValues(options)
|
|
75
72
|
if (!warnings) delete result.warnings
|
|
76
73
|
if (!noResultSanitizer) {
|
|
@@ -82,7 +79,6 @@ async function updateRecord (...args) {
|
|
|
82
79
|
await execDynHook.call(this, 'afterUpdateRecord', id, input, result, options)
|
|
83
80
|
await execModelHook.call(this, 'afterUpdateRecord', id, input, result, options)
|
|
84
81
|
await execHook.call(this, 'afterUpdateRecord', id, input, result, options)
|
|
85
|
-
await runHook('cache:clear', this, 'update', id, body, result, options)
|
|
86
82
|
return dataOnly ? result.data : result
|
|
87
83
|
}
|
|
88
84
|
|
|
@@ -4,9 +4,9 @@ const action = 'upsertRecord'
|
|
|
4
4
|
async function native (body = {}, opts = {}) {
|
|
5
5
|
const { isSet } = this.app.lib.aneka
|
|
6
6
|
const { getDefaultValues } = this.app.dobo
|
|
7
|
-
const { runHook } = this.app.bajo
|
|
8
7
|
const { cloneDeep, get } = this.app.lib._
|
|
9
|
-
|
|
8
|
+
opts.dataOnly = opts.dataOnly ?? true
|
|
9
|
+
const { dataOnly } = opts
|
|
10
10
|
const { options } = await getFilterAndOptions.call(this, null, opts, action)
|
|
11
11
|
const { truncateString, noResult, noBodySanitizer, noResultSanitizer, noValidation } = options
|
|
12
12
|
const extFields = get(options, 'validation.extFields', [])
|
|
@@ -16,10 +16,7 @@ async function native (body = {}, opts = {}) {
|
|
|
16
16
|
await execModelHook.call(this, 'beforeUpsertRecord', input, options)
|
|
17
17
|
await execDynHook.call(this, 'beforeUpsertRecord', input, options)
|
|
18
18
|
const result = options.record ?? (await this.driver._upsertRecord(this, input, options)) ?? {}
|
|
19
|
-
if (noResult)
|
|
20
|
-
await runHook('cache:clear', this, 'upsert', body)
|
|
21
|
-
return
|
|
22
|
-
}
|
|
19
|
+
if (noResult) return
|
|
23
20
|
const { warnings } = getDefaultValues(options)
|
|
24
21
|
if (!warnings) delete result.warnings
|
|
25
22
|
if (!noResultSanitizer) result.data = await this.sanitizeRecord(result.data, options)
|
|
@@ -28,7 +25,6 @@ async function native (body = {}, opts = {}) {
|
|
|
28
25
|
await execDynHook.call(this, 'afterUpsertRecord', input, result, options)
|
|
29
26
|
await execModelHook.call(this, 'afterUpsertRecord', input, result, options)
|
|
30
27
|
await execHook.call(this, 'afterUpsertRecord', input, result, options)
|
|
31
|
-
await runHook('cache:clear', this, 'upsert', body, result, options)
|
|
32
28
|
return dataOnly ? result.data : result
|
|
33
29
|
}
|
|
34
30
|
|
package/package.json
CHANGED
package/wiki/CHANGES.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# Changes
|
|
2
2
|
|
|
3
|
+
## 2026-04-21
|
|
4
|
+
|
|
5
|
+
- [2.5.1] Bug fix in ```collect-models.js```, now values are sanitized with ```parseObject()```
|
|
6
|
+
- [2.5.1] Bug fix in ```options.dataOnly``` on all model methods
|
|
7
|
+
- [2.5.1] Add ```options.noMagic```
|
|
8
|
+
|
|
9
|
+
## 2026-04-19
|
|
10
|
+
|
|
11
|
+
- [2.19.0] Add ```queryAny()``` for query using model's scanables fields
|
|
12
|
+
- [2.19.0] Bug fix in ```reviveRegexInJson()```
|
|
13
|
+
- [2.19.0] Bug fix in query sanitizing especially for regex existance
|
|
14
|
+
- [2.19.0] Remove ```replaceIdInQuerySearch()``` in ```_util.js``` as it isn't needed anymore
|
|
15
|
+
|
|
3
16
|
## 2026-04-18
|
|
4
17
|
|
|
5
18
|
- [2.18.2] Bug fix in ```config``` object
|