@budibase/server 2.3.18-alpha.2 → 2.3.18-alpha.21
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/__mocks__/node-fetch.ts +3 -0
- package/builder/assets/blankScreenPreview.72634dd1.png +0 -0
- package/builder/assets/{index.bbe4c16b.js → index.baf0ac34.js} +430 -417
- package/builder/assets/index.dc0472d8.css +6 -0
- package/builder/assets/listScreenPreview.599c0aae.png +0 -0
- package/builder/index.html +2 -2
- package/dist/api/controllers/automation.js +11 -2
- package/dist/api/controllers/cloud.js +2 -2
- package/dist/api/controllers/row/ExternalRequest.js +49 -24
- package/dist/api/controllers/row/external.js +1 -1
- package/dist/api/controllers/row/internalSearch.js +6 -450
- package/dist/api/controllers/row/utils.js +1 -3
- package/dist/api/routes/automation.js +1 -1
- package/dist/api/routes/public/applications.js +7 -7
- package/dist/api/routes/public/queries.js +2 -2
- package/dist/api/routes/public/rows.js +5 -5
- package/dist/api/routes/public/tables.js +5 -5
- package/dist/api/routes/public/users.js +5 -5
- package/dist/app.js +2 -0
- package/dist/db/index.js +25 -2
- package/dist/db/utils.js +2 -5
- package/dist/db/views/staticViews.js +2 -1
- package/dist/integrations/base/sql.js +4 -8
- package/dist/integrations/googlesheets.js +17 -20
- package/dist/middleware/authorized.js +5 -3
- package/dist/middleware/builder.js +6 -3
- package/dist/migrations/functions/backfill/global/configs.js +10 -4
- package/dist/migrations/tests/helpers.js +1 -1
- package/dist/migrations/tests/structures.js +1 -1
- package/dist/package.json +9 -8
- package/dist/startup.js +3 -0
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/jest.config.ts +1 -0
- package/package.json +10 -9
- package/scripts/test.sh +12 -0
- package/specs/{generate.js → generate.ts} +7 -9
- package/specs/openapi.json +24 -24
- package/specs/openapi.yaml +24 -24
- package/specs/{parameters.js → parameters.ts} +6 -6
- package/specs/resources/{application.js → application.ts} +4 -4
- package/specs/resources/{index.js → index.ts} +8 -8
- package/specs/resources/{misc.js → misc.ts} +3 -3
- package/specs/resources/{query.js → query.ts} +4 -4
- package/specs/resources/{row.js → row.ts} +3 -4
- package/specs/resources/{table.js → table.ts} +5 -5
- package/specs/resources/{user.js → user.ts} +3 -3
- package/specs/resources/utils/Resource.ts +39 -0
- package/specs/resources/utils/{index.js → index.ts} +1 -1
- package/specs/{security.js → security.ts} +1 -1
- package/src/api/controllers/automation.ts +13 -2
- package/src/api/controllers/cloud.ts +2 -2
- package/src/api/controllers/row/ExternalRequest.ts +95 -28
- package/src/api/controllers/row/external.ts +1 -1
- package/src/api/controllers/row/internalSearch.ts +11 -524
- package/src/api/controllers/row/utils.ts +1 -2
- package/src/api/routes/automation.ts +1 -1
- package/src/api/routes/public/applications.ts +7 -7
- package/src/api/routes/public/queries.ts +2 -2
- package/src/api/routes/public/rows.ts +5 -5
- package/src/api/routes/public/tables.ts +5 -5
- package/src/api/routes/public/tests/{compare.spec.js → compare.spec.ts} +44 -25
- package/src/api/routes/public/users.ts +5 -5
- package/src/api/routes/tests/{cloud.seq.spec.ts → cloud.spec.ts} +13 -20
- package/src/api/routes/tests/utilities/TestFunctions.ts +1 -2
- package/src/app.ts +2 -0
- package/src/db/index.ts +2 -2
- package/src/db/utils.ts +0 -4
- package/src/db/views/staticViews.ts +3 -3
- package/src/definitions/openapi.ts +449 -63
- package/src/integration-test/postgres.spec.ts +351 -81
- package/src/integrations/base/sql.ts +4 -8
- package/src/integrations/googlesheets.ts +21 -22
- package/src/integrations/tests/googlesheets.spec.ts +122 -0
- package/src/middleware/authorized.ts +6 -4
- package/src/middleware/builder.ts +8 -3
- package/src/migrations/functions/backfill/global/configs.ts +15 -9
- package/src/migrations/functions/tests/userEmailViewCasing.spec.js +3 -4
- package/src/migrations/tests/helpers.ts +2 -2
- package/src/migrations/tests/structures.ts +1 -0
- package/src/startup.ts +4 -1
- package/src/tests/jestEnv.ts +1 -0
- package/src/tests/utilities/TestConfiguration.ts +42 -30
- package/src/tests/utilities/structures.ts +0 -2
- package/builder/assets/index.7e76c039.css +0 -6
- package/dist/integrations/base/utils.js +0 -16
- package/specs/resources/utils/Resource.js +0 -26
- package/src/integrations/base/utils.ts +0 -12
|
@@ -1,531 +1,18 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import fetch from "node-fetch"
|
|
4
|
-
import { db as dbCore, context } from "@budibase/backend-core"
|
|
5
|
-
import { SearchFilters, Row } from "@budibase/types"
|
|
1
|
+
import { db as dbCore, context, SearchParams } from "@budibase/backend-core"
|
|
2
|
+
import { SearchFilters, Row, SearchIndex } from "@budibase/types"
|
|
6
3
|
|
|
7
|
-
type SearchParams = {
|
|
8
|
-
tableId: string
|
|
9
|
-
sort?: string
|
|
10
|
-
sortOrder?: string
|
|
11
|
-
sortType?: string
|
|
12
|
-
limit?: number
|
|
13
|
-
bookmark?: string
|
|
14
|
-
version?: string
|
|
15
|
-
rows?: Row[]
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Class to build lucene query URLs.
|
|
20
|
-
* Optionally takes a base lucene query object.
|
|
21
|
-
*/
|
|
22
|
-
export class QueryBuilder {
|
|
23
|
-
query: SearchFilters
|
|
24
|
-
limit: number
|
|
25
|
-
sort?: string
|
|
26
|
-
bookmark?: string
|
|
27
|
-
sortOrder: string
|
|
28
|
-
sortType: string
|
|
29
|
-
includeDocs: boolean
|
|
30
|
-
version?: string
|
|
31
|
-
|
|
32
|
-
constructor(base?: SearchFilters) {
|
|
33
|
-
this.query = {
|
|
34
|
-
allOr: false,
|
|
35
|
-
string: {},
|
|
36
|
-
fuzzy: {},
|
|
37
|
-
range: {},
|
|
38
|
-
equal: {},
|
|
39
|
-
notEqual: {},
|
|
40
|
-
empty: {},
|
|
41
|
-
notEmpty: {},
|
|
42
|
-
oneOf: {},
|
|
43
|
-
contains: {},
|
|
44
|
-
notContains: {},
|
|
45
|
-
containsAny: {},
|
|
46
|
-
...base,
|
|
47
|
-
}
|
|
48
|
-
this.limit = 50
|
|
49
|
-
this.sortOrder = "ascending"
|
|
50
|
-
this.sortType = "string"
|
|
51
|
-
this.includeDocs = true
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
setVersion(version?: string) {
|
|
55
|
-
if (version != null) {
|
|
56
|
-
this.version = version
|
|
57
|
-
}
|
|
58
|
-
return this
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
setTable(tableId: string) {
|
|
62
|
-
this.query.equal!.tableId = tableId
|
|
63
|
-
return this
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
setLimit(limit?: number) {
|
|
67
|
-
if (limit != null) {
|
|
68
|
-
this.limit = limit
|
|
69
|
-
}
|
|
70
|
-
return this
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
setSort(sort?: string) {
|
|
74
|
-
if (sort != null) {
|
|
75
|
-
this.sort = sort
|
|
76
|
-
}
|
|
77
|
-
return this
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
setSortOrder(sortOrder?: string) {
|
|
81
|
-
if (sortOrder != null) {
|
|
82
|
-
this.sortOrder = sortOrder
|
|
83
|
-
}
|
|
84
|
-
return this
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
setSortType(sortType?: string) {
|
|
88
|
-
if (sortType != null) {
|
|
89
|
-
this.sortType = sortType
|
|
90
|
-
}
|
|
91
|
-
return this
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
setBookmark(bookmark?: string) {
|
|
95
|
-
if (bookmark != null) {
|
|
96
|
-
this.bookmark = bookmark
|
|
97
|
-
}
|
|
98
|
-
return this
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
excludeDocs() {
|
|
102
|
-
this.includeDocs = false
|
|
103
|
-
return this
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
addString(key: string, partial: string) {
|
|
107
|
-
this.query.string![key] = partial
|
|
108
|
-
return this
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
addFuzzy(key: string, fuzzy: string) {
|
|
112
|
-
this.query.fuzzy![key] = fuzzy
|
|
113
|
-
return this
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
addRange(key: string, low: string | number, high: string | number) {
|
|
117
|
-
this.query.range![key] = {
|
|
118
|
-
low,
|
|
119
|
-
high,
|
|
120
|
-
}
|
|
121
|
-
return this
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
addEqual(key: string, value: any) {
|
|
125
|
-
this.query.equal![key] = value
|
|
126
|
-
return this
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
addNotEqual(key: string, value: any) {
|
|
130
|
-
this.query.notEqual![key] = value
|
|
131
|
-
return this
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
addEmpty(key: string, value: any) {
|
|
135
|
-
this.query.empty![key] = value
|
|
136
|
-
return this
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
addNotEmpty(key: string, value: any) {
|
|
140
|
-
this.query.notEmpty![key] = value
|
|
141
|
-
return this
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
addOneOf(key: string, value: any) {
|
|
145
|
-
this.query.oneOf![key] = value
|
|
146
|
-
return this
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
addContains(key: string, value: any) {
|
|
150
|
-
this.query.contains![key] = value
|
|
151
|
-
return this
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
addNotContains(key: string, value: any) {
|
|
155
|
-
this.query.notContains![key] = value
|
|
156
|
-
return this
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
addContainsAny(key: string, value: any) {
|
|
160
|
-
this.query.containsAny![key] = value
|
|
161
|
-
return this
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
/**
|
|
165
|
-
* Preprocesses a value before going into a lucene search.
|
|
166
|
-
* Transforms strings to lowercase and wraps strings and bools in quotes.
|
|
167
|
-
* @param value The value to process
|
|
168
|
-
* @param options The preprocess options
|
|
169
|
-
* @returns {string|*}
|
|
170
|
-
*/
|
|
171
|
-
preprocess(value: any, { escape, lowercase, wrap, type }: any = {}) {
|
|
172
|
-
const hasVersion = !!this.version
|
|
173
|
-
// Determine if type needs wrapped
|
|
174
|
-
const originalType = typeof value
|
|
175
|
-
// Convert to lowercase
|
|
176
|
-
if (value && lowercase) {
|
|
177
|
-
value = value.toLowerCase ? value.toLowerCase() : value
|
|
178
|
-
}
|
|
179
|
-
// Escape characters
|
|
180
|
-
if (escape && originalType === "string") {
|
|
181
|
-
value = `${value}`.replace(/[ #+\-&|!(){}\]^"~*?:\\]/g, "\\$&")
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
// Wrap in quotes
|
|
185
|
-
if (originalType === "string" && !isNaN(value) && !type) {
|
|
186
|
-
value = `"${value}"`
|
|
187
|
-
} else if (hasVersion && wrap) {
|
|
188
|
-
value = originalType === "number" ? value : `"${value}"`
|
|
189
|
-
}
|
|
190
|
-
return value
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
buildSearchQuery() {
|
|
194
|
-
const builder = this
|
|
195
|
-
let allOr = this.query && this.query.allOr
|
|
196
|
-
let query = allOr ? "" : "*:*"
|
|
197
|
-
const allPreProcessingOpts = { escape: true, lowercase: true, wrap: true }
|
|
198
|
-
let tableId
|
|
199
|
-
if (this.query.equal!.tableId) {
|
|
200
|
-
tableId = this.query.equal!.tableId
|
|
201
|
-
delete this.query.equal!.tableId
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
const equal = (key: string, value: any) => {
|
|
205
|
-
// 0 evaluates to false, which means we would return all rows if we don't check it
|
|
206
|
-
if (!value && value !== 0) {
|
|
207
|
-
return null
|
|
208
|
-
}
|
|
209
|
-
return `${key}:${builder.preprocess(value, allPreProcessingOpts)}`
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
const contains = (key: string, value: any, mode = "AND") => {
|
|
213
|
-
if (Array.isArray(value) && value.length === 0) {
|
|
214
|
-
return null
|
|
215
|
-
}
|
|
216
|
-
if (!Array.isArray(value)) {
|
|
217
|
-
return `${key}:${value}`
|
|
218
|
-
}
|
|
219
|
-
let statement = `${builder.preprocess(value[0], { escape: true })}`
|
|
220
|
-
for (let i = 1; i < value.length; i++) {
|
|
221
|
-
statement += ` ${mode} ${builder.preprocess(value[i], {
|
|
222
|
-
escape: true,
|
|
223
|
-
})}`
|
|
224
|
-
}
|
|
225
|
-
return `${key}:(${statement})`
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
const notContains = (key: string, value: any) => {
|
|
229
|
-
// @ts-ignore
|
|
230
|
-
const allPrefix = allOr === "" ? "*:* AND" : ""
|
|
231
|
-
return allPrefix + "NOT " + contains(key, value)
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
const containsAny = (key: string, value: any) => {
|
|
235
|
-
return contains(key, value, "OR")
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
const oneOf = (key: string, value: any) => {
|
|
239
|
-
if (!Array.isArray(value)) {
|
|
240
|
-
if (typeof value === "string") {
|
|
241
|
-
value = value.split(",")
|
|
242
|
-
} else {
|
|
243
|
-
return ""
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
let orStatement = `${builder.preprocess(value[0], allPreProcessingOpts)}`
|
|
247
|
-
for (let i = 1; i < value.length; i++) {
|
|
248
|
-
orStatement += ` OR ${builder.preprocess(
|
|
249
|
-
value[i],
|
|
250
|
-
allPreProcessingOpts
|
|
251
|
-
)}`
|
|
252
|
-
}
|
|
253
|
-
return `${key}:(${orStatement})`
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
function build(structure: any, queryFn: any) {
|
|
257
|
-
for (let [key, value] of Object.entries(structure)) {
|
|
258
|
-
// check for new format - remove numbering if needed
|
|
259
|
-
key = removeKeyNumbering(key)
|
|
260
|
-
key = builder.preprocess(key.replace(/ /g, "_"), {
|
|
261
|
-
escape: true,
|
|
262
|
-
})
|
|
263
|
-
const expression = queryFn(key, value)
|
|
264
|
-
if (expression == null) {
|
|
265
|
-
continue
|
|
266
|
-
}
|
|
267
|
-
if (query.length > 0) {
|
|
268
|
-
query += ` ${allOr ? "OR" : "AND"} `
|
|
269
|
-
}
|
|
270
|
-
query += expression
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
// Construct the actual lucene search query string from JSON structure
|
|
275
|
-
if (this.query.string) {
|
|
276
|
-
build(this.query.string, (key: string, value: any) => {
|
|
277
|
-
if (!value) {
|
|
278
|
-
return null
|
|
279
|
-
}
|
|
280
|
-
value = builder.preprocess(value, {
|
|
281
|
-
escape: true,
|
|
282
|
-
lowercase: true,
|
|
283
|
-
type: "string",
|
|
284
|
-
})
|
|
285
|
-
return `${key}:${value}*`
|
|
286
|
-
})
|
|
287
|
-
}
|
|
288
|
-
if (this.query.range) {
|
|
289
|
-
build(this.query.range, (key: string, value: any) => {
|
|
290
|
-
if (!value) {
|
|
291
|
-
return null
|
|
292
|
-
}
|
|
293
|
-
if (value.low == null || value.low === "") {
|
|
294
|
-
return null
|
|
295
|
-
}
|
|
296
|
-
if (value.high == null || value.high === "") {
|
|
297
|
-
return null
|
|
298
|
-
}
|
|
299
|
-
const low = builder.preprocess(value.low, allPreProcessingOpts)
|
|
300
|
-
const high = builder.preprocess(value.high, allPreProcessingOpts)
|
|
301
|
-
return `${key}:[${low} TO ${high}]`
|
|
302
|
-
})
|
|
303
|
-
}
|
|
304
|
-
if (this.query.fuzzy) {
|
|
305
|
-
build(this.query.fuzzy, (key: string, value: any) => {
|
|
306
|
-
if (!value) {
|
|
307
|
-
return null
|
|
308
|
-
}
|
|
309
|
-
value = builder.preprocess(value, {
|
|
310
|
-
escape: true,
|
|
311
|
-
lowercase: true,
|
|
312
|
-
type: "fuzzy",
|
|
313
|
-
})
|
|
314
|
-
return `${key}:${value}~`
|
|
315
|
-
})
|
|
316
|
-
}
|
|
317
|
-
if (this.query.equal) {
|
|
318
|
-
build(this.query.equal, equal)
|
|
319
|
-
}
|
|
320
|
-
if (this.query.notEqual) {
|
|
321
|
-
build(this.query.notEqual, (key: string, value: any) => {
|
|
322
|
-
if (!value) {
|
|
323
|
-
return null
|
|
324
|
-
}
|
|
325
|
-
return `!${key}:${builder.preprocess(value, allPreProcessingOpts)}`
|
|
326
|
-
})
|
|
327
|
-
}
|
|
328
|
-
if (this.query.empty) {
|
|
329
|
-
build(this.query.empty, (key: string) => `!${key}:["" TO *]`)
|
|
330
|
-
}
|
|
331
|
-
if (this.query.notEmpty) {
|
|
332
|
-
build(this.query.notEmpty, (key: string) => `${key}:["" TO *]`)
|
|
333
|
-
}
|
|
334
|
-
if (this.query.oneOf) {
|
|
335
|
-
build(this.query.oneOf, oneOf)
|
|
336
|
-
}
|
|
337
|
-
if (this.query.contains) {
|
|
338
|
-
build(this.query.contains, contains)
|
|
339
|
-
}
|
|
340
|
-
if (this.query.notContains) {
|
|
341
|
-
build(this.query.notContains, notContains)
|
|
342
|
-
}
|
|
343
|
-
if (this.query.containsAny) {
|
|
344
|
-
build(this.query.containsAny, containsAny)
|
|
345
|
-
}
|
|
346
|
-
// make sure table ID is always added as an AND
|
|
347
|
-
if (tableId) {
|
|
348
|
-
query = `(${query})`
|
|
349
|
-
allOr = false
|
|
350
|
-
build({ tableId }, equal)
|
|
351
|
-
}
|
|
352
|
-
return query
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
buildSearchBody() {
|
|
356
|
-
let body: any = {
|
|
357
|
-
q: this.buildSearchQuery(),
|
|
358
|
-
limit: Math.min(this.limit, 200),
|
|
359
|
-
include_docs: this.includeDocs,
|
|
360
|
-
}
|
|
361
|
-
if (this.bookmark) {
|
|
362
|
-
body.bookmark = this.bookmark
|
|
363
|
-
}
|
|
364
|
-
if (this.sort) {
|
|
365
|
-
const order = this.sortOrder === "descending" ? "-" : ""
|
|
366
|
-
const type = `<${this.sortType}>`
|
|
367
|
-
body.sort = `${order}${this.sort.replace(/ /g, "_")}${type}`
|
|
368
|
-
}
|
|
369
|
-
return body
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
async run() {
|
|
373
|
-
const appId = context.getAppId()
|
|
374
|
-
const { url, cookie } = dbCore.getCouchInfo()
|
|
375
|
-
const fullPath = `${url}/${appId}/_design/database/_search/${SearchIndexes.ROWS}`
|
|
376
|
-
const body = this.buildSearchBody()
|
|
377
|
-
return await runQuery(fullPath, body, cookie)
|
|
378
|
-
}
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
/**
|
|
382
|
-
* Executes a lucene search query.
|
|
383
|
-
* @param url The query URL
|
|
384
|
-
* @param body The request body defining search criteria
|
|
385
|
-
* @param cookie The auth cookie for CouchDB
|
|
386
|
-
* @returns {Promise<{rows: []}>}
|
|
387
|
-
*/
|
|
388
|
-
const runQuery = async (url: string, body: any, cookie: string) => {
|
|
389
|
-
const response = await fetch(url, {
|
|
390
|
-
body: JSON.stringify(body),
|
|
391
|
-
method: "POST",
|
|
392
|
-
headers: {
|
|
393
|
-
Authorization: cookie,
|
|
394
|
-
},
|
|
395
|
-
})
|
|
396
|
-
const json = await response.json()
|
|
397
|
-
|
|
398
|
-
let output: any = {
|
|
399
|
-
rows: [],
|
|
400
|
-
}
|
|
401
|
-
if (json.rows != null && json.rows.length > 0) {
|
|
402
|
-
output.rows = json.rows.map((row: any) => row.doc)
|
|
403
|
-
}
|
|
404
|
-
if (json.bookmark) {
|
|
405
|
-
output.bookmark = json.bookmark
|
|
406
|
-
}
|
|
407
|
-
return output
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
/**
|
|
411
|
-
* Gets round the fixed limit of 200 results from a query by fetching as many
|
|
412
|
-
* pages as required and concatenating the results. This recursively operates
|
|
413
|
-
* until enough results have been found.
|
|
414
|
-
* @param query {object} The JSON query structure
|
|
415
|
-
* @param params {object} The search params including:
|
|
416
|
-
* tableId {string} The table ID to search
|
|
417
|
-
* sort {string} The sort column
|
|
418
|
-
* sortOrder {string} The sort order ("ascending" or "descending")
|
|
419
|
-
* sortType {string} Whether to treat sortable values as strings or
|
|
420
|
-
* numbers. ("string" or "number")
|
|
421
|
-
* limit {number} The number of results to fetch
|
|
422
|
-
* bookmark {string|null} Current bookmark in the recursive search
|
|
423
|
-
* rows {array|null} Current results in the recursive search
|
|
424
|
-
* @returns {Promise<*[]|*>}
|
|
425
|
-
*/
|
|
426
|
-
async function recursiveSearch(query: any, params: any): Promise<any> {
|
|
427
|
-
const bookmark = params.bookmark
|
|
428
|
-
const rows = params.rows || []
|
|
429
|
-
if (rows.length >= params.limit) {
|
|
430
|
-
return rows
|
|
431
|
-
}
|
|
432
|
-
let pageSize = 200
|
|
433
|
-
if (rows.length > params.limit - 200) {
|
|
434
|
-
pageSize = params.limit - rows.length
|
|
435
|
-
}
|
|
436
|
-
const page = await new QueryBuilder(query)
|
|
437
|
-
.setVersion(params.version)
|
|
438
|
-
.setTable(params.tableId)
|
|
439
|
-
.setBookmark(bookmark)
|
|
440
|
-
.setLimit(pageSize)
|
|
441
|
-
.setSort(params.sort)
|
|
442
|
-
.setSortOrder(params.sortOrder)
|
|
443
|
-
.setSortType(params.sortType)
|
|
444
|
-
.run()
|
|
445
|
-
if (!page.rows.length) {
|
|
446
|
-
return rows
|
|
447
|
-
}
|
|
448
|
-
if (page.rows.length < 200) {
|
|
449
|
-
return [...rows, ...page.rows]
|
|
450
|
-
}
|
|
451
|
-
const newParams = {
|
|
452
|
-
...params,
|
|
453
|
-
bookmark: page.bookmark,
|
|
454
|
-
rows: [...rows, ...page.rows],
|
|
455
|
-
}
|
|
456
|
-
return await recursiveSearch(query, newParams)
|
|
457
|
-
}
|
|
458
|
-
|
|
459
|
-
/**
|
|
460
|
-
* Performs a paginated search. A bookmark will be returned to allow the next
|
|
461
|
-
* page to be fetched. There is a max limit off 200 results per page in a
|
|
462
|
-
* paginated search.
|
|
463
|
-
* @param query {object} The JSON query structure
|
|
464
|
-
* @param params {object} The search params including:
|
|
465
|
-
* tableId {string} The table ID to search
|
|
466
|
-
* sort {string} The sort column
|
|
467
|
-
* sortOrder {string} The sort order ("ascending" or "descending")
|
|
468
|
-
* sortType {string} Whether to treat sortable values as strings or
|
|
469
|
-
* numbers. ("string" or "number")
|
|
470
|
-
* limit {number} The desired page size
|
|
471
|
-
* bookmark {string} The bookmark to resume from
|
|
472
|
-
* @returns {Promise<{hasNextPage: boolean, rows: *[]}>}
|
|
473
|
-
*/
|
|
474
4
|
export async function paginatedSearch(
|
|
475
5
|
query: SearchFilters,
|
|
476
|
-
params: SearchParams
|
|
6
|
+
params: SearchParams<Row>
|
|
477
7
|
) {
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
limit = 50
|
|
481
|
-
}
|
|
482
|
-
limit = Math.min(limit, 200)
|
|
483
|
-
const search = new QueryBuilder(query)
|
|
484
|
-
.setVersion(params.version)
|
|
485
|
-
.setTable(params.tableId)
|
|
486
|
-
.setSort(params.sort)
|
|
487
|
-
.setSortOrder(params.sortOrder)
|
|
488
|
-
.setSortType(params.sortType)
|
|
489
|
-
const searchResults = await search
|
|
490
|
-
.setBookmark(params.bookmark)
|
|
491
|
-
.setLimit(limit)
|
|
492
|
-
.run()
|
|
493
|
-
|
|
494
|
-
// Try fetching 1 row in the next page to see if another page of results
|
|
495
|
-
// exists or not
|
|
496
|
-
const nextResults = await search
|
|
497
|
-
.setTable(params.tableId)
|
|
498
|
-
.setBookmark(searchResults.bookmark)
|
|
499
|
-
.setLimit(1)
|
|
500
|
-
.run()
|
|
501
|
-
|
|
502
|
-
return {
|
|
503
|
-
...searchResults,
|
|
504
|
-
hasNextPage: nextResults.rows && nextResults.rows.length > 0,
|
|
505
|
-
}
|
|
8
|
+
const appId = context.getAppId()
|
|
9
|
+
return dbCore.paginatedSearch(appId!, SearchIndex.ROWS, query, params)
|
|
506
10
|
}
|
|
507
11
|
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
* @param params {object} The search params including:
|
|
515
|
-
* tableId {string} The table ID to search
|
|
516
|
-
* sort {string} The sort column
|
|
517
|
-
* sortOrder {string} The sort order ("ascending" or "descending")
|
|
518
|
-
* sortType {string} Whether to treat sortable values as strings or
|
|
519
|
-
* numbers. ("string" or "number")
|
|
520
|
-
* limit {number} The desired number of results
|
|
521
|
-
* @returns {Promise<{rows: *}>}
|
|
522
|
-
*/
|
|
523
|
-
export async function fullSearch(query: SearchFilters, params: SearchParams) {
|
|
524
|
-
let limit = params.limit
|
|
525
|
-
if (limit == null || isNaN(limit) || limit < 0) {
|
|
526
|
-
limit = 1000
|
|
527
|
-
}
|
|
528
|
-
params.limit = Math.min(limit, 1000)
|
|
529
|
-
const rows = await recursiveSearch(query, params)
|
|
530
|
-
return { rows }
|
|
12
|
+
export async function fullSearch(
|
|
13
|
+
query: SearchFilters,
|
|
14
|
+
params: SearchParams<Row>
|
|
15
|
+
) {
|
|
16
|
+
const appId = context.getAppId()
|
|
17
|
+
return dbCore.fullSearch(appId!, SearchIndex.ROWS, query, params)
|
|
531
18
|
}
|
|
@@ -3,8 +3,7 @@ import * as userController from "../user"
|
|
|
3
3
|
import { FieldTypes } from "../../../constants"
|
|
4
4
|
import { context } from "@budibase/backend-core"
|
|
5
5
|
import { makeExternalQuery } from "../../../integrations/base/query"
|
|
6
|
-
import {
|
|
7
|
-
export { removeKeyNumbering } from "../../../integrations/base/utils"
|
|
6
|
+
import { Row, Table } from "@budibase/types"
|
|
8
7
|
const validateJs = require("validate.js")
|
|
9
8
|
const { cloneDeep } = require("lodash/fp")
|
|
10
9
|
import { Format } from "../view/exporters"
|
|
@@ -9,7 +9,7 @@ const read = [],
|
|
|
9
9
|
* @openapi
|
|
10
10
|
* /applications:
|
|
11
11
|
* post:
|
|
12
|
-
* operationId:
|
|
12
|
+
* operationId: appCreate
|
|
13
13
|
* summary: Create an application
|
|
14
14
|
* tags:
|
|
15
15
|
* - applications
|
|
@@ -42,7 +42,7 @@ write.push(
|
|
|
42
42
|
* @openapi
|
|
43
43
|
* /applications/{appId}:
|
|
44
44
|
* put:
|
|
45
|
-
* operationId:
|
|
45
|
+
* operationId: appUpdate
|
|
46
46
|
* summary: Update an application
|
|
47
47
|
* tags:
|
|
48
48
|
* - applications
|
|
@@ -75,7 +75,7 @@ write.push(
|
|
|
75
75
|
* @openapi
|
|
76
76
|
* /applications/{appId}:
|
|
77
77
|
* delete:
|
|
78
|
-
* operationId:
|
|
78
|
+
* operationId: appDestroy
|
|
79
79
|
* summary: Delete an application
|
|
80
80
|
* tags:
|
|
81
81
|
* - applications
|
|
@@ -98,7 +98,7 @@ write.push(new Endpoint("delete", "/applications/:appId", controller.destroy))
|
|
|
98
98
|
* @openapi
|
|
99
99
|
* /applications/{appId}/unpublish:
|
|
100
100
|
* post:
|
|
101
|
-
* operationId:
|
|
101
|
+
* operationId: appUnpublish
|
|
102
102
|
* summary: Unpublish an application
|
|
103
103
|
* tags:
|
|
104
104
|
* - applications
|
|
@@ -116,7 +116,7 @@ write.push(
|
|
|
116
116
|
* @openapi
|
|
117
117
|
* /applications/{appId}/publish:
|
|
118
118
|
* post:
|
|
119
|
-
* operationId:
|
|
119
|
+
* operationId: appPublish
|
|
120
120
|
* summary: Unpublish an application
|
|
121
121
|
* tags:
|
|
122
122
|
* - applications
|
|
@@ -141,7 +141,7 @@ write.push(
|
|
|
141
141
|
* @openapi
|
|
142
142
|
* /applications/{appId}:
|
|
143
143
|
* get:
|
|
144
|
-
* operationId:
|
|
144
|
+
* operationId: appGetById
|
|
145
145
|
* summary: Retrieve an application
|
|
146
146
|
* tags:
|
|
147
147
|
* - applications
|
|
@@ -164,7 +164,7 @@ read.push(new Endpoint("get", "/applications/:appId", controller.read))
|
|
|
164
164
|
* @openapi
|
|
165
165
|
* /applications/search:
|
|
166
166
|
* post:
|
|
167
|
-
* operationId:
|
|
167
|
+
* operationId: appSearch
|
|
168
168
|
* summary: Search for applications
|
|
169
169
|
* description: Based on application properties (currently only name) search for applications.
|
|
170
170
|
* tags:
|
|
@@ -9,7 +9,7 @@ const read = [],
|
|
|
9
9
|
* @openapi
|
|
10
10
|
* /queries/{queryId}:
|
|
11
11
|
* post:
|
|
12
|
-
* operationId:
|
|
12
|
+
* operationId: queryExecute
|
|
13
13
|
* summary: Execute a query
|
|
14
14
|
* description: Queries which have been created within a Budibase app can be executed using this,
|
|
15
15
|
* tags:
|
|
@@ -43,7 +43,7 @@ write.push(new Endpoint("post", "/queries/:queryId", controller.execute))
|
|
|
43
43
|
* @openapi
|
|
44
44
|
* /queries/search:
|
|
45
45
|
* post:
|
|
46
|
-
* operationId:
|
|
46
|
+
* operationId: querySearch
|
|
47
47
|
* summary: Search for queries
|
|
48
48
|
* description: Based on query properties (currently only name) search for queries.
|
|
49
49
|
* tags:
|
|
@@ -9,7 +9,7 @@ const read = [],
|
|
|
9
9
|
* @openapi
|
|
10
10
|
* /tables/{tableId}/rows:
|
|
11
11
|
* post:
|
|
12
|
-
* operationId:
|
|
12
|
+
* operationId: rowCreate
|
|
13
13
|
* summary: Create a row
|
|
14
14
|
* description: Creates a row within the specified table.
|
|
15
15
|
* tags:
|
|
@@ -44,7 +44,7 @@ write.push(new Endpoint("post", "/tables/:tableId/rows", controller.create))
|
|
|
44
44
|
* @openapi
|
|
45
45
|
* /tables/{tableId}/rows/{rowId}:
|
|
46
46
|
* put:
|
|
47
|
-
* operationId:
|
|
47
|
+
* operationId: rowUpdate
|
|
48
48
|
* summary: Update a row
|
|
49
49
|
* description: Updates a row within the specified table.
|
|
50
50
|
* tags:
|
|
@@ -81,7 +81,7 @@ write.push(
|
|
|
81
81
|
* @openapi
|
|
82
82
|
* /tables/{tableId}/rows/{rowId}:
|
|
83
83
|
* delete:
|
|
84
|
-
* operationId:
|
|
84
|
+
* operationId: rowDestroy
|
|
85
85
|
* summary: Delete a row
|
|
86
86
|
* description: Deletes a row within the specified table.
|
|
87
87
|
* tags:
|
|
@@ -109,7 +109,7 @@ write.push(
|
|
|
109
109
|
* @openapi
|
|
110
110
|
* /tables/{tableId}/rows/{rowId}:
|
|
111
111
|
* get:
|
|
112
|
-
* operationId:
|
|
112
|
+
* operationId: rowGetById
|
|
113
113
|
* summary: Retrieve a row
|
|
114
114
|
* description: This gets a single row, it will be enriched with the full related rows, rather than
|
|
115
115
|
* the squashed "primaryDisplay" format returned by the search endpoint.
|
|
@@ -136,7 +136,7 @@ read.push(new Endpoint("get", "/tables/:tableId/rows/:rowId", controller.read))
|
|
|
136
136
|
* @openapi
|
|
137
137
|
* /tables/{tableId}/rows/search:
|
|
138
138
|
* post:
|
|
139
|
-
* operationId:
|
|
139
|
+
* operationId: rowSearch
|
|
140
140
|
* summary: Search for rows
|
|
141
141
|
* tags:
|
|
142
142
|
* - rows
|