@budibase/backend-core 2.9.19 → 2.9.21-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (250) hide show
  1. package/dist/index.js +266 -324
  2. package/dist/index.js.map +4 -4
  3. package/dist/index.js.meta.json +1 -1
  4. package/dist/package.json +19 -4
  5. package/dist/plugins.js +1 -1
  6. package/dist/plugins.js.map +1 -1
  7. package/dist/plugins.js.meta.json +1 -1
  8. package/dist/src/security/permissions.d.ts +1 -1
  9. package/dist/tests.js +222 -260
  10. package/dist/tests.js.map +4 -4
  11. package/dist/tests.js.meta.json +1 -1
  12. package/package.json +19 -4
  13. package/dist/tsconfig.build.tsbuildinfo +0 -1
  14. package/src/accounts/accounts.ts +0 -82
  15. package/src/accounts/api.ts +0 -59
  16. package/src/accounts/index.ts +0 -1
  17. package/src/auth/auth.ts +0 -208
  18. package/src/auth/index.ts +0 -1
  19. package/src/auth/tests/auth.spec.ts +0 -14
  20. package/src/blacklist/blacklist.ts +0 -54
  21. package/src/blacklist/index.ts +0 -1
  22. package/src/blacklist/tests/blacklist.spec.ts +0 -46
  23. package/src/cache/appMetadata.ts +0 -88
  24. package/src/cache/base/index.ts +0 -92
  25. package/src/cache/generic.ts +0 -30
  26. package/src/cache/index.ts +0 -5
  27. package/src/cache/tests/writethrough.spec.ts +0 -138
  28. package/src/cache/user.ts +0 -69
  29. package/src/cache/writethrough.ts +0 -133
  30. package/src/configs/configs.ts +0 -257
  31. package/src/configs/index.ts +0 -1
  32. package/src/configs/tests/configs.spec.ts +0 -184
  33. package/src/constants/db.ts +0 -63
  34. package/src/constants/index.ts +0 -2
  35. package/src/constants/misc.ts +0 -50
  36. package/src/context/Context.ts +0 -14
  37. package/src/context/identity.ts +0 -58
  38. package/src/context/index.ts +0 -3
  39. package/src/context/mainContext.ts +0 -310
  40. package/src/context/tests/index.spec.ts +0 -147
  41. package/src/context/types.ts +0 -11
  42. package/src/db/Replication.ts +0 -84
  43. package/src/db/constants.ts +0 -10
  44. package/src/db/couch/DatabaseImpl.ts +0 -238
  45. package/src/db/couch/connections.ts +0 -77
  46. package/src/db/couch/index.ts +0 -5
  47. package/src/db/couch/pouchDB.ts +0 -97
  48. package/src/db/couch/pouchDump.ts +0 -0
  49. package/src/db/couch/utils.ts +0 -50
  50. package/src/db/db.ts +0 -39
  51. package/src/db/errors.ts +0 -14
  52. package/src/db/index.ts +0 -12
  53. package/src/db/lucene.ts +0 -732
  54. package/src/db/searchIndexes/index.ts +0 -1
  55. package/src/db/searchIndexes/searchIndexes.ts +0 -62
  56. package/src/db/tests/index.spec.js +0 -25
  57. package/src/db/tests/lucene.spec.ts +0 -298
  58. package/src/db/tests/pouch.spec.js +0 -62
  59. package/src/db/tests/utils.spec.ts +0 -63
  60. package/src/db/utils.ts +0 -207
  61. package/src/db/views.ts +0 -241
  62. package/src/docIds/conversions.ts +0 -59
  63. package/src/docIds/ids.ts +0 -113
  64. package/src/docIds/index.ts +0 -2
  65. package/src/docIds/newid.ts +0 -5
  66. package/src/docIds/params.ts +0 -174
  67. package/src/docUpdates/index.ts +0 -29
  68. package/src/environment.ts +0 -201
  69. package/src/errors/errors.ts +0 -119
  70. package/src/errors/index.ts +0 -1
  71. package/src/events/analytics.ts +0 -6
  72. package/src/events/asyncEvents/index.ts +0 -2
  73. package/src/events/asyncEvents/publisher.ts +0 -12
  74. package/src/events/asyncEvents/queue.ts +0 -22
  75. package/src/events/backfill.ts +0 -183
  76. package/src/events/documentId.ts +0 -56
  77. package/src/events/events.ts +0 -40
  78. package/src/events/identification.ts +0 -310
  79. package/src/events/index.ts +0 -14
  80. package/src/events/processors/AnalyticsProcessor.ts +0 -64
  81. package/src/events/processors/AuditLogsProcessor.ts +0 -93
  82. package/src/events/processors/LoggingProcessor.ts +0 -37
  83. package/src/events/processors/Processors.ts +0 -52
  84. package/src/events/processors/async/DocumentUpdateProcessor.ts +0 -43
  85. package/src/events/processors/index.ts +0 -19
  86. package/src/events/processors/posthog/PosthogProcessor.ts +0 -118
  87. package/src/events/processors/posthog/index.ts +0 -2
  88. package/src/events/processors/posthog/rateLimiting.ts +0 -106
  89. package/src/events/processors/posthog/tests/PosthogProcessor.spec.ts +0 -168
  90. package/src/events/processors/types.ts +0 -1
  91. package/src/events/publishers/account.ts +0 -35
  92. package/src/events/publishers/app.ts +0 -155
  93. package/src/events/publishers/auditLog.ts +0 -26
  94. package/src/events/publishers/auth.ts +0 -73
  95. package/src/events/publishers/automation.ts +0 -110
  96. package/src/events/publishers/backfill.ts +0 -74
  97. package/src/events/publishers/backup.ts +0 -42
  98. package/src/events/publishers/datasource.ts +0 -48
  99. package/src/events/publishers/email.ts +0 -17
  100. package/src/events/publishers/environmentVariable.ts +0 -38
  101. package/src/events/publishers/group.ts +0 -99
  102. package/src/events/publishers/index.ts +0 -24
  103. package/src/events/publishers/installation.ts +0 -38
  104. package/src/events/publishers/layout.ts +0 -26
  105. package/src/events/publishers/license.ts +0 -84
  106. package/src/events/publishers/org.ts +0 -37
  107. package/src/events/publishers/plugin.ts +0 -47
  108. package/src/events/publishers/query.ts +0 -88
  109. package/src/events/publishers/role.ts +0 -62
  110. package/src/events/publishers/rows.ts +0 -29
  111. package/src/events/publishers/screen.ts +0 -36
  112. package/src/events/publishers/serve.ts +0 -43
  113. package/src/events/publishers/table.ts +0 -70
  114. package/src/events/publishers/user.ts +0 -202
  115. package/src/events/publishers/view.ts +0 -107
  116. package/src/featureFlags/index.ts +0 -77
  117. package/src/featureFlags/tests/featureFlags.spec.ts +0 -85
  118. package/src/helpers.ts +0 -9
  119. package/src/index.ts +0 -53
  120. package/src/installation.ts +0 -107
  121. package/src/logging/alerts.ts +0 -26
  122. package/src/logging/correlation/correlation.ts +0 -13
  123. package/src/logging/correlation/index.ts +0 -1
  124. package/src/logging/correlation/middleware.ts +0 -17
  125. package/src/logging/index.ts +0 -4
  126. package/src/logging/pino/logger.ts +0 -232
  127. package/src/logging/pino/middleware.ts +0 -45
  128. package/src/logging/system.ts +0 -81
  129. package/src/logging/tests/system.spec.ts +0 -61
  130. package/src/middleware/adminOnly.ts +0 -9
  131. package/src/middleware/auditLog.ts +0 -6
  132. package/src/middleware/authenticated.ts +0 -193
  133. package/src/middleware/builderOnly.ts +0 -20
  134. package/src/middleware/builderOrAdmin.ts +0 -20
  135. package/src/middleware/csrf.ts +0 -81
  136. package/src/middleware/errorHandling.ts +0 -29
  137. package/src/middleware/index.ts +0 -21
  138. package/src/middleware/internalApi.ts +0 -23
  139. package/src/middleware/joi-validator.ts +0 -45
  140. package/src/middleware/matchers.ts +0 -47
  141. package/src/middleware/passport/datasource/google.ts +0 -95
  142. package/src/middleware/passport/local.ts +0 -54
  143. package/src/middleware/passport/sso/google.ts +0 -77
  144. package/src/middleware/passport/sso/oidc.ts +0 -154
  145. package/src/middleware/passport/sso/sso.ts +0 -165
  146. package/src/middleware/passport/sso/tests/google.spec.ts +0 -67
  147. package/src/middleware/passport/sso/tests/oidc.spec.ts +0 -152
  148. package/src/middleware/passport/sso/tests/sso.spec.ts +0 -197
  149. package/src/middleware/passport/utils.ts +0 -38
  150. package/src/middleware/querystringToBody.ts +0 -28
  151. package/src/middleware/tenancy.ts +0 -36
  152. package/src/middleware/tests/builder.spec.ts +0 -180
  153. package/src/middleware/tests/matchers.spec.ts +0 -134
  154. package/src/migrations/definitions.ts +0 -40
  155. package/src/migrations/index.ts +0 -2
  156. package/src/migrations/migrations.ts +0 -191
  157. package/src/migrations/tests/__snapshots__/migrations.spec.ts.snap +0 -11
  158. package/src/migrations/tests/migrations.spec.ts +0 -64
  159. package/src/objectStore/buckets/app.ts +0 -40
  160. package/src/objectStore/buckets/global.ts +0 -29
  161. package/src/objectStore/buckets/index.ts +0 -3
  162. package/src/objectStore/buckets/plugins.ts +0 -71
  163. package/src/objectStore/buckets/tests/app.spec.ts +0 -171
  164. package/src/objectStore/buckets/tests/global.spec.ts +0 -74
  165. package/src/objectStore/buckets/tests/plugins.spec.ts +0 -111
  166. package/src/objectStore/cloudfront.ts +0 -41
  167. package/src/objectStore/index.ts +0 -3
  168. package/src/objectStore/objectStore.ts +0 -440
  169. package/src/objectStore/utils.ts +0 -27
  170. package/src/platform/index.ts +0 -3
  171. package/src/platform/platformDb.ts +0 -6
  172. package/src/platform/tenants.ts +0 -101
  173. package/src/platform/tests/tenants.spec.ts +0 -26
  174. package/src/platform/users.ts +0 -90
  175. package/src/plugin/index.ts +0 -1
  176. package/src/plugin/tests/validation.spec.ts +0 -83
  177. package/src/plugin/utils.ts +0 -156
  178. package/src/queue/constants.ts +0 -6
  179. package/src/queue/inMemoryQueue.ts +0 -141
  180. package/src/queue/index.ts +0 -2
  181. package/src/queue/listeners.ts +0 -195
  182. package/src/queue/queue.ts +0 -54
  183. package/src/redis/index.ts +0 -6
  184. package/src/redis/init.ts +0 -86
  185. package/src/redis/redis.ts +0 -308
  186. package/src/redis/redlockImpl.ts +0 -139
  187. package/src/redis/utils.ts +0 -117
  188. package/src/security/encryption.ts +0 -179
  189. package/src/security/permissions.ts +0 -159
  190. package/src/security/roles.ts +0 -420
  191. package/src/security/sessions.ts +0 -120
  192. package/src/security/tests/encryption.spec.ts +0 -31
  193. package/src/security/tests/permissions.spec.ts +0 -145
  194. package/src/security/tests/sessions.spec.ts +0 -12
  195. package/src/tenancy/db.ts +0 -6
  196. package/src/tenancy/index.ts +0 -2
  197. package/src/tenancy/tenancy.ts +0 -140
  198. package/src/tenancy/tests/tenancy.spec.ts +0 -184
  199. package/src/timers/index.ts +0 -1
  200. package/src/timers/timers.ts +0 -22
  201. package/src/users/db.ts +0 -460
  202. package/src/users/events.ts +0 -176
  203. package/src/users/index.ts +0 -4
  204. package/src/users/lookup.ts +0 -102
  205. package/src/users/users.ts +0 -276
  206. package/src/users/utils.ts +0 -55
  207. package/src/utils/hashing.ts +0 -14
  208. package/src/utils/index.ts +0 -3
  209. package/src/utils/stringUtils.ts +0 -8
  210. package/src/utils/tests/utils.spec.ts +0 -191
  211. package/src/utils/utils.ts +0 -239
  212. package/tests/core/logging.ts +0 -34
  213. package/tests/core/utilities/index.ts +0 -6
  214. package/tests/core/utilities/jestUtils.ts +0 -30
  215. package/tests/core/utilities/mocks/alerts.ts +0 -3
  216. package/tests/core/utilities/mocks/date.ts +0 -2
  217. package/tests/core/utilities/mocks/events.ts +0 -131
  218. package/tests/core/utilities/mocks/fetch.ts +0 -17
  219. package/tests/core/utilities/mocks/index.ts +0 -10
  220. package/tests/core/utilities/mocks/licenses.ts +0 -107
  221. package/tests/core/utilities/mocks/posthog.ts +0 -7
  222. package/tests/core/utilities/structures/Chance.ts +0 -20
  223. package/tests/core/utilities/structures/accounts.ts +0 -115
  224. package/tests/core/utilities/structures/apps.ts +0 -21
  225. package/tests/core/utilities/structures/common.ts +0 -7
  226. package/tests/core/utilities/structures/db.ts +0 -12
  227. package/tests/core/utilities/structures/documents/index.ts +0 -1
  228. package/tests/core/utilities/structures/documents/platform/index.ts +0 -1
  229. package/tests/core/utilities/structures/documents/platform/installation.ts +0 -12
  230. package/tests/core/utilities/structures/generator.ts +0 -2
  231. package/tests/core/utilities/structures/index.ts +0 -15
  232. package/tests/core/utilities/structures/koa.ts +0 -16
  233. package/tests/core/utilities/structures/licenses.ts +0 -167
  234. package/tests/core/utilities/structures/plugins.ts +0 -19
  235. package/tests/core/utilities/structures/quotas.ts +0 -67
  236. package/tests/core/utilities/structures/scim.ts +0 -80
  237. package/tests/core/utilities/structures/shared.ts +0 -19
  238. package/tests/core/utilities/structures/sso.ts +0 -119
  239. package/tests/core/utilities/structures/tenants.ts +0 -5
  240. package/tests/core/utilities/structures/userGroups.ts +0 -10
  241. package/tests/core/utilities/structures/users.ts +0 -73
  242. package/tests/core/utilities/testContainerUtils.ts +0 -98
  243. package/tests/core/utilities/utils/index.ts +0 -1
  244. package/tests/core/utilities/utils/time.ts +0 -3
  245. package/tests/extra/DBTestConfiguration.ts +0 -36
  246. package/tests/extra/index.ts +0 -2
  247. package/tests/extra/testEnv.ts +0 -95
  248. package/tests/index.ts +0 -1
  249. package/tests/jestEnv.ts +0 -6
  250. 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
- }