waibu-db 2.32.2 → 2.33.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/extend/waibuBootstrap/theme/component/widget/btn-export.js +1 -1
- package/extend/waibuBootstrap/theme/component/widget/query.js +157 -136
- package/extend/waibuBootstrap/theme/component/widget/recs-info.js +3 -3
- package/index.js +4 -4
- package/lib/crud/export-handler.js +1 -1
- package/lib/crud/helper.js +2 -2
- package/lib/method/{create-aggregate.js → aggregate.js} +5 -5
- package/lib/method/count-record.js +2 -2
- package/lib/method/find-all-record.js +2 -2
- package/lib/method/find-one-record.js +2 -2
- package/lib/method/find-record.js +2 -2
- package/lib/method/get-record.js +2 -2
- package/lib/method/{create-histogram.js → histogram.js} +5 -5
- package/lib/util.js +12 -8
- package/package.json +1 -1
- package/wiki/CHANGES.md +10 -0
|
@@ -101,7 +101,7 @@ async function btnExport () {
|
|
|
101
101
|
if (_.isEmpty(selector)) {
|
|
102
102
|
await wbs.notify('Cant get data selector', { type: 'danger' })
|
|
103
103
|
} else {
|
|
104
|
-
const item =
|
|
104
|
+
const item = ['list', ''].includes(handler) ? this.extractTable(selector) : this.extractForm(selector)
|
|
105
105
|
await wbs.copyToClipboard(item)
|
|
106
106
|
}
|
|
107
107
|
instance.hide()
|
|
@@ -4,12 +4,141 @@ async function query () {
|
|
|
4
4
|
const WdbBase = await wdbBase.call(this)
|
|
5
5
|
|
|
6
6
|
return class WdbQuery extends WdbBase {
|
|
7
|
+
modal = ({ container, models, columns, fields, id }) => {
|
|
8
|
+
const { jsonStringify } = this.app.waibuMpa
|
|
9
|
+
const { upperFirst } = this.app.lib._
|
|
10
|
+
const qsKey = this.app.waibu.config.qsKey
|
|
11
|
+
const icon = this.params.attr.mini ? 'search' : (this.params.attr.icon ?? 'dotsThree')
|
|
12
|
+
const applyBtn = !this.params.attr.mini ? '<c:btn color="primary" t:content="apply" margin="start-2" @click="submit()" />' : ''
|
|
13
|
+
let parse = 'parse () {},'
|
|
14
|
+
if (!this.params.attr.mini) {
|
|
15
|
+
parse = `parse () {
|
|
16
|
+
this.builder = document.getElementById('${id}').value
|
|
17
|
+
if (!this.builder.includes(':')) this.builder = ''
|
|
18
|
+
if (_.isEmpty(this.builder)) return
|
|
19
|
+
const tokens = _.merge({}, this.ops, {
|
|
20
|
+
in: ':[',
|
|
21
|
+
between: ':{',
|
|
22
|
+
contains: ':~',
|
|
23
|
+
starts: ':~^',
|
|
24
|
+
ends: ':~$$',
|
|
25
|
+
'!in': ':-[',
|
|
26
|
+
'!between': ':-{',
|
|
27
|
+
'!contains': ':-~',
|
|
28
|
+
'!starts': ':-~^',
|
|
29
|
+
'!ends': ':-~$$'
|
|
30
|
+
})
|
|
31
|
+
for (const part of this.builder.split('+')) {
|
|
32
|
+
let [f, ...opv] = part.split(':')
|
|
33
|
+
opv = ':' + opv.join(':')
|
|
34
|
+
this.selected.push(f)
|
|
35
|
+
let op
|
|
36
|
+
let val
|
|
37
|
+
_.each(tokens, (v, k) => {
|
|
38
|
+
if (opv.slice(0, v.length) === v) {
|
|
39
|
+
op = k
|
|
40
|
+
val = opv.slice(v.length).replaceAll('[', '').replaceAll('{', '').replaceAll(']', '').replaceAll('}', '').replaceAll('\\'', '')
|
|
41
|
+
}
|
|
42
|
+
})
|
|
43
|
+
if (_.isEmpty(op)) continue
|
|
44
|
+
this[f + 'Op'] = op
|
|
45
|
+
if (op === 'between') {
|
|
46
|
+
const vals = val.split(',')
|
|
47
|
+
this[f + 'Val'] = vals[0]
|
|
48
|
+
this[f + 'Val2'] = vals[1]
|
|
49
|
+
} else {
|
|
50
|
+
this[f + 'Val'] = val
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
`
|
|
55
|
+
}
|
|
56
|
+
return `
|
|
57
|
+
<c:${container} trigger-color="secondary-outline" trigger-icon="${icon}" trigger-on-end t:title="queryBuilder" x-ref="query" x-data="{
|
|
58
|
+
fields: ${jsonStringify(fields, true)},
|
|
59
|
+
builder: '',
|
|
60
|
+
selected: [],
|
|
61
|
+
${models.join(',\n')},
|
|
62
|
+
ops: { eq: ':', neq: ':-', gt: ':>', gte: ':>=', lt: ':<', lte: ':<=' },
|
|
63
|
+
opsIn (v, neg) { return ':' + (neg ? '-' : '') + '[' + this.expandArray(v) + ']' },
|
|
64
|
+
opsBetween (v, v2, neg) { return ':' + (neg ? '-' : '') + '{' + this.expandArray(v) + ',' + this.expandArray(v2) + '}' },
|
|
65
|
+
opsExt (v, neg, ext) {
|
|
66
|
+
let prefix = (neg ? '-' : '') + '~'
|
|
67
|
+
if (ext) prefix += ext
|
|
68
|
+
return ':' + prefix + '\\'' + v + '\\''
|
|
69
|
+
},
|
|
70
|
+
${parse}
|
|
71
|
+
expandArray (val = '') {
|
|
72
|
+
return _.map(val.split(','), item => {
|
|
73
|
+
item = _.trim(item)
|
|
74
|
+
if (Number(item)) return item
|
|
75
|
+
return '\\'' + item + '\\''
|
|
76
|
+
})
|
|
77
|
+
},
|
|
78
|
+
rebuild () {
|
|
79
|
+
const items = []
|
|
80
|
+
for (const sel of this.selected) {
|
|
81
|
+
const key = this[sel + 'Op']
|
|
82
|
+
let val = this[sel + 'Val']
|
|
83
|
+
let val2 = this[sel + 'Val2']
|
|
84
|
+
if (_.isEmpty(val)) continue
|
|
85
|
+
if (key === 'between' && _.isEmpty(val2)) continue
|
|
86
|
+
let item
|
|
87
|
+
if (key === 'in') item = this.opsIn(val)
|
|
88
|
+
else if (key === '!in') item = this.opsIn(val, true)
|
|
89
|
+
else if (key === 'between') item = this.opsBetween(val, val2, false)
|
|
90
|
+
else if (key === '!between') item = this.opsBetween(val, val2, true)
|
|
91
|
+
else if (key === 'contains') item = this.opsExt(val)
|
|
92
|
+
else if (key === '!contains') item = this.opsExt(val, true)
|
|
93
|
+
else if (key === 'starts') item = this.opsExt(val, false, '^')
|
|
94
|
+
else if (key === '!starts') item = this.opsExt(val, true, '^')
|
|
95
|
+
else if (key === 'ends') item = this.opsExt(val, false, '$$')
|
|
96
|
+
else if (key === '!ends') item = this.opsExt(val, true, '$$')
|
|
97
|
+
else if (val.includes(' ')) item = this.ops[key] + '\\'' + val + '\\''
|
|
98
|
+
else item = this.ops[key] + val
|
|
99
|
+
items.push(sel + item)
|
|
100
|
+
}
|
|
101
|
+
this.builder = items.join('+')
|
|
102
|
+
},
|
|
103
|
+
submit (run) {
|
|
104
|
+
if (run) {
|
|
105
|
+
const url = new URL(window.location.href)
|
|
106
|
+
const params = new URLSearchParams(url.search)
|
|
107
|
+
params.set('${qsKey.page}', 1)
|
|
108
|
+
params.set('${qsKey.query}', this.builder ?? '')
|
|
109
|
+
window.location.href = '?' + params.toString()
|
|
110
|
+
} else $dispatch('on-query', this.builder)
|
|
111
|
+
const instance = wbs.getInstance('${upperFirst(container)}', $refs.query)
|
|
112
|
+
instance.hide()
|
|
113
|
+
}
|
|
114
|
+
}" x-init="
|
|
115
|
+
parse()
|
|
116
|
+
const ops = _.map(fields, f => (f + 'Op'))
|
|
117
|
+
const vals = _.map(fields, f => (f + 'Val'))
|
|
118
|
+
const vals2 = _.map(fields, f => (f + 'Val2'))
|
|
119
|
+
const watcher = ['selected', ...ops, ...vals, ...vals2].join(',')
|
|
120
|
+
$watch(watcher, v => rebuild())
|
|
121
|
+
" ${this.params.attr.modal ? '' : 'style="width:600px;"'}>
|
|
122
|
+
<c:grid-row gutter="2">
|
|
123
|
+
<c:grid-col col="12">
|
|
124
|
+
<c:form-textarea x-model="builder" readonly rows="4"/>
|
|
125
|
+
</c:grid-col>
|
|
126
|
+
${columns.join('\n')}
|
|
127
|
+
</c:grid-row>
|
|
128
|
+
<c:div flex="justify-content:end" margin="top-3">
|
|
129
|
+
<c:btn color="secondary" t:content="close" dismiss="${container}" />
|
|
130
|
+
${applyBtn}
|
|
131
|
+
<c:btn color="primary" t:content="submitQuery" margin="start-2" @click="submit(true)" />
|
|
132
|
+
</c:div>
|
|
133
|
+
</c:${container}>
|
|
134
|
+
`
|
|
135
|
+
}
|
|
136
|
+
|
|
7
137
|
build = async () => {
|
|
8
138
|
const { req } = this.component
|
|
9
139
|
const { generateId } = this.app.lib.aneka
|
|
10
140
|
const { join } = this.app.bajo
|
|
11
|
-
const {
|
|
12
|
-
const { find, get, without, isEmpty, filter, upperFirst } = this.app.lib._
|
|
141
|
+
const { find, get, without, isEmpty, filter } = this.app.lib._
|
|
13
142
|
const qsKey = this.app.waibu.config.qsKey
|
|
14
143
|
const schema = get(this, 'component.locals.schema', {})
|
|
15
144
|
if (schema.view.disabled.includes('find')) {
|
|
@@ -62,144 +191,36 @@ async function query () {
|
|
|
62
191
|
this.params.noTag = true
|
|
63
192
|
const container = this.params.attr.modal ? 'modal' : 'drawer'
|
|
64
193
|
const scanables = (this.schema.scanables ?? []).map(item => req.t(`field.${item}`))
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
return ':' + prefix + '\\'' + v + '\\''
|
|
85
|
-
},
|
|
86
|
-
parse () {
|
|
87
|
-
this.builder = document.getElementById('${id}').value
|
|
88
|
-
if (!this.builder.includes(':')) this.builder = ''
|
|
89
|
-
if (_.isEmpty(this.builder)) return
|
|
90
|
-
const tokens = _.merge({}, this.ops, {
|
|
91
|
-
in: ':[',
|
|
92
|
-
between: ':{',
|
|
93
|
-
contains: ':~',
|
|
94
|
-
starts: ':~^',
|
|
95
|
-
ends: ':~$$',
|
|
96
|
-
'!in': ':-[',
|
|
97
|
-
'!between': ':-{',
|
|
98
|
-
'!contains': ':-~',
|
|
99
|
-
'!starts': ':-~^',
|
|
100
|
-
'!ends': ':-~$$'
|
|
101
|
-
})
|
|
102
|
-
for (const part of this.builder.split('+')) {
|
|
103
|
-
let [f, ...opv] = part.split(':')
|
|
104
|
-
opv = ':' + opv.join(':')
|
|
105
|
-
this.selected.push(f)
|
|
106
|
-
let op
|
|
107
|
-
let val
|
|
108
|
-
_.each(tokens, (v, k) => {
|
|
109
|
-
if (opv.slice(0, v.length) === v) {
|
|
110
|
-
op = k
|
|
111
|
-
val = opv.slice(v.length).replaceAll('[', '').replaceAll('{', '').replaceAll(']', '').replaceAll('}', '').replaceAll('\\'', '')
|
|
112
|
-
}
|
|
113
|
-
})
|
|
114
|
-
if (_.isEmpty(op)) continue
|
|
115
|
-
this[f + 'Op'] = op
|
|
116
|
-
if (op === 'between') {
|
|
117
|
-
const vals = val.split(',')
|
|
118
|
-
this[f + 'Val'] = vals[0]
|
|
119
|
-
this[f + 'Val2'] = vals[1]
|
|
120
|
-
} else {
|
|
121
|
-
this[f + 'Val'] = val
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
},
|
|
125
|
-
expandArray (val = '') {
|
|
126
|
-
return _.map(val.split(','), item => {
|
|
127
|
-
item = _.trim(item)
|
|
128
|
-
if (Number(item)) return item
|
|
129
|
-
return '\\'' + item + '\\''
|
|
130
|
-
})
|
|
131
|
-
},
|
|
132
|
-
rebuild () {
|
|
133
|
-
const items = []
|
|
134
|
-
for (const sel of this.selected) {
|
|
135
|
-
const key = this[sel + 'Op']
|
|
136
|
-
let val = this[sel + 'Val']
|
|
137
|
-
let val2 = this[sel + 'Val2']
|
|
138
|
-
if (_.isEmpty(val)) continue
|
|
139
|
-
if (key === 'between' && _.isEmpty(val2)) continue
|
|
140
|
-
let item
|
|
141
|
-
if (key === 'in') item = this.opsIn(val)
|
|
142
|
-
else if (key === '!in') item = this.opsIn(val, true)
|
|
143
|
-
else if (key === 'between') item = this.opsBetween(val, val2, false)
|
|
144
|
-
else if (key === '!between') item = this.opsBetween(val, val2, true)
|
|
145
|
-
else if (key === 'contains') item = this.opsExt(val)
|
|
146
|
-
else if (key === '!contains') item = this.opsExt(val, true)
|
|
147
|
-
else if (key === 'starts') item = this.opsExt(val, false, '^')
|
|
148
|
-
else if (key === '!starts') item = this.opsExt(val, true, '^')
|
|
149
|
-
else if (key === 'ends') item = this.opsExt(val, false, '$$')
|
|
150
|
-
else if (key === '!ends') item = this.opsExt(val, true, '$$')
|
|
151
|
-
else if (val.includes(' ')) item = this.ops[key] + '\\'' + val + '\\''
|
|
152
|
-
else item = this.ops[key] + val
|
|
153
|
-
items.push(sel + item)
|
|
154
|
-
}
|
|
155
|
-
this.builder = items.join('+')
|
|
156
|
-
},
|
|
157
|
-
submit (run) {
|
|
158
|
-
if (run) {
|
|
194
|
+
if (this.params.attr.mini) {
|
|
195
|
+
this.params.html = await this.component.buildSentence(`
|
|
196
|
+
${this.modal({ container, models, columns, fields, id })}
|
|
197
|
+
`)
|
|
198
|
+
} else {
|
|
199
|
+
let placeholder = this.params.attr.placeholder
|
|
200
|
+
if (!placeholder) placeholder = scanables.length > 0 ? req.t('queryHint%s', join(scanables, { separator: ', ', lastSeparator: 'or' })) : req.t('query')
|
|
201
|
+
this.params.html = await this.component.buildSentence(`
|
|
202
|
+
<c:form-input type="search" placeholder="${placeholder}" id="${id}" x-data="{ query: '' }" x-init="
|
|
203
|
+
const url = new URL(window.location.href)
|
|
204
|
+
query = url.searchParams.get('${qsKey.query}') ?? ''
|
|
205
|
+
" x-model="query" @on-query.window="query = $event.detail ?? ''" @keyup.enter="$dispatch('on-submit')">
|
|
206
|
+
<c:form-input-addon>
|
|
207
|
+
${this.modal({ container, models, columns, fields, id })}
|
|
208
|
+
</c:form-input-addon>
|
|
209
|
+
<c:form-input-addon>
|
|
210
|
+
<c:btn t:content="submit" x-data="{
|
|
211
|
+
submit () {
|
|
212
|
+
const val = document.getElementById('${id}').value ?? ''
|
|
159
213
|
const url = new URL(window.location.href)
|
|
160
214
|
const params = new URLSearchParams(url.search)
|
|
161
215
|
params.set('${qsKey.page}', 1)
|
|
162
|
-
params.set('${qsKey.query}',
|
|
216
|
+
params.set('${qsKey.query}', val)
|
|
163
217
|
window.location.href = '?' + params.toString()
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
const ops = _.map(fields, f => (f + 'Op'))
|
|
171
|
-
const vals = _.map(fields, f => (f + 'Val'))
|
|
172
|
-
const vals2 = _.map(fields, f => (f + 'Val2'))
|
|
173
|
-
const watcher = ['selected', ...ops, ...vals, ...vals2].join(',')
|
|
174
|
-
$watch(watcher, v => rebuild())
|
|
175
|
-
" ${this.params.attr.modal ? '' : 'style="width:600px;"'}>
|
|
176
|
-
<c:grid-row gutter="2">
|
|
177
|
-
<c:grid-col col="12">
|
|
178
|
-
<c:form-textarea x-model="builder" readonly rows="4"/>
|
|
179
|
-
</c:grid-col>
|
|
180
|
-
${columns.join('\n')}
|
|
181
|
-
</c:grid-row>
|
|
182
|
-
<c:div flex="justify-content:end" margin="top-3">
|
|
183
|
-
<c:btn color="secondary" t:content="close" dismiss="${container}" />
|
|
184
|
-
<c:btn color="primary" t:content="apply" margin="start-2" @click="submit()" />
|
|
185
|
-
<c:btn color="primary" t:content="submitQuery" margin="start-2" @click="submit(true)" />
|
|
186
|
-
</c:div>
|
|
187
|
-
</c:${container}>
|
|
188
|
-
</c:form-input-addon>
|
|
189
|
-
<c:form-input-addon>
|
|
190
|
-
<c:btn t:content="submit" x-data="{
|
|
191
|
-
submit () {
|
|
192
|
-
const val = document.getElementById('${id}').value ?? ''
|
|
193
|
-
const url = new URL(window.location.href)
|
|
194
|
-
const params = new URLSearchParams(url.search)
|
|
195
|
-
params.set('${qsKey.page}', 1)
|
|
196
|
-
params.set('${qsKey.query}', val)
|
|
197
|
-
window.location.href = '?' + params.toString()
|
|
198
|
-
}
|
|
199
|
-
}" @click="submit" @on-submit.window="submit()" />
|
|
200
|
-
</c:form-input-addon>
|
|
201
|
-
</c:form-input>
|
|
202
|
-
`)
|
|
218
|
+
}
|
|
219
|
+
}" @click="submit" @on-submit.window="submit()" />
|
|
220
|
+
</c:form-input-addon>
|
|
221
|
+
</c:form-input>
|
|
222
|
+
`)
|
|
223
|
+
}
|
|
203
224
|
}
|
|
204
225
|
}
|
|
205
226
|
}
|
|
@@ -23,9 +23,9 @@ async function recsInfo () {
|
|
|
23
23
|
this.params.html = ''
|
|
24
24
|
return
|
|
25
25
|
}
|
|
26
|
-
page =
|
|
27
|
-
limit =
|
|
28
|
-
pages =
|
|
26
|
+
page = get(this, 'component.locals.list.page', page ?? 1)
|
|
27
|
+
limit = get(this, 'component.locals.list.limit', limit ?? 25)
|
|
28
|
+
pages = get(this, 'component.locals.list.pages', pages ?? 0)
|
|
29
29
|
|
|
30
30
|
this.params.tag = 'div'
|
|
31
31
|
this.params.attr.flex = 'justify-center:start align-items:center'
|
package/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import countRecord from './lib/method/count-record.js'
|
|
2
|
-
import
|
|
3
|
-
import
|
|
2
|
+
import aggregate from './lib/method/aggregate.js'
|
|
3
|
+
import histogram from './lib/method/histogram.js'
|
|
4
4
|
import createRecord from './lib/method/create-record.js'
|
|
5
5
|
import findOneRecord from './lib/method/find-one-record.js'
|
|
6
6
|
import findRecord from './lib/method/find-record.js'
|
|
@@ -59,8 +59,8 @@ async function factory (pkgName) {
|
|
|
59
59
|
|
|
60
60
|
this.bindThis(
|
|
61
61
|
countRecord,
|
|
62
|
-
|
|
63
|
-
|
|
62
|
+
aggregate,
|
|
63
|
+
histogram,
|
|
64
64
|
createRecord,
|
|
65
65
|
findOneRecord,
|
|
66
66
|
findRecord,
|
|
@@ -36,7 +36,7 @@ async function exportHandler (opts = {}) {
|
|
|
36
36
|
const mdl = this.app.dobo.getModel(model)
|
|
37
37
|
const { schema } = await getSchemaExt(model, 'add', merge({ args: [{ req, model: mdl }] }, { params }, options))
|
|
38
38
|
if (schema.disabled.includes('find')) return await reply.view(templateDisabled, { action: 'list' })
|
|
39
|
-
const crud = await prepCrud.call(this.app.getPlugin('waibuDb'), { model, req, reply, args: ['model'] })
|
|
39
|
+
const crud = await prepCrud.call(this.app.getPlugin('waibuDb'), { model, req, reply, args: ['model'], options })
|
|
40
40
|
const data = {
|
|
41
41
|
name: model,
|
|
42
42
|
filter: crud.filter,
|
package/lib/crud/helper.js
CHANGED
|
@@ -22,7 +22,7 @@ export async function addOnsHandler (opts = {}) {
|
|
|
22
22
|
const { req, reply, schema } = opts
|
|
23
23
|
const { escape } = this.app.waibu
|
|
24
24
|
const { base64JsonEncode } = this.app.waibu
|
|
25
|
-
const {
|
|
25
|
+
const { aggregate } = this.app.waibuDb
|
|
26
26
|
const { get, map, pick, pullAt } = this.app.lib._
|
|
27
27
|
const _opts = map(get(schema, 'view.stat.aggregate', []), item => {
|
|
28
28
|
const dbOpts = pick(item, ['fields', 'group', 'aggregate'])
|
|
@@ -34,7 +34,7 @@ export async function addOnsHandler (opts = {}) {
|
|
|
34
34
|
for (const idx in _opts) {
|
|
35
35
|
const o = _opts[idx]
|
|
36
36
|
try {
|
|
37
|
-
const resp = await
|
|
37
|
+
const resp = await aggregate({ model: schema.name, req, reply, options: o.dbOpts })
|
|
38
38
|
const data = []
|
|
39
39
|
for (const d of resp.data) {
|
|
40
40
|
const key = o.dbOpts.fields[0]
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { prepCrud, processHandler } from '../util.js'
|
|
2
2
|
|
|
3
|
-
async function
|
|
4
|
-
const { model: mdl, opts, filter } = await prepCrud.call(this, { model, req, reply, options, args: ['model'], transaction })
|
|
3
|
+
async function aggregate ({ model, req, reply, options = {}, noAutoFilter, transaction } = {}) {
|
|
4
|
+
const { model: mdl, opts, filter } = await prepCrud.call(this, { model, req, reply, options, args: ['model'], noAutoFilter, transaction })
|
|
5
5
|
const params = {}
|
|
6
6
|
for (const item of ['group', 'field', 'aggregates']) {
|
|
7
7
|
params[item] = options[item] ?? req.params[item] ?? req.query[item]
|
|
@@ -10,10 +10,10 @@ async function createAggregate ({ model, req, reply, options = {}, transaction }
|
|
|
10
10
|
|
|
11
11
|
async function handler (trx) {
|
|
12
12
|
if (opts.trx === true) opts.trx = trx
|
|
13
|
-
return await mdl.
|
|
13
|
+
return await mdl.aggregate(filter, params, opts)
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
-
return await processHandler.call(this, { action: '
|
|
16
|
+
return await processHandler.call(this, { action: 'aggregate', model: mdl, handler, options: opts })
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
export default
|
|
19
|
+
export default aggregate
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { prepCrud, processHandler } from '../util.js'
|
|
2
2
|
|
|
3
|
-
async function countRecord ({ model, req, reply, options = {}, transaction } = {}) {
|
|
4
|
-
const { model: mdl, opts, filter } = await prepCrud.call(this, { model, req, reply, options, args: ['model'], transaction })
|
|
3
|
+
async function countRecord ({ model, req, reply, options = {}, noAutoFilter, transaction } = {}) {
|
|
4
|
+
const { model: mdl, opts, filter } = await prepCrud.call(this, { model, req, reply, options, args: ['model'], noAutoFilter, transaction })
|
|
5
5
|
|
|
6
6
|
async function handler (trx) {
|
|
7
7
|
if (opts.trx === true) opts.trx = trx
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { prepCrud, processHandler } from '../util.js'
|
|
2
2
|
|
|
3
|
-
async function findAllRecord ({ model, req, reply, options = {}, transaction } = {}) {
|
|
4
|
-
const { model: mdl, opts, filter, attachment, stats, mimeType } = await prepCrud.call(this, { model, req, reply, options, args: ['model'], transaction })
|
|
3
|
+
async function findAllRecord ({ model, req, reply, options = {}, noAutoFilter, transaction } = {}) {
|
|
4
|
+
const { model: mdl, opts, filter, attachment, stats, mimeType } = await prepCrud.call(this, { model, req, reply, options, args: ['model'], noAutoFilter, transaction })
|
|
5
5
|
|
|
6
6
|
async function handler (trx) {
|
|
7
7
|
if (opts.trx === true) opts.trx = trx
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { prepCrud, processHandler } from '../util.js'
|
|
2
2
|
|
|
3
|
-
async function findOneRecord ({ model, req, reply, options = {}, transaction } = {}) {
|
|
4
|
-
const { model: mdl, opts, filter, attachment, stats, mimeType } = await prepCrud.call(this, { model, req, reply, options, args: ['model'], transaction })
|
|
3
|
+
async function findOneRecord ({ model, req, reply, options = {}, noAutoFilter, transaction } = {}) {
|
|
4
|
+
const { model: mdl, opts, filter, attachment, stats, mimeType } = await prepCrud.call(this, { model, req, reply, options, args: ['model'], noAutoFilter, transaction })
|
|
5
5
|
|
|
6
6
|
async function handler (trx) {
|
|
7
7
|
if (opts.trx === true) opts.trx = trx
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { prepCrud, processHandler } from '../util.js'
|
|
2
2
|
|
|
3
|
-
async function findRecord ({ model, req, reply, options = {}, transaction } = {}) {
|
|
4
|
-
const { model: mdl, opts, filter, attachment, stats, mimeType } = await prepCrud.call(this, { model, req, reply, options, args: ['model'], transaction })
|
|
3
|
+
async function findRecord ({ model, req, reply, noAutoFilter, options = {}, transaction } = {}) {
|
|
4
|
+
const { model: mdl, opts, filter, attachment, stats, mimeType } = await prepCrud.call(this, { model, req, reply, options, args: ['model'], noAutoFilter, transaction })
|
|
5
5
|
|
|
6
6
|
async function handler (trx) {
|
|
7
7
|
if (opts.trx === true) opts.trx = trx
|
package/lib/method/get-record.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { prepCrud, getOneRecord, processHandler } from '../util.js'
|
|
2
2
|
|
|
3
|
-
async function getRecord ({ model, req, reply, id, options = {}, transaction } = {}) {
|
|
4
|
-
const { model: mdl, recId, filter, attachment, stats, mimeType, opts } = await prepCrud.call(this, { model, req, reply, id, options, args: ['model', 'id'], transaction })
|
|
3
|
+
async function getRecord ({ model, req, reply, id, options = {}, transaction, noAutoFilter } = {}) {
|
|
4
|
+
const { model: mdl, recId, filter, attachment, stats, mimeType, opts } = await prepCrud.call(this, { model, req, reply, id, options, args: ['model', 'id'], transaction, noAutoFilter })
|
|
5
5
|
const me = this
|
|
6
6
|
|
|
7
7
|
async function handler (trx) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { prepCrud, processHandler } from '../util.js'
|
|
2
2
|
|
|
3
|
-
async function
|
|
4
|
-
const { model: mdl, opts, filter } = await prepCrud.call(this, { model, req, reply, options, args: ['model'], transaction })
|
|
3
|
+
async function histogram ({ model, req, reply, options = {}, noAutoFilter, transaction } = {}) {
|
|
4
|
+
const { model: mdl, opts, filter } = await prepCrud.call(this, { model, req, reply, options, args: ['model'], noAutoFilter, transaction })
|
|
5
5
|
const params = {}
|
|
6
6
|
for (const item of ['type', 'group', 'field', 'aggregates']) {
|
|
7
7
|
params[item] = options[item] ?? req.params[item] ?? req.query[item]
|
|
@@ -10,10 +10,10 @@ async function createHistogram ({ model, req, reply, options = {}, transaction }
|
|
|
10
10
|
|
|
11
11
|
async function handler (trx) {
|
|
12
12
|
if (opts.trx === true) opts.trx = trx
|
|
13
|
-
return await mdl.
|
|
13
|
+
return await mdl.histogram(filter, params, opts)
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
-
return await processHandler.call(this, { action: '
|
|
16
|
+
return await processHandler.call(this, { action: 'histogram', model: mdl, handler, options: opts })
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
export default
|
|
19
|
+
export default histogram
|
package/lib/util.js
CHANGED
|
@@ -22,8 +22,8 @@
|
|
|
22
22
|
* @typedef TActions
|
|
23
23
|
* @type {Object}
|
|
24
24
|
* @property {number} 0='countRecord' - Count the number of records
|
|
25
|
-
* @property {number} 1='
|
|
26
|
-
* @property {number} 2='
|
|
25
|
+
* @property {number} 1='aggregate' - Create an aggregate record
|
|
26
|
+
* @property {number} 2='histogram' - Create a histogram record
|
|
27
27
|
* @property {number} 3='createRecord' - Create a new record
|
|
28
28
|
* @property {number} 4='findAllRecord' - Find all records
|
|
29
29
|
* @property {number} 5='findOneRecord' - Find one record
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
* @property {number} 8='removeRecord' - Remove a record
|
|
33
33
|
* @property {number} 9='updateRecord' - Update a record
|
|
34
34
|
*/
|
|
35
|
-
export const actions = ['countRecord', '
|
|
35
|
+
export const actions = ['countRecord', 'aggregate', 'histogram', 'createRecord', 'findAllRecord', 'findOneRecord', 'findRecord', 'getRecord', 'removeRecord', 'updateRecord']
|
|
36
36
|
|
|
37
37
|
/**
|
|
38
38
|
* Prepare CRUD operation parameters.
|
|
@@ -43,12 +43,13 @@ export const actions = ['countRecord', 'createAggregate', 'createHistogram', 'cr
|
|
|
43
43
|
* @param {Object} [options.req] - The request object
|
|
44
44
|
* @param {Object} [options.reply] - The reply object
|
|
45
45
|
* @param {Object} [options.transaction] - The transaction object
|
|
46
|
+
* @param {boolean} [options.noAutoFilter] - Disable automatic filter parsing from `req.query`
|
|
46
47
|
* @param {Object} [options.options] - Additional options
|
|
47
48
|
* @param {Array} [options.args] - Additional arguments
|
|
48
49
|
* @returns {Object} Prepared CRUD parameters
|
|
49
50
|
*/
|
|
50
51
|
export async function prepCrud (options = {}) {
|
|
51
|
-
let { model, id, req, reply, transaction, options: _options = {}, args } = options
|
|
52
|
+
let { model, id, req, reply, transaction, options: _options = {}, args, noAutoFilter } = options
|
|
52
53
|
const { isSet } = this.app.lib.aneka
|
|
53
54
|
const { importModule } = this.app.bajo
|
|
54
55
|
const { parseFilter } = this.app.waibu
|
|
@@ -93,9 +94,12 @@ export async function prepCrud (options = {}) {
|
|
|
93
94
|
}
|
|
94
95
|
opts.bboxLatField = req.query[cfgWeb.qsKey.bboxLatField]
|
|
95
96
|
opts.bboxLngField = req.query[cfgWeb.qsKey.bboxLngField]
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
97
|
+
let filter = { query: {}, search: {} }
|
|
98
|
+
if (!noAutoFilter) {
|
|
99
|
+
filter = parseFilter(req)
|
|
100
|
+
filter.query = buildFilterQuery.call(mdl, filter)
|
|
101
|
+
filter.search = buildFilterSearch.call(mdl, filter)
|
|
102
|
+
}
|
|
99
103
|
if (_options.query) filter.query = cloneDeep(_options.query)
|
|
100
104
|
if (_options.search) filter.search = cloneDeep(_options.search)
|
|
101
105
|
if (_options.limit) filter.limit = _options.limit
|
|
@@ -143,7 +147,7 @@ export async function processHandler (opts = {}) {
|
|
|
143
147
|
if (!(req.method === 'POST' && req.body._action === 'export' && options.allowExport)) return
|
|
144
148
|
const { importModule } = this.app.bajo
|
|
145
149
|
const exportHandler = await importModule('waibuDb:/lib/crud/export-handler.js')
|
|
146
|
-
await exportHandler.call(this, { model: model.name, req, reply })
|
|
150
|
+
await exportHandler.call(this, { model: model.name, req, reply, options })
|
|
147
151
|
}
|
|
148
152
|
|
|
149
153
|
function suppressedReturn (err) {
|
package/package.json
CHANGED
package/wiki/CHANGES.md
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# Changes
|
|
2
2
|
|
|
3
|
+
## 2026-08-19
|
|
4
|
+
|
|
5
|
+
- [2.33.1] Bug fix in `wdb-btn-export` widget
|
|
6
|
+
- [2.33.1] Bug fix in `export-handler.js`
|
|
7
|
+
- [2.33.1] Bug fix in `util.js`
|
|
8
|
+
|
|
9
|
+
## 2026-08-18
|
|
10
|
+
|
|
11
|
+
- [2.33.0] Update things to match `dobo@2.38.0`
|
|
12
|
+
|
|
3
13
|
## 2026-07-29
|
|
4
14
|
|
|
5
15
|
- [2.32.2] Bug fix in `export-handler.js`
|