@budibase/backend-core 2.9.19 → 2.9.20

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.
Files changed (241) hide show
  1. package/dist/package.json +19 -4
  2. package/dist/src/security/permissions.d.ts +1 -1
  3. package/package.json +19 -4
  4. package/dist/tsconfig.build.tsbuildinfo +0 -1
  5. package/src/accounts/accounts.ts +0 -82
  6. package/src/accounts/api.ts +0 -59
  7. package/src/accounts/index.ts +0 -1
  8. package/src/auth/auth.ts +0 -208
  9. package/src/auth/index.ts +0 -1
  10. package/src/auth/tests/auth.spec.ts +0 -14
  11. package/src/blacklist/blacklist.ts +0 -54
  12. package/src/blacklist/index.ts +0 -1
  13. package/src/blacklist/tests/blacklist.spec.ts +0 -46
  14. package/src/cache/appMetadata.ts +0 -88
  15. package/src/cache/base/index.ts +0 -92
  16. package/src/cache/generic.ts +0 -30
  17. package/src/cache/index.ts +0 -5
  18. package/src/cache/tests/writethrough.spec.ts +0 -138
  19. package/src/cache/user.ts +0 -69
  20. package/src/cache/writethrough.ts +0 -133
  21. package/src/configs/configs.ts +0 -257
  22. package/src/configs/index.ts +0 -1
  23. package/src/configs/tests/configs.spec.ts +0 -184
  24. package/src/constants/db.ts +0 -63
  25. package/src/constants/index.ts +0 -2
  26. package/src/constants/misc.ts +0 -50
  27. package/src/context/Context.ts +0 -14
  28. package/src/context/identity.ts +0 -58
  29. package/src/context/index.ts +0 -3
  30. package/src/context/mainContext.ts +0 -310
  31. package/src/context/tests/index.spec.ts +0 -147
  32. package/src/context/types.ts +0 -11
  33. package/src/db/Replication.ts +0 -84
  34. package/src/db/constants.ts +0 -10
  35. package/src/db/couch/DatabaseImpl.ts +0 -238
  36. package/src/db/couch/connections.ts +0 -77
  37. package/src/db/couch/index.ts +0 -5
  38. package/src/db/couch/pouchDB.ts +0 -97
  39. package/src/db/couch/pouchDump.ts +0 -0
  40. package/src/db/couch/utils.ts +0 -50
  41. package/src/db/db.ts +0 -39
  42. package/src/db/errors.ts +0 -14
  43. package/src/db/index.ts +0 -12
  44. package/src/db/lucene.ts +0 -732
  45. package/src/db/searchIndexes/index.ts +0 -1
  46. package/src/db/searchIndexes/searchIndexes.ts +0 -62
  47. package/src/db/tests/index.spec.js +0 -25
  48. package/src/db/tests/lucene.spec.ts +0 -298
  49. package/src/db/tests/pouch.spec.js +0 -62
  50. package/src/db/tests/utils.spec.ts +0 -63
  51. package/src/db/utils.ts +0 -207
  52. package/src/db/views.ts +0 -241
  53. package/src/docIds/conversions.ts +0 -59
  54. package/src/docIds/ids.ts +0 -113
  55. package/src/docIds/index.ts +0 -2
  56. package/src/docIds/newid.ts +0 -5
  57. package/src/docIds/params.ts +0 -174
  58. package/src/docUpdates/index.ts +0 -29
  59. package/src/environment.ts +0 -201
  60. package/src/errors/errors.ts +0 -119
  61. package/src/errors/index.ts +0 -1
  62. package/src/events/analytics.ts +0 -6
  63. package/src/events/asyncEvents/index.ts +0 -2
  64. package/src/events/asyncEvents/publisher.ts +0 -12
  65. package/src/events/asyncEvents/queue.ts +0 -22
  66. package/src/events/backfill.ts +0 -183
  67. package/src/events/documentId.ts +0 -56
  68. package/src/events/events.ts +0 -40
  69. package/src/events/identification.ts +0 -310
  70. package/src/events/index.ts +0 -14
  71. package/src/events/processors/AnalyticsProcessor.ts +0 -64
  72. package/src/events/processors/AuditLogsProcessor.ts +0 -93
  73. package/src/events/processors/LoggingProcessor.ts +0 -37
  74. package/src/events/processors/Processors.ts +0 -52
  75. package/src/events/processors/async/DocumentUpdateProcessor.ts +0 -43
  76. package/src/events/processors/index.ts +0 -19
  77. package/src/events/processors/posthog/PosthogProcessor.ts +0 -118
  78. package/src/events/processors/posthog/index.ts +0 -2
  79. package/src/events/processors/posthog/rateLimiting.ts +0 -106
  80. package/src/events/processors/posthog/tests/PosthogProcessor.spec.ts +0 -168
  81. package/src/events/processors/types.ts +0 -1
  82. package/src/events/publishers/account.ts +0 -35
  83. package/src/events/publishers/app.ts +0 -155
  84. package/src/events/publishers/auditLog.ts +0 -26
  85. package/src/events/publishers/auth.ts +0 -73
  86. package/src/events/publishers/automation.ts +0 -110
  87. package/src/events/publishers/backfill.ts +0 -74
  88. package/src/events/publishers/backup.ts +0 -42
  89. package/src/events/publishers/datasource.ts +0 -48
  90. package/src/events/publishers/email.ts +0 -17
  91. package/src/events/publishers/environmentVariable.ts +0 -38
  92. package/src/events/publishers/group.ts +0 -99
  93. package/src/events/publishers/index.ts +0 -24
  94. package/src/events/publishers/installation.ts +0 -38
  95. package/src/events/publishers/layout.ts +0 -26
  96. package/src/events/publishers/license.ts +0 -84
  97. package/src/events/publishers/org.ts +0 -37
  98. package/src/events/publishers/plugin.ts +0 -47
  99. package/src/events/publishers/query.ts +0 -88
  100. package/src/events/publishers/role.ts +0 -62
  101. package/src/events/publishers/rows.ts +0 -29
  102. package/src/events/publishers/screen.ts +0 -36
  103. package/src/events/publishers/serve.ts +0 -43
  104. package/src/events/publishers/table.ts +0 -70
  105. package/src/events/publishers/user.ts +0 -202
  106. package/src/events/publishers/view.ts +0 -107
  107. package/src/featureFlags/index.ts +0 -77
  108. package/src/featureFlags/tests/featureFlags.spec.ts +0 -85
  109. package/src/helpers.ts +0 -9
  110. package/src/index.ts +0 -53
  111. package/src/installation.ts +0 -107
  112. package/src/logging/alerts.ts +0 -26
  113. package/src/logging/correlation/correlation.ts +0 -13
  114. package/src/logging/correlation/index.ts +0 -1
  115. package/src/logging/correlation/middleware.ts +0 -17
  116. package/src/logging/index.ts +0 -4
  117. package/src/logging/pino/logger.ts +0 -232
  118. package/src/logging/pino/middleware.ts +0 -45
  119. package/src/logging/system.ts +0 -81
  120. package/src/logging/tests/system.spec.ts +0 -61
  121. package/src/middleware/adminOnly.ts +0 -9
  122. package/src/middleware/auditLog.ts +0 -6
  123. package/src/middleware/authenticated.ts +0 -193
  124. package/src/middleware/builderOnly.ts +0 -20
  125. package/src/middleware/builderOrAdmin.ts +0 -20
  126. package/src/middleware/csrf.ts +0 -81
  127. package/src/middleware/errorHandling.ts +0 -29
  128. package/src/middleware/index.ts +0 -21
  129. package/src/middleware/internalApi.ts +0 -23
  130. package/src/middleware/joi-validator.ts +0 -45
  131. package/src/middleware/matchers.ts +0 -47
  132. package/src/middleware/passport/datasource/google.ts +0 -95
  133. package/src/middleware/passport/local.ts +0 -54
  134. package/src/middleware/passport/sso/google.ts +0 -77
  135. package/src/middleware/passport/sso/oidc.ts +0 -154
  136. package/src/middleware/passport/sso/sso.ts +0 -165
  137. package/src/middleware/passport/sso/tests/google.spec.ts +0 -67
  138. package/src/middleware/passport/sso/tests/oidc.spec.ts +0 -152
  139. package/src/middleware/passport/sso/tests/sso.spec.ts +0 -197
  140. package/src/middleware/passport/utils.ts +0 -38
  141. package/src/middleware/querystringToBody.ts +0 -28
  142. package/src/middleware/tenancy.ts +0 -36
  143. package/src/middleware/tests/builder.spec.ts +0 -180
  144. package/src/middleware/tests/matchers.spec.ts +0 -134
  145. package/src/migrations/definitions.ts +0 -40
  146. package/src/migrations/index.ts +0 -2
  147. package/src/migrations/migrations.ts +0 -191
  148. package/src/migrations/tests/__snapshots__/migrations.spec.ts.snap +0 -11
  149. package/src/migrations/tests/migrations.spec.ts +0 -64
  150. package/src/objectStore/buckets/app.ts +0 -40
  151. package/src/objectStore/buckets/global.ts +0 -29
  152. package/src/objectStore/buckets/index.ts +0 -3
  153. package/src/objectStore/buckets/plugins.ts +0 -71
  154. package/src/objectStore/buckets/tests/app.spec.ts +0 -171
  155. package/src/objectStore/buckets/tests/global.spec.ts +0 -74
  156. package/src/objectStore/buckets/tests/plugins.spec.ts +0 -111
  157. package/src/objectStore/cloudfront.ts +0 -41
  158. package/src/objectStore/index.ts +0 -3
  159. package/src/objectStore/objectStore.ts +0 -440
  160. package/src/objectStore/utils.ts +0 -27
  161. package/src/platform/index.ts +0 -3
  162. package/src/platform/platformDb.ts +0 -6
  163. package/src/platform/tenants.ts +0 -101
  164. package/src/platform/tests/tenants.spec.ts +0 -26
  165. package/src/platform/users.ts +0 -90
  166. package/src/plugin/index.ts +0 -1
  167. package/src/plugin/tests/validation.spec.ts +0 -83
  168. package/src/plugin/utils.ts +0 -156
  169. package/src/queue/constants.ts +0 -6
  170. package/src/queue/inMemoryQueue.ts +0 -141
  171. package/src/queue/index.ts +0 -2
  172. package/src/queue/listeners.ts +0 -195
  173. package/src/queue/queue.ts +0 -54
  174. package/src/redis/index.ts +0 -6
  175. package/src/redis/init.ts +0 -86
  176. package/src/redis/redis.ts +0 -308
  177. package/src/redis/redlockImpl.ts +0 -139
  178. package/src/redis/utils.ts +0 -117
  179. package/src/security/encryption.ts +0 -179
  180. package/src/security/permissions.ts +0 -159
  181. package/src/security/roles.ts +0 -420
  182. package/src/security/sessions.ts +0 -120
  183. package/src/security/tests/encryption.spec.ts +0 -31
  184. package/src/security/tests/permissions.spec.ts +0 -145
  185. package/src/security/tests/sessions.spec.ts +0 -12
  186. package/src/tenancy/db.ts +0 -6
  187. package/src/tenancy/index.ts +0 -2
  188. package/src/tenancy/tenancy.ts +0 -140
  189. package/src/tenancy/tests/tenancy.spec.ts +0 -184
  190. package/src/timers/index.ts +0 -1
  191. package/src/timers/timers.ts +0 -22
  192. package/src/users/db.ts +0 -460
  193. package/src/users/events.ts +0 -176
  194. package/src/users/index.ts +0 -4
  195. package/src/users/lookup.ts +0 -102
  196. package/src/users/users.ts +0 -276
  197. package/src/users/utils.ts +0 -55
  198. package/src/utils/hashing.ts +0 -14
  199. package/src/utils/index.ts +0 -3
  200. package/src/utils/stringUtils.ts +0 -8
  201. package/src/utils/tests/utils.spec.ts +0 -191
  202. package/src/utils/utils.ts +0 -239
  203. package/tests/core/logging.ts +0 -34
  204. package/tests/core/utilities/index.ts +0 -6
  205. package/tests/core/utilities/jestUtils.ts +0 -30
  206. package/tests/core/utilities/mocks/alerts.ts +0 -3
  207. package/tests/core/utilities/mocks/date.ts +0 -2
  208. package/tests/core/utilities/mocks/events.ts +0 -131
  209. package/tests/core/utilities/mocks/fetch.ts +0 -17
  210. package/tests/core/utilities/mocks/index.ts +0 -10
  211. package/tests/core/utilities/mocks/licenses.ts +0 -107
  212. package/tests/core/utilities/mocks/posthog.ts +0 -7
  213. package/tests/core/utilities/structures/Chance.ts +0 -20
  214. package/tests/core/utilities/structures/accounts.ts +0 -115
  215. package/tests/core/utilities/structures/apps.ts +0 -21
  216. package/tests/core/utilities/structures/common.ts +0 -7
  217. package/tests/core/utilities/structures/db.ts +0 -12
  218. package/tests/core/utilities/structures/documents/index.ts +0 -1
  219. package/tests/core/utilities/structures/documents/platform/index.ts +0 -1
  220. package/tests/core/utilities/structures/documents/platform/installation.ts +0 -12
  221. package/tests/core/utilities/structures/generator.ts +0 -2
  222. package/tests/core/utilities/structures/index.ts +0 -15
  223. package/tests/core/utilities/structures/koa.ts +0 -16
  224. package/tests/core/utilities/structures/licenses.ts +0 -167
  225. package/tests/core/utilities/structures/plugins.ts +0 -19
  226. package/tests/core/utilities/structures/quotas.ts +0 -67
  227. package/tests/core/utilities/structures/scim.ts +0 -80
  228. package/tests/core/utilities/structures/shared.ts +0 -19
  229. package/tests/core/utilities/structures/sso.ts +0 -119
  230. package/tests/core/utilities/structures/tenants.ts +0 -5
  231. package/tests/core/utilities/structures/userGroups.ts +0 -10
  232. package/tests/core/utilities/structures/users.ts +0 -73
  233. package/tests/core/utilities/testContainerUtils.ts +0 -98
  234. package/tests/core/utilities/utils/index.ts +0 -1
  235. package/tests/core/utilities/utils/time.ts +0 -3
  236. package/tests/extra/DBTestConfiguration.ts +0 -36
  237. package/tests/extra/index.ts +0 -2
  238. package/tests/extra/testEnv.ts +0 -95
  239. package/tests/index.ts +0 -1
  240. package/tests/jestEnv.ts +0 -6
  241. package/tests/jestSetup.ts +0 -28
package/src/db/lucene.ts DELETED
@@ -1,732 +0,0 @@
1
- import fetch from "node-fetch"
2
- import { getCouchInfo } from "./couch"
3
- import { SearchFilters, Row } from "@budibase/types"
4
- import { createUserIndex } from "./searchIndexes/searchIndexes"
5
-
6
- const QUERY_START_REGEX = /\d[0-9]*:/g
7
-
8
- interface SearchResponse<T> {
9
- rows: T[] | any[]
10
- bookmark?: string
11
- totalRows: number
12
- }
13
-
14
- interface PaginatedSearchResponse<T> extends SearchResponse<T> {
15
- hasNextPage: boolean
16
- }
17
-
18
- export type SearchParams<T> = {
19
- tableId?: string
20
- sort?: string
21
- sortOrder?: string
22
- sortType?: string
23
- limit?: number
24
- bookmark?: string
25
- version?: string
26
- indexer?: () => Promise<any>
27
- disableEscaping?: boolean
28
- rows?: T | Row[]
29
- }
30
-
31
- export function removeKeyNumbering(key: any): string {
32
- if (typeof key === "string" && key.match(QUERY_START_REGEX) != null) {
33
- const parts = key.split(":")
34
- // remove the number
35
- parts.shift()
36
- return parts.join(":")
37
- } else {
38
- return key
39
- }
40
- }
41
-
42
- /**
43
- * Class to build lucene query URLs.
44
- * Optionally takes a base lucene query object.
45
- */
46
- export class QueryBuilder<T> {
47
- #dbName: string
48
- #index: string
49
- #query: SearchFilters
50
- #limit: number
51
- #sort?: string
52
- #bookmark?: string
53
- #sortOrder: string
54
- #sortType: string
55
- #includeDocs: boolean
56
- #version?: string
57
- #indexBuilder?: () => Promise<any>
58
- #noEscaping = false
59
- #skip?: number
60
-
61
- static readonly maxLimit = 200
62
-
63
- constructor(dbName: string, index: string, base?: SearchFilters) {
64
- this.#dbName = dbName
65
- this.#index = index
66
- this.#query = {
67
- allOr: false,
68
- string: {},
69
- fuzzy: {},
70
- range: {},
71
- equal: {},
72
- notEqual: {},
73
- empty: {},
74
- notEmpty: {},
75
- oneOf: {},
76
- contains: {},
77
- notContains: {},
78
- containsAny: {},
79
- ...base,
80
- }
81
- this.#limit = 50
82
- this.#sortOrder = "ascending"
83
- this.#sortType = "string"
84
- this.#includeDocs = true
85
- }
86
-
87
- disableEscaping() {
88
- this.#noEscaping = true
89
- return this
90
- }
91
-
92
- setIndexBuilder(builderFn: () => Promise<any>) {
93
- this.#indexBuilder = builderFn
94
- return this
95
- }
96
-
97
- setVersion(version?: string) {
98
- if (version != null) {
99
- this.#version = version
100
- }
101
- return this
102
- }
103
-
104
- setTable(tableId: string) {
105
- this.#query.equal!.tableId = tableId
106
- return this
107
- }
108
-
109
- setLimit(limit?: number) {
110
- if (limit != null) {
111
- this.#limit = limit
112
- }
113
- return this
114
- }
115
-
116
- setSort(sort?: string) {
117
- if (sort != null) {
118
- this.#sort = sort
119
- }
120
- return this
121
- }
122
-
123
- setSortOrder(sortOrder?: string) {
124
- if (sortOrder != null) {
125
- this.#sortOrder = sortOrder
126
- }
127
- return this
128
- }
129
-
130
- setSortType(sortType?: string) {
131
- if (sortType != null) {
132
- this.#sortType = sortType
133
- }
134
- return this
135
- }
136
-
137
- setBookmark(bookmark?: string) {
138
- if (bookmark != null) {
139
- this.#bookmark = bookmark
140
- }
141
- return this
142
- }
143
-
144
- setSkip(skip: number | undefined) {
145
- this.#skip = skip
146
- return this
147
- }
148
-
149
- excludeDocs() {
150
- this.#includeDocs = false
151
- return this
152
- }
153
-
154
- includeDocs() {
155
- this.#includeDocs = true
156
- return this
157
- }
158
-
159
- addString(key: string, partial: string) {
160
- this.#query.string![key] = partial
161
- return this
162
- }
163
-
164
- addFuzzy(key: string, fuzzy: string) {
165
- this.#query.fuzzy![key] = fuzzy
166
- return this
167
- }
168
-
169
- addRange(key: string, low: string | number, high: string | number) {
170
- this.#query.range![key] = {
171
- low,
172
- high,
173
- }
174
- return this
175
- }
176
-
177
- addEqual(key: string, value: any) {
178
- this.#query.equal![key] = value
179
- return this
180
- }
181
-
182
- addNotEqual(key: string, value: any) {
183
- this.#query.notEqual![key] = value
184
- return this
185
- }
186
-
187
- addEmpty(key: string, value: any) {
188
- this.#query.empty![key] = value
189
- return this
190
- }
191
-
192
- addNotEmpty(key: string, value: any) {
193
- this.#query.notEmpty![key] = value
194
- return this
195
- }
196
-
197
- addOneOf(key: string, value: any) {
198
- this.#query.oneOf![key] = value
199
- return this
200
- }
201
-
202
- addContains(key: string, value: any) {
203
- this.#query.contains![key] = value
204
- return this
205
- }
206
-
207
- addNotContains(key: string, value: any) {
208
- this.#query.notContains![key] = value
209
- return this
210
- }
211
-
212
- addContainsAny(key: string, value: any) {
213
- this.#query.containsAny![key] = value
214
- return this
215
- }
216
-
217
- setAllOr() {
218
- this.#query.allOr = true
219
- }
220
-
221
- handleSpaces(input: string) {
222
- if (this.#noEscaping) {
223
- return input
224
- } else {
225
- return input.replace(/ /g, "_")
226
- }
227
- }
228
-
229
- /**
230
- * Preprocesses a value before going into a lucene search.
231
- * Transforms strings to lowercase and wraps strings and bools in quotes.
232
- * @param value The value to process
233
- * @param options The preprocess options
234
- * @returns {string|*}
235
- */
236
- preprocess(value: any, { escape, lowercase, wrap, type }: any = {}) {
237
- const hasVersion = !!this.#version
238
- // Determine if type needs wrapped
239
- const originalType = typeof value
240
- // Convert to lowercase
241
- if (value && lowercase) {
242
- value = value.toLowerCase ? value.toLowerCase() : value
243
- }
244
- // Escape characters
245
- if (!this.#noEscaping && escape && originalType === "string") {
246
- value = `${value}`.replace(/[ \/#+\-&|!(){}\]^"~*?:\\]/g, "\\$&")
247
- }
248
-
249
- // Wrap in quotes
250
- if (originalType === "string" && !isNaN(value) && !type) {
251
- value = `"${value}"`
252
- } else if (hasVersion && wrap) {
253
- value = originalType === "number" ? value : `"${value}"`
254
- }
255
- return value
256
- }
257
-
258
- isMultiCondition() {
259
- let count = 0
260
- for (let filters of Object.values(this.#query)) {
261
- // not contains is one massive filter in allOr mode
262
- if (typeof filters === "object") {
263
- count += Object.keys(filters).length
264
- }
265
- }
266
- return count > 1
267
- }
268
-
269
- compressFilters(filters: Record<string, string[]>) {
270
- const compressed: typeof filters = {}
271
- for (let key of Object.keys(filters)) {
272
- const finalKey = removeKeyNumbering(key)
273
- if (compressed[finalKey]) {
274
- compressed[finalKey] = compressed[finalKey].concat(filters[key])
275
- } else {
276
- compressed[finalKey] = filters[key]
277
- }
278
- }
279
- // add prefixes back
280
- const final: typeof filters = {}
281
- let count = 1
282
- for (let [key, value] of Object.entries(compressed)) {
283
- final[`${count++}:${key}`] = value
284
- }
285
- return final
286
- }
287
-
288
- buildSearchQuery() {
289
- const builder = this
290
- let allOr = this.#query && this.#query.allOr
291
- let query = allOr ? "" : "*:*"
292
- const allPreProcessingOpts = { escape: true, lowercase: true, wrap: true }
293
- let tableId
294
- if (this.#query.equal!.tableId) {
295
- tableId = this.#query.equal!.tableId
296
- delete this.#query.equal!.tableId
297
- }
298
-
299
- const equal = (key: string, value: any) => {
300
- // 0 evaluates to false, which means we would return all rows if we don't check it
301
- if (!value && value !== 0) {
302
- return null
303
- }
304
- return `${key}:${builder.preprocess(value, allPreProcessingOpts)}`
305
- }
306
-
307
- const contains = (key: string, value: any, mode = "AND") => {
308
- if (Array.isArray(value) && value.length === 0) {
309
- return null
310
- }
311
- if (!Array.isArray(value)) {
312
- return `${key}:${value}`
313
- }
314
- let statement = `${builder.preprocess(value[0], { escape: true })}`
315
- for (let i = 1; i < value.length; i++) {
316
- statement += ` ${mode} ${builder.preprocess(value[i], {
317
- escape: true,
318
- })}`
319
- }
320
- return `${key}:(${statement})`
321
- }
322
-
323
- const fuzzy = (key: string, value: any) => {
324
- if (!value) {
325
- return null
326
- }
327
- value = builder.preprocess(value, {
328
- escape: true,
329
- lowercase: true,
330
- type: "fuzzy",
331
- })
332
- return `${key}:/.*${value}.*/`
333
- }
334
-
335
- const notContains = (key: string, value: any) => {
336
- const allPrefix = allOr ? "*:* AND " : ""
337
- const mode = allOr ? "AND" : undefined
338
- return allPrefix + "NOT " + contains(key, value, mode)
339
- }
340
-
341
- const containsAny = (key: string, value: any) => {
342
- return contains(key, value, "OR")
343
- }
344
-
345
- const oneOf = (key: string, value: any) => {
346
- if (!value) {
347
- return `*:*`
348
- }
349
- if (!Array.isArray(value)) {
350
- if (typeof value === "string") {
351
- value = value.split(",")
352
- } else {
353
- return ""
354
- }
355
- }
356
- let orStatement = `${builder.preprocess(value[0], allPreProcessingOpts)}`
357
- for (let i = 1; i < value.length; i++) {
358
- orStatement += ` OR ${builder.preprocess(
359
- value[i],
360
- allPreProcessingOpts
361
- )}`
362
- }
363
- return `${key}:(${orStatement})`
364
- }
365
-
366
- function build(
367
- structure: any,
368
- queryFn: (key: string, value: any) => string | null,
369
- opts?: { returnBuilt?: boolean; mode?: string }
370
- ) {
371
- let built = ""
372
- for (let [key, value] of Object.entries(structure)) {
373
- // check for new format - remove numbering if needed
374
- key = removeKeyNumbering(key)
375
- key = builder.preprocess(builder.handleSpaces(key), {
376
- escape: true,
377
- })
378
- let expression = queryFn(key, value)
379
- if (expression == null) {
380
- continue
381
- }
382
- if (built.length > 0 || query.length > 0) {
383
- const mode = opts?.mode ? opts.mode : allOr ? "OR" : "AND"
384
- built += ` ${mode} `
385
- }
386
- built += expression
387
- }
388
- if (opts?.returnBuilt) {
389
- return built
390
- } else {
391
- query += built
392
- }
393
- }
394
-
395
- // Construct the actual lucene search query string from JSON structure
396
- if (this.#query.string) {
397
- build(this.#query.string, (key: string, value: any) => {
398
- if (!value) {
399
- return null
400
- }
401
- value = builder.preprocess(value, {
402
- escape: true,
403
- lowercase: true,
404
- type: "string",
405
- })
406
- return `${key}:${value}*`
407
- })
408
- }
409
- if (this.#query.range) {
410
- build(this.#query.range, (key: string, value: any) => {
411
- if (!value) {
412
- return null
413
- }
414
- if (value.low == null || value.low === "") {
415
- return null
416
- }
417
- if (value.high == null || value.high === "") {
418
- return null
419
- }
420
- const low = builder.preprocess(value.low, allPreProcessingOpts)
421
- const high = builder.preprocess(value.high, allPreProcessingOpts)
422
- return `${key}:[${low} TO ${high}]`
423
- })
424
- }
425
- if (this.#query.fuzzy) {
426
- build(this.#query.fuzzy, fuzzy)
427
- }
428
- if (this.#query.equal) {
429
- build(this.#query.equal, equal)
430
- }
431
- if (this.#query.notEqual) {
432
- build(this.#query.notEqual, (key: string, value: any) => {
433
- if (!value) {
434
- return null
435
- }
436
- if (typeof value === "boolean") {
437
- return `(*:* AND !${key}:${value})`
438
- }
439
- return `!${key}:${builder.preprocess(value, allPreProcessingOpts)}`
440
- })
441
- }
442
- if (this.#query.empty) {
443
- build(this.#query.empty, (key: string) => `(*:* -${key}:["" TO *])`)
444
- }
445
- if (this.#query.notEmpty) {
446
- build(this.#query.notEmpty, (key: string) => `${key}:["" TO *]`)
447
- }
448
- if (this.#query.oneOf) {
449
- build(this.#query.oneOf, oneOf)
450
- }
451
- if (this.#query.contains) {
452
- build(this.#query.contains, contains)
453
- }
454
- if (this.#query.notContains) {
455
- build(this.compressFilters(this.#query.notContains), notContains)
456
- }
457
- if (this.#query.containsAny) {
458
- build(this.#query.containsAny, containsAny)
459
- }
460
- // make sure table ID is always added as an AND
461
- if (tableId) {
462
- query = this.isMultiCondition() ? `(${query})` : query
463
- allOr = false
464
- build({ tableId }, equal)
465
- }
466
- return query
467
- }
468
-
469
- buildSearchBody() {
470
- let body: any = {
471
- q: this.buildSearchQuery(),
472
- limit: Math.min(this.#limit, QueryBuilder.maxLimit),
473
- include_docs: this.#includeDocs,
474
- }
475
- if (this.#bookmark) {
476
- body.bookmark = this.#bookmark
477
- }
478
- if (this.#sort) {
479
- const order = this.#sortOrder === "descending" ? "-" : ""
480
- const type = `<${this.#sortType}>`
481
- body.sort = `${order}${this.handleSpaces(this.#sort)}${type}`
482
- }
483
- return body
484
- }
485
-
486
- async run() {
487
- if (this.#skip) {
488
- await this.#skipItems(this.#skip)
489
- }
490
- return await this.#execute()
491
- }
492
-
493
- /**
494
- * Lucene queries do not support pagination and use bookmarks instead.
495
- * For the given builder, walk through pages using bookmarks until the desired
496
- * page has been met.
497
- */
498
- async #skipItems(skip: number) {
499
- // Lucene does not support pagination.
500
- // Handle pagination by finding the right bookmark
501
- const prevIncludeDocs = this.#includeDocs
502
- const prevLimit = this.#limit
503
-
504
- this.excludeDocs()
505
- let skipRemaining = skip
506
- let iterationFetched = 0
507
- do {
508
- const toSkip = Math.min(QueryBuilder.maxLimit, skipRemaining)
509
- this.setLimit(toSkip)
510
- const { bookmark, rows } = await this.#execute()
511
- this.setBookmark(bookmark)
512
- iterationFetched = rows.length
513
- skipRemaining -= rows.length
514
- } while (skipRemaining > 0 && iterationFetched > 0)
515
-
516
- this.#includeDocs = prevIncludeDocs
517
- this.#limit = prevLimit
518
- }
519
-
520
- async #execute() {
521
- const { url, cookie } = getCouchInfo()
522
- const fullPath = `${url}/${this.#dbName}/_design/database/_search/${
523
- this.#index
524
- }`
525
- const body = this.buildSearchBody()
526
- try {
527
- return await runQuery<T>(fullPath, body, cookie)
528
- } catch (err: any) {
529
- if (err.status === 404 && this.#indexBuilder) {
530
- await this.#indexBuilder()
531
- return await runQuery<T>(fullPath, body, cookie)
532
- } else {
533
- throw err
534
- }
535
- }
536
- }
537
- }
538
-
539
- /**
540
- * Executes a lucene search query.
541
- * @param url The query URL
542
- * @param body The request body defining search criteria
543
- * @param cookie The auth cookie for CouchDB
544
- * @returns {Promise<{rows: []}>}
545
- */
546
- async function runQuery<T>(
547
- url: string,
548
- body: any,
549
- cookie: string
550
- ): Promise<SearchResponse<T>> {
551
- const response = await fetch(url, {
552
- body: JSON.stringify(body),
553
- method: "POST",
554
- headers: {
555
- Authorization: cookie,
556
- },
557
- })
558
-
559
- if (response.status === 404) {
560
- throw response
561
- }
562
- const json = await response.json()
563
-
564
- let output: SearchResponse<T> = {
565
- rows: [],
566
- totalRows: 0,
567
- }
568
- if (json.rows != null && json.rows.length > 0) {
569
- output.rows = json.rows.map((row: any) => row.doc)
570
- }
571
- if (json.bookmark) {
572
- output.bookmark = json.bookmark
573
- }
574
- if (json.total_rows) {
575
- output.totalRows = json.total_rows
576
- }
577
- return output
578
- }
579
-
580
- /**
581
- * Gets round the fixed limit of 200 results from a query by fetching as many
582
- * pages as required and concatenating the results. This recursively operates
583
- * until enough results have been found.
584
- * @param dbName {string} Which database to run a lucene query on
585
- * @param index {string} Which search index to utilise
586
- * @param query {object} The JSON query structure
587
- * @param params {object} The search params including:
588
- * tableId {string} The table ID to search
589
- * sort {string} The sort column
590
- * sortOrder {string} The sort order ("ascending" or "descending")
591
- * sortType {string} Whether to treat sortable values as strings or
592
- * numbers. ("string" or "number")
593
- * limit {number} The number of results to fetch
594
- * bookmark {string|null} Current bookmark in the recursive search
595
- * rows {array|null} Current results in the recursive search
596
- * @returns {Promise<*[]|*>}
597
- */
598
- async function recursiveSearch<T>(
599
- dbName: string,
600
- index: string,
601
- query: any,
602
- params: any
603
- ): Promise<any> {
604
- const bookmark = params.bookmark
605
- const rows = params.rows || []
606
- if (rows.length >= params.limit) {
607
- return rows
608
- }
609
- let pageSize = QueryBuilder.maxLimit
610
- if (rows.length > params.limit - QueryBuilder.maxLimit) {
611
- pageSize = params.limit - rows.length
612
- }
613
- const page = await new QueryBuilder<T>(dbName, index, query)
614
- .setVersion(params.version)
615
- .setTable(params.tableId)
616
- .setBookmark(bookmark)
617
- .setLimit(pageSize)
618
- .setSort(params.sort)
619
- .setSortOrder(params.sortOrder)
620
- .setSortType(params.sortType)
621
- .run()
622
- if (!page.rows.length) {
623
- return rows
624
- }
625
- if (page.rows.length < QueryBuilder.maxLimit) {
626
- return [...rows, ...page.rows]
627
- }
628
- const newParams = {
629
- ...params,
630
- bookmark: page.bookmark,
631
- rows: [...rows, ...page.rows],
632
- }
633
- return await recursiveSearch(dbName, index, query, newParams)
634
- }
635
-
636
- /**
637
- * Performs a paginated search. A bookmark will be returned to allow the next
638
- * page to be fetched. There is a max limit off 200 results per page in a
639
- * paginated search.
640
- * @param dbName {string} Which database to run a lucene query on
641
- * @param index {string} Which search index to utilise
642
- * @param query {object} The JSON query structure
643
- * @param params {object} The search params including:
644
- * tableId {string} The table ID to search
645
- * sort {string} The sort column
646
- * sortOrder {string} The sort order ("ascending" or "descending")
647
- * sortType {string} Whether to treat sortable values as strings or
648
- * numbers. ("string" or "number")
649
- * limit {number} The desired page size
650
- * bookmark {string} The bookmark to resume from
651
- * @returns {Promise<{hasNextPage: boolean, rows: *[]}>}
652
- */
653
- export async function paginatedSearch<T>(
654
- dbName: string,
655
- index: string,
656
- query: SearchFilters,
657
- params: SearchParams<T>
658
- ) {
659
- let limit = params.limit
660
- if (limit == null || isNaN(limit) || limit < 0) {
661
- limit = 50
662
- }
663
- limit = Math.min(limit, QueryBuilder.maxLimit)
664
- const search = new QueryBuilder<T>(dbName, index, query)
665
- if (params.version) {
666
- search.setVersion(params.version)
667
- }
668
- if (params.tableId) {
669
- search.setTable(params.tableId)
670
- }
671
- if (params.sort) {
672
- search
673
- .setSort(params.sort)
674
- .setSortOrder(params.sortOrder)
675
- .setSortType(params.sortType)
676
- }
677
- if (params.indexer) {
678
- search.setIndexBuilder(params.indexer)
679
- }
680
- if (params.disableEscaping) {
681
- search.disableEscaping()
682
- }
683
- const searchResults = await search
684
- .setBookmark(params.bookmark)
685
- .setLimit(limit)
686
- .run()
687
-
688
- // Try fetching 1 row in the next page to see if another page of results
689
- // exists or not
690
- search.setBookmark(searchResults.bookmark).setLimit(1)
691
- if (params.tableId) {
692
- search.setTable(params.tableId)
693
- }
694
- const nextResults = await search.run()
695
-
696
- return {
697
- ...searchResults,
698
- hasNextPage: nextResults.rows && nextResults.rows.length > 0,
699
- }
700
- }
701
-
702
- /**
703
- * Performs a full search, fetching multiple pages if required to return the
704
- * desired amount of results. There is a limit of 1000 results to avoid
705
- * heavy performance hits, and to avoid client components breaking from
706
- * handling too much data.
707
- * @param dbName {string} Which database to run a lucene query on
708
- * @param index {string} Which search index to utilise
709
- * @param query {object} The JSON query structure
710
- * @param params {object} The search params including:
711
- * tableId {string} The table ID to search
712
- * sort {string} The sort column
713
- * sortOrder {string} The sort order ("ascending" or "descending")
714
- * sortType {string} Whether to treat sortable values as strings or
715
- * numbers. ("string" or "number")
716
- * limit {number} The desired number of results
717
- * @returns {Promise<{rows: *}>}
718
- */
719
- export async function fullSearch<T>(
720
- dbName: string,
721
- index: string,
722
- query: SearchFilters,
723
- params: SearchParams<T>
724
- ) {
725
- let limit = params.limit
726
- if (limit == null || isNaN(limit) || limit < 0) {
727
- limit = 1000
728
- }
729
- params.limit = Math.min(limit, 1000)
730
- const rows = await recursiveSearch<T>(dbName, index, query, params)
731
- return { rows }
732
- }