dobo 2.18.1 → 2.19.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/index.js CHANGED
@@ -501,9 +501,12 @@ async function factory (pkgName) {
501
501
  }
502
502
 
503
503
  getDefaultValues = (options = {}) => {
504
+ const { defaultsDeep } = this.app.lib.aneka
504
505
  const key = 'default.filter'
505
- let config = this.app.dobo.getConfig(key)
506
- if (options.req) config = options.req.getSetting(`dobo:${key}`, config)
506
+ let config = this.getConfig(key)
507
+ config.hardCap = this.config.default.hardCap
508
+ config.warnings = this.config.default.warnings
509
+ if (options.req) config = defaultsDeep({}, options.req.getSetting('dobo:default'), config)
507
510
  const { limit, maxLimit, maxPage, hardCap, warnings } = config
508
511
  const t = options.req ? options.req.t : this.t
509
512
  return { limit, maxLimit, hardCap, maxPage, warnings, t }
@@ -536,23 +539,47 @@ async function factory (pkgName) {
536
539
  return nql(sanitized).parse()
537
540
  }
538
541
 
539
- parseQuery = (query, silent = true) => {
540
- 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._
541
565
  let result = {}
542
566
  if (isPlainObject(query)) result = query
543
567
  else {
544
568
  query = trim(query)
569
+ if (isEmpty(query)) return result
545
570
  try {
546
571
  if (query.startsWith('{')) result = JSON.parse(query)
547
- else result = this.parseNql(query)
572
+ else if (query.includes(':')) result = this.parseNql(query)
573
+ else result = this.parseAny(query, model)
548
574
  } catch (err) {
549
575
  if (silent) return {}
550
576
  throw err
551
577
  }
552
578
  }
579
+ let strQ = this.replaceRegexInJson(result)
580
+ if (model && model.driver.idField.name !== 'id') strQ = strQ.replaceAll('"id"', `"${model.driver.idField.name}"`)
553
581
  try {
554
- const text = JSON.stringify(result)
555
- if (text.includes('["__REGEXP__",')) result = this.reviveRegexInJson(text)
582
+ result = this.reviveRegexInJson(strQ)
556
583
  } catch (err) {}
557
584
  return result
558
585
  }
@@ -572,7 +599,7 @@ async function factory (pkgName) {
572
599
  const { isPlainObject } = this.app.lib._
573
600
  if (isPlainObject(input)) input = JSON.stringify(input)
574
601
  const result = JSON.parse(input, (key, value) => {
575
- 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])
576
603
  return value
577
604
  })
578
605
  return returnObject ? result : JSON.stringify(result)
@@ -10,7 +10,7 @@ import actionFactory from './factory/action.js'
10
10
  * @param {Array} [indexes] - Container array to fill up found index
11
11
  */
12
12
  async function sanitizeProp (model, prop, indexes) {
13
- const { isEmpty, isString, keys, pick, isArray, isPlainObject } = this.app.lib._
13
+ const { isEmpty, isString, keys, pick, isArray, isPlainObject, omit } = this.app.lib._
14
14
  const allPropKeys = this.getAllPropertyKeys(model.connection.driver)
15
15
  const propType = this.constructor.propertyType
16
16
  if (isString(prop)) {
@@ -39,7 +39,9 @@ async function sanitizeProp (model, prop, indexes) {
39
39
  else {
40
40
  const feature = this.getFeature(prop.type)
41
41
  if (!feature) this.fatal('unknownPropType%s%s', prop.type, model.name)
42
- await applyFeature.call(this, model, feature, { field: prop.name }, indexes)
42
+ const opts = omit(prop, ['name', 'type'])
43
+ opts.field = prop.name
44
+ await applyFeature.call(this, model, feature, opts, indexes)
43
45
  }
44
46
  }
45
47
 
@@ -77,7 +77,6 @@ export async function getFilterAndOptions (filter = {}, options = {}, action) {
77
77
  if (!nOptions.noModelHook) await runModelHook(this, 'beforeBuilSearch', nFilter.search, nOptions)
78
78
  nFilter.search = buildFilterSearch.call(this, nFilter) ?? {}
79
79
  if (!nOptions.noModelHook) await runModelHook(this, 'afterBuildSearch', nFilter.search, nOptions)
80
- if (this.driver.idField.name !== 'id') replaceIdInQuerySearch.call(this, nFilter)
81
80
  const { limit, page, skip, sort } = preparePagination.call(this, nFilter, nOptions)
82
81
  nFilter.limit = limit
83
82
  nFilter.page = page
@@ -190,7 +189,7 @@ export async function getSingleRef (record = {}, options = {}) {
190
189
  let query = {}
191
190
  query[ref.field] = record[prop.name]
192
191
  if (ref.field === 'id') query[ref.field] = this.sanitizeId(query[ref.field])
193
- if (ref.query) query = { $and: [query, parseQuery(ref.query)] }
192
+ if (ref.query) query = { $and: [query, parseQuery(ref.query, rModel)] }
194
193
  const filter = { query }
195
194
  const resp = await _getRef.call(this, { ref, rModel, prop, key, options, filter })
196
195
  if (!resp) continue
@@ -228,7 +227,7 @@ export async function getMultiRefs (records = [], options = {}) {
228
227
  matches = uniq(without(matches, undefined, null, NaN))
229
228
  let query = {}
230
229
  query[ref.field] = { $in: matches }
231
- if (ref.query) query = { $and: [query, parseQuery(ref.query)] }
230
+ if (ref.query) query = { $and: [query, parseQuery(ref.query, rModel)] }
232
231
  const filter = { query, limit: matches.length }
233
232
  const resp = await _getRef.call(this, { ref, rModel, prop, key, options, filter })
234
233
  if (!resp) continue
@@ -247,35 +246,9 @@ export async function getMultiRefs (records = [], options = {}) {
247
246
  }
248
247
 
249
248
  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)
249
+ const { parseQuery } = this.app.dobo
250
+ const query = parseQuery(filter.query ?? {}, this, false)
251
+ return sanitizeQuery.call(this, query)
279
252
  }
280
253
 
281
254
  function sanitizeQuery (query = {}, parent) {
@@ -346,7 +319,7 @@ export function buildFilterSearch (filter = {}) {
346
319
  items[part.field].push(...part.value.split(' ').filter(v => ![''].includes(v)))
347
320
  }
348
321
  }
349
- const s = {}
322
+ let s = {}
350
323
  for (const index of this.indexes.filter(i => i.type === 'fulltext')) {
351
324
  for (const f of index.fields) {
352
325
  const value = []
@@ -357,22 +330,15 @@ export function buildFilterSearch (filter = {}) {
357
330
  }
358
331
  }
359
332
  if (has(items, '*')) s['*'] = items['*']
333
+ if (this.driver.idField.name !== 'id') {
334
+ const search = JSON.stringify(s).replaceAll('"id"', `"${this.driver.idField.name}"`)
335
+ try {
336
+ s = JSON.parse(search)
337
+ } catch (err) {}
338
+ }
360
339
  return s
361
340
  }
362
341
 
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
342
  /**
377
343
  * Prepare records pagination:
378
344
  * - making sure records limit is obeyed
@@ -46,10 +46,10 @@ async function sanitizeRecord (record = {}, opts = {}) {
46
46
  })
47
47
  value = (values.find(v => v.value === value) ?? {}).text ?? value
48
48
  }
49
- if (prop.format) {
50
- if (isFunction(prop.format)) newRecord[key] = await prop.format.call(this, value, newRecord, opts)
51
- else if (isString(prop.format)) newRecord[key] = await callHandler(this.plugin, this, value, newRecord, opts)
52
- } else {
49
+ if (prop.format === false) newRecord[key] = value + ''
50
+ else if (isFunction(prop.format)) newRecord[key] = await prop.format.call(this, value, newRecord, opts)
51
+ else if (isString(prop.format)) newRecord[key] = await callHandler(this.plugin, this, value, newRecord, opts)
52
+ else {
53
53
  const options = {
54
54
  lang: get(opts, 'req.lang'),
55
55
  latitude: ['lat', 'latitude'].includes(key),
@@ -57,8 +57,6 @@ async function sanitizeRecord (record = {}, opts = {}) {
57
57
  }
58
58
  newRecord[key] = format(value, prop.type, options)
59
59
  }
60
- // exception
61
- if (['year'].includes(key)) newRecord[key] = (get(newRecord, `_orig.${key}`, newRecord[key]) + '')
62
60
  }
63
61
  }
64
62
  if (record._ref) newRecord._ref = record._ref
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dobo",
3
- "version": "2.18.1",
3
+ "version": "2.19.0",
4
4
  "description": "DBMS for Bajo Framework",
5
5
  "main": "index.js",
6
6
  "scripts": {
package/wiki/CHANGES.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # Changes
2
2
 
3
+ ## 2026-04-19
4
+
5
+ - [2.19.0] Add ```queryAny()``` for query using model's scanables fields
6
+ - [2.19.0] Bug fix in ```reviveRegexInJson()```
7
+ - [2.19.0] Bug fix in query sanitizing especially for regex existance
8
+ - [2.19.0] Remove ```replaceIdInQuerySearch()``` in ```_util.js``` as it isn't needed anymore
9
+
10
+ ## 2026-04-18
11
+
12
+ - [2.18.2] Bug fix in ```config``` object
13
+ - [2.18.2] Bug fix in ```getDefaultValues```
14
+ - [2.18.2] Bug fix in ```sanitizeProp()``` in ```collect-models.js```
15
+ - [2.18.2] Bug fix in ```sanitizeRecord()``` for prop with ```format``` set to ```false```
16
+
3
17
  ## 2026-04-17
4
18
 
5
19
  - [2.18.1] Bug fix in ```model.createAttachment()```