dobo 2.13.0 → 2.14.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
@@ -150,13 +150,14 @@ async function factory (pkgName) {
150
150
  filter: {
151
151
  limit: 25, // num of records per page
152
152
  maxLimit: 200, // max num of records per page
153
- hardCap: 10000, // max returned records
154
153
  maxPage: 50, // max allowed page number
155
154
  sort: ['dt:-1', 'updatedAt:-1', 'updated_at:-1', 'createdAt:-1', 'createdAt:-1', 'ts:-1', 'username', 'name']
156
155
  },
157
156
  cache: {
158
157
  ttlDur: '10s'
159
- }
158
+ },
159
+ hardCap: 10000, // max returned records
160
+ warnings: true
160
161
  },
161
162
  memDb: {
162
163
  autoSaveDur: '1s'
@@ -497,12 +498,13 @@ async function factory (pkgName) {
497
498
  getDefaultValues = (options = {}) => {
498
499
  const { get } = this.app.lib._
499
500
  const config = this.app.dobo.config
500
- const limit = get(options, 'req.site.setting.dobo.default.limit', config.default.filter.limit)
501
- const maxLimit = get(options, 'req.site.setting.dobo.default.maxLimit', config.default.filter.maxLimit)
502
- const hardCap = get(options, 'req.site.setting.dobo.default.hardCap', config.default.filter.hardCap)
503
- const maxPage = get(options, 'req.site.setting.dobo.default.maxPage', config.default.filter.maxPage)
501
+ const limit = get(options, 'req.site.setting.dobo.default.filter.limit', config.default.filter.limit)
502
+ const maxLimit = get(options, 'req.site.setting.dobo.default.filter.maxLimit', config.default.filter.maxLimit)
503
+ const maxPage = get(options, 'req.site.setting.dobo.default.filter.maxPage', config.default.filter.maxPage)
504
+ const hardCap = get(options, 'req.site.setting.dobo.default.hardCap', config.default.hardCap)
505
+ const warnings = get(options, 'req.site.setting.dobo.default.warnings', config.default.warnings)
504
506
  const t = options.req ? options.req.t : this.t
505
- return { limit, maxLimit, hardCap, maxPage, t }
507
+ return { limit, maxLimit, hardCap, maxPage, warnings, t }
506
508
  }
507
509
 
508
510
  handleLastPage = (params = {}, options = {}) => {
@@ -236,6 +236,22 @@ export async function getMultiRefs (records = [], options = {}) {
236
236
  }
237
237
  }
238
238
 
239
+ export function parseNql (text) {
240
+ const sanitized = text.split('+').map(item => {
241
+ const [key, ...rest] = item.split(':').map(i => i.trim())
242
+ let value = rest.join(':')
243
+ const neg = value[1] === '-' ? '-' : ''
244
+ if ((value[0] === '{' || value[1] === '{') && value[value.length - 1] === '}') {
245
+ if (value[0] === '-') value = value.slice(1)
246
+ const items = value.slice(1, -1).split(',')
247
+ value = `${neg}>=${items[0]}+${key}:${neg}<=${items[1]}`
248
+ }
249
+ return `${key}:${value}`
250
+ }).join('+')
251
+
252
+ return nql(sanitized).parse()
253
+ }
254
+
239
255
  export function buildFilterQuery (filter = {}) {
240
256
  const { trim, find, isString, isPlainObject } = this.app.lib._
241
257
  let query = filter.query ?? {}
@@ -244,7 +260,7 @@ export function buildFilterQuery (filter = {}) {
244
260
  try {
245
261
  query = trim(query)
246
262
  if (query.startsWith('{')) q = JSON.parse(query) // JSON formatted query
247
- else if (query.includes(':')) q = nql(query).parse() // NQL
263
+ else if (query.includes(':')) q = parseNql.call(this, query) // NQL
248
264
  else {
249
265
  let scanables = [...this.scanables]
250
266
  if (scanables.length === 0) scanables = [...this.sortables]
@@ -16,8 +16,11 @@ async function countRecord (...args) {
16
16
  result.warnings = result.warnings ?? []
17
17
  result.warnings.push(t('hardCapWarning%s%s', result.data, hardCap))
18
18
  result.orgCount = result.data
19
+ result.hardCapped = true
19
20
  result.data = hardCap
20
21
  }
22
+ const { warnings } = getDefaultValues(options)
23
+ if (!warnings) delete result.warnings
21
24
  await execDynHook.call(this, 'afterCountRecord', filter, result, options)
22
25
  await execModelHook.call(this, 'afterCountRecord', filter, result, options)
23
26
  await execHook.call(this, 'afterCountRecord', filter, result, options)
@@ -6,10 +6,13 @@ async function createAggregate (...args) {
6
6
  const [_filter = {}, params = {}, opts = {}] = args
7
7
  const { dataOnly = true } = opts
8
8
  const { filter, options } = await getFilterAndOptions.call(this, _filter, opts, action)
9
+ const { getDefaultValues } = this.app.dobo
9
10
  await execHook.call(this, 'beforeCreateAggregate', filter, params, options)
10
11
  await execModelHook.call(this, 'beforeCreateAggregate', filter, params, options)
11
12
  await execDynHook.call(this, 'beforeCreateAggregate', filter, params, options)
12
13
  const result = (await this.driver._createAggregate(this, filter, params, options)) ?? {}
14
+ const { warnings } = getDefaultValues(options)
15
+ if (!warnings) delete result.warnings
13
16
  await execDynHook.call(this, 'afterCreateAggregate', filter, params, result, options)
14
17
  await execModelHook.call(this, 'afterCreateAggregate', filter, params, result, options)
15
18
  await execHook.call(this, 'afterCreateAggregate', filter, params, result, options)
@@ -6,10 +6,13 @@ async function createHistogram (...args) {
6
6
  const [_filter = {}, params = {}, opts = {}] = args
7
7
  const { dataOnly = true } = opts
8
8
  const { filter, options } = await getFilterAndOptions.call(this, _filter, opts, action)
9
+ const { getDefaultValues } = this.app.dobo
9
10
  await execHook.call(this, 'beforeCreateHistogram', filter, params, options)
10
11
  await execModelHook.call(this, 'beforeCreateHistogram', filter, params, options)
11
12
  await execDynHook.call(this, 'beforeCreateHistogram', filter, params, options)
12
13
  const result = (await this.driver._createHistogram(this, filter, params, options)) ?? {}
14
+ const { warnings } = getDefaultValues(options)
15
+ if (!warnings) delete result.warnings
13
16
  await execDynHook.call(this, 'afterCreateHistogram', filter, params, result, options)
14
17
  await execModelHook.call(this, 'afterCreateHistogram', filter, params, result, options)
15
18
  await execHook.call(this, 'afterCreateHistogram', filter, params, result, options)
@@ -6,6 +6,7 @@ const action = 'createRecord'
6
6
  async function createRecord (...args) {
7
7
  if (args.length === 0) return this.action(action, ...args)
8
8
  const [body = {}, opts = {}] = args
9
+ const { getDefaultValues } = this.app.dobo
9
10
  const { isSet } = this.app.lib.aneka
10
11
  const { runHook } = this.app.bajo
11
12
  const { cloneDeep, get } = this.app.lib._
@@ -24,6 +25,8 @@ async function createRecord (...args) {
24
25
  return
25
26
  }
26
27
  result = result ?? {}
28
+ const { warnings } = getDefaultValues(options)
29
+ if (!warnings) delete result.warnings
27
30
  if (!noResultSanitizer) result.data = await this.sanitizeRecord(result.data, options)
28
31
  if (isSet(options.refs)) await getSingleRef.call(this, result.data, options)
29
32
  await handleReq.call(this, result.data.id, 'created', options)
@@ -3,11 +3,13 @@ const action = 'findAllRecord'
3
3
 
4
4
  async function native (...args) {
5
5
  const { isSet } = this.app.lib.aneka
6
- const { cloneDeep } = this.app.lib._
6
+ const { getDefaultValues, t } = this.app.dobo
7
+ const { cloneDeep, pick, omit } = this.app.lib._
7
8
  const [params = {}, opts = {}] = args
8
9
  const { dataOnly = true } = opts
9
10
  const { get: getCache, set: setCache } = this.app.bajoCache ?? {}
10
11
  const { filter, options } = await getFilterAndOptions.call(this, params, opts, action)
12
+ const { hardCap, warnings } = getDefaultValues(options)
11
13
  if (dataOnly) options.count = false
12
14
  let { noResultSanitizer, noCache } = options
13
15
  await execHook.call(this, 'beforeFindRecord', filter, options)
@@ -25,7 +27,19 @@ async function native (...args) {
25
27
  return dataOnly ? result.data : result
26
28
  }
27
29
  }
28
- const result = options.record ?? (await this.driver._findAllRecord(this, filter, options)) ?? {}
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
+
29
43
  if (!noResultSanitizer) {
30
44
  for (const idx in result.data) {
31
45
  result.data[idx] = await this.sanitizeRecord(result.data[idx], options)
@@ -48,7 +62,7 @@ async function loop (...args) {
48
62
  const [params = {}, opts = {}] = args
49
63
  const { dataOnly = true } = opts
50
64
  const { filter, options } = await getFilterAndOptions.call(this, params, opts, action)
51
- const { maxLimit, hardCap } = getDefaultValues(options)
65
+ const { maxLimit, hardCap, warnings: showWarnings } = getDefaultValues(options)
52
66
  const nFilter = cloneDeep(filter || {})
53
67
  const nOptions = cloneOptions.call(this, options)
54
68
  nOptions.count = false
@@ -71,7 +85,9 @@ async function loop (...args) {
71
85
  count = count + result.data.length
72
86
  nFilter.page++
73
87
  }
74
- return dataOnly ? data : { data, count, warnings }
88
+ const result = { data, count, warnings }
89
+ if (!showWarnings) delete result.warnings
90
+ return dataOnly ? data : result
75
91
  }
76
92
 
77
93
  async function findAllRecord (...args) {
@@ -4,6 +4,7 @@ const action = 'findOneRecord'
4
4
 
5
5
  async function findOneRecord (...args) {
6
6
  if (args.length === 0) return this.action(action, ...args)
7
+ const { getDefaultValues } = this.app.dobo
7
8
  const [params = {}, opts = {}] = args
8
9
  const { cloneDeep } = this.app.lib._
9
10
  const { dataOnly = true } = opts
@@ -13,9 +14,12 @@ async function findOneRecord (...args) {
13
14
  nOptions.count = false
14
15
  nOptions.dataOnly = false
15
16
  nFilter.limit = 1
16
- const result = await this.findRecord(nFilter, nOptions)
17
- const data = result.data[0]
18
- return dataOnly ? data : { data, cached: result.cached, warnings: result.warnings }
17
+ const { warnings } = getDefaultValues(nOptions)
18
+ const resp = await this.findRecord(nFilter, nOptions)
19
+ const data = resp.data[0]
20
+ const result = { data, cached: resp.cached, warnings: resp.warnings }
21
+ if (!warnings) delete result.warnings
22
+ return dataOnly ? data : result
19
23
  }
20
24
 
21
25
  export default findOneRecord
@@ -73,7 +73,7 @@ async function findRecord (...args) {
73
73
  const { dataOnly = true } = opts
74
74
  const { get: getCache, set: setCache } = this.app.bajoCache ?? {}
75
75
  const { filter, options } = await getFilterAndOptions.call(this, params, opts, action)
76
- const { hardCap } = getDefaultValues(options)
76
+ const { hardCap, warnings } = getDefaultValues(options)
77
77
  if (dataOnly) options.count = false
78
78
  let { noResultSanitizer, noCache } = options
79
79
  await execHook.call(this, 'beforeFindRecord', filter, options)
@@ -100,8 +100,10 @@ async function findRecord (...args) {
100
100
  else if (options.count && result.count > hardCap) {
101
101
  result.warnings.push(t('hardCapWarning%s%s', result.count, hardCap))
102
102
  result.count = hardCap
103
+ result.hardCapped = true
103
104
  }
104
105
  result.pages = options.count ? Math.ceil(result.count / filter.limit) : undefined
106
+ if (!warnings) delete result.warnings
105
107
 
106
108
  if (!noResultSanitizer) {
107
109
  for (const idx in result.data) {
@@ -45,6 +45,7 @@ const action = 'getRecord'
45
45
  async function getRecord (...args) {
46
46
  if (args.length === 0) return this.action(action, ...args)
47
47
  let [id, opts = {}] = args
48
+ const { getDefaultValues } = this.app.dobo
48
49
  const { isEmpty } = this.app.lib._
49
50
  const { isSet } = this.app.lib.aneka
50
51
  const { get: getCache, set: setCache } = this.app.bajoCache ?? {}
@@ -66,6 +67,8 @@ async function getRecord (...args) {
66
67
  }
67
68
  }
68
69
  const result = options.record ?? (await this.driver._getRecord(this, id, options)) ?? {}
70
+ const { warnings } = getDefaultValues(options)
71
+ if (!warnings) delete result.warnings
69
72
  if (isEmpty(result.data) && !options.throwNotFound) return dataOnly ? undefined : { data: undefined }
70
73
  if (!noResultSanitizer) result.data = await this.sanitizeRecord(result.data, options)
71
74
  if (isSet(options.refs)) await getSingleRef.call(this, result.data, options)
@@ -34,6 +34,7 @@ const action = 'removeRecord'
34
34
  async function removeRecord (...args) {
35
35
  if (args.length === 0) return this.action(action, ...args)
36
36
  let [id, opts = {}] = args
37
+ const { getDefaultValues } = this.app.dobo
37
38
  const { isSet } = this.app.lib.aneka
38
39
  const { runHook } = this.app.bajo
39
40
  const { dataOnly = true } = opts
@@ -48,6 +49,8 @@ async function removeRecord (...args) {
48
49
  await runHook('cache:clear', this, 'remove', id)
49
50
  return
50
51
  }
52
+ const { warnings } = getDefaultValues(options)
53
+ if (!warnings) delete result.warnings
51
54
  if (!noResultSanitizer) result.oldData = await this.sanitizeRecord(result.oldData, options)
52
55
  if (isSet(options.refs)) await getSingleRef.call(this, result.data, options)
53
56
  await handleReq.call(this, result.oldData.id, 'removed', options)
@@ -11,9 +11,11 @@
11
11
  */
12
12
  async function sanitizeRecord (record = {}, opts = {}) {
13
13
  const { fields = [], hidden = [], forceNoHidden } = opts
14
- const { isEmpty, map, without } = this.app.lib._
14
+ const { isEmpty, map, without, isArray } = this.app.lib._
15
15
  const { fillObject } = this.app.lib.aneka
16
- const allHidden = forceNoHidden ? [] : without([...this.hidden, ...hidden], 'id')
16
+ let allHidden = without([...this.hidden, ...hidden], 'id')
17
+ if (forceNoHidden === true) allHidden = []
18
+ else if (isArray(forceNoHidden)) allHidden = without(allHidden, ...forceNoHidden)
17
19
  let newFields = [...fields]
18
20
  if (isEmpty(newFields)) newFields = map(this.properties, prop => prop.name)
19
21
  if (!newFields.includes('id')) newFields.unshift('id')
@@ -47,6 +47,7 @@ const action = 'updateRecord'
47
47
  async function updateRecord (...args) {
48
48
  if (args.length === 0) return this.action(action, ...args)
49
49
  let [id, body = {}, opts = {}] = args
50
+ const { getDefaultValues } = this.app.dobo
50
51
  const { isSet } = this.app.lib.aneka
51
52
  const { runHook } = this.app.bajo
52
53
  const { cloneDeep, get, omit } = this.app.lib._
@@ -69,6 +70,8 @@ async function updateRecord (...args) {
69
70
  await runHook('cache:clear', this, 'update', id, body)
70
71
  return
71
72
  }
73
+ const { warnings } = getDefaultValues(options)
74
+ if (!warnings) delete result.warnings
72
75
  if (!noResultSanitizer) {
73
76
  result.data = await this.sanitizeRecord(result.data, options)
74
77
  result.oldData = await this.sanitizeRecord(result.oldData, options)
@@ -3,6 +3,7 @@ const action = 'upsertRecord'
3
3
 
4
4
  async function native (body = {}, opts = {}) {
5
5
  const { isSet } = this.app.lib.aneka
6
+ const { getDefaultValues } = this.app.dobo
6
7
  const { runHook } = this.app.bajo
7
8
  const { cloneDeep, get } = this.app.lib._
8
9
  const { dataOnly = true } = opts
@@ -19,6 +20,8 @@ async function native (body = {}, opts = {}) {
19
20
  await runHook('cache:clear', this, 'upsert', body)
20
21
  return
21
22
  }
23
+ const { warnings } = getDefaultValues(options)
24
+ if (!warnings) delete result.warnings
22
25
  if (!noResultSanitizer) result.data = await this.sanitizeRecord(result.data, options)
23
26
  if (isSet(options.refs)) await getSingleRef.call(this, result.data, options)
24
27
  await handleReq.call(this, result.data.id, 'upserted', options)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dobo",
3
- "version": "2.13.0",
3
+ "version": "2.14.1",
4
4
  "description": "DBMS for Bajo Framework",
5
5
  "main": "index.js",
6
6
  "scripts": {
package/wiki/CHANGES.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # Changes
2
2
 
3
+ ## 2026-04-01
4
+
5
+ - [2.14.0] Add ```between``` as custom query, since it doesn't exists in NQL
6
+
3
7
  ## 2026-03-30
4
8
 
5
9
  - [2.12.0] Add ```.bootorder``` level 10