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 CHANGED
@@ -539,23 +539,47 @@ async function factory (pkgName) {
539
539
  return nql(sanitized).parse()
540
540
  }
541
541
 
542
- parseQuery = (query, silent = true) => {
543
- const { isPlainObject, trim } = this.app.lib._
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
- const text = JSON.stringify(result)
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 { $regex: new RegExp(value[1], value[2]) }
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)
@@ -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 { trim, find, isString, isPlainObject } = this.app.lib._
251
- const { parseNql } = this.app.dobo
252
- let query = filter.query ?? {}
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
- const s = {}
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
- const { dataOnly = true } = opts
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
- const { dataOnly = true } = opts
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
- const { dataOnly = true } = opts
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
- const { dataOnly = true } = opts
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
- const { dataOnly = true } = opts
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
- const { dataOnly = true } = opts
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
- const { dataOnly = true } = opts
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
- const { dataOnly = true } = opts
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 { cloneDeep, pick, omit } = this.app.lib._
7
+ const { pick, omit } = this.app.lib._
8
8
  const [params = {}, opts = {}] = args
9
- const { dataOnly = true } = opts
10
- const { get: getCache, set: setCache } = this.app.bajoCache ?? {}
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
- let { noResultSanitizer, noCache } = options
15
- await execHook.call(this, 'beforeFindRecord', filter, options)
16
- await execModelHook.call(this, 'beforeFindRecord', filter, options)
17
- await execDynHook.call(this, 'beforeFindRecord', filter, options)
18
- if (this.cache.ttlDur === 0 || options.record) noCache = true
19
- const cacheFilter = cloneDeep(filter)
20
- if (!noCache && getCache) {
21
- const result = await getCache({ model: this, filter: cacheFilter, options })
22
- if (result) {
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
- const { dataOnly = true } = opts
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
- const { dataOnly = true } = opts
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, cached: resp.cached, warnings: resp.warnings }
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 { cloneDeep, pick, omit } = this.app.lib._
73
- const { dataOnly = true } = opts
74
- const { get: getCache, set: setCache } = this.app.bajoCache ?? {}
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
- let { noResultSanitizer, noCache } = options
79
- await execHook.call(this, 'beforeFindRecord', filter, options)
80
- await execModelHook.call(this, 'beforeFindRecord', filter, options)
81
- await execDynHook.call(this, 'beforeFindRecord', filter, options)
82
- if (this.cache.ttlDur === 0 || options.record) noCache = true
83
- const cacheFilter = cloneDeep(filter)
84
- if (!noCache && getCache) {
85
- const result = await getCache({ model: this, filter: cacheFilter, options })
86
- if (result) {
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
- const { get: getCache, set: setCache } = this.app.bajoCache ?? {}
52
- const { dataOnly = true } = opts
51
+ opts.dataOnly = opts.dataOnly ?? true
52
+ const { dataOnly } = opts
53
53
  const { options } = await getFilterAndOptions.call(this, null, opts, action)
54
- let { noResultSanitizer, noCache } = options
55
- if (!this.cacheable || !options.record) noCache = true
54
+ const { noResultSanitizer } = options
56
55
  id = this.sanitizeId(id)
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
- if (this.cache.ttlDur === 0 || options.record) noCache = true
61
- if (!noCache && getCache) {
62
- const result = await getCache({ model: this, id, options })
63
- if (result) {
64
- result.data = await this.sanitizeRecord(result.data, options)
65
- result.cached = true
66
- return dataOnly ? result.data : result
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
- const { runHook } = this.app.bajo
40
- const { dataOnly = true } = opts
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
- const { dataOnly = true } = opts
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
- const { dataOnly = true } = opts
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dobo",
3
- "version": "2.18.2",
3
+ "version": "2.19.1",
4
4
  "description": "DBMS for Bajo Framework",
5
5
  "main": "index.js",
6
6
  "scripts": {
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