@budibase/backend-core 2.22.17 → 2.23.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 (37) hide show
  1. package/dist/index.js +51 -15
  2. package/dist/index.js.map +2 -2
  3. package/dist/index.js.meta.json +1 -1
  4. package/dist/package.json +4 -4
  5. package/dist/plugins.js.map +1 -1
  6. package/dist/plugins.js.meta.json +1 -1
  7. package/dist/src/constants/db.d.ts +1 -0
  8. package/dist/src/constants/db.js +2 -1
  9. package/dist/src/constants/db.js.map +1 -1
  10. package/dist/src/db/couch/DatabaseImpl.d.ts +1 -0
  11. package/dist/src/db/couch/DatabaseImpl.js +17 -0
  12. package/dist/src/db/couch/DatabaseImpl.js.map +1 -1
  13. package/dist/src/db/couch/connections.d.ts +1 -0
  14. package/dist/src/db/couch/connections.js +1 -0
  15. package/dist/src/db/couch/connections.js.map +1 -1
  16. package/dist/src/db/couch/utils.js +8 -2
  17. package/dist/src/db/couch/utils.js.map +1 -1
  18. package/dist/src/db/instrumentation.d.ts +1 -0
  19. package/dist/src/db/instrumentation.js +6 -0
  20. package/dist/src/db/instrumentation.js.map +1 -1
  21. package/dist/src/db/lucene.d.ts +12 -71
  22. package/dist/src/db/lucene.js +10 -48
  23. package/dist/src/db/lucene.js.map +1 -1
  24. package/dist/src/index.d.ts +0 -1
  25. package/dist/src/index.js.map +1 -1
  26. package/dist/tests/core/utilities/testContainerUtils.js +5 -0
  27. package/dist/tests/core/utilities/testContainerUtils.js.map +1 -1
  28. package/package.json +4 -4
  29. package/src/constants/db.ts +1 -0
  30. package/src/db/couch/DatabaseImpl.ts +16 -0
  31. package/src/db/couch/connections.ts +1 -0
  32. package/src/db/couch/utils.ts +7 -2
  33. package/src/db/instrumentation.ts +7 -0
  34. package/src/db/lucene.ts +43 -78
  35. package/src/db/tests/lucene.spec.ts +47 -15
  36. package/src/index.ts +0 -1
  37. package/tests/core/utilities/testContainerUtils.ts +6 -0
package/src/db/lucene.ts CHANGED
@@ -1,28 +1,16 @@
1
1
  import fetch from "node-fetch"
2
2
  import { getCouchInfo } from "./couch"
3
- import { SearchFilters, Row, EmptyFilterOption } from "@budibase/types"
3
+ import {
4
+ SearchFilters,
5
+ Row,
6
+ EmptyFilterOption,
7
+ SearchResponse,
8
+ SearchParams,
9
+ WithRequired,
10
+ } from "@budibase/types"
4
11
 
5
12
  const QUERY_START_REGEX = /\d[0-9]*:/g
6
13
 
7
- interface SearchResponse<T> {
8
- rows: T[] | any[]
9
- bookmark?: string
10
- totalRows: number
11
- }
12
-
13
- export type SearchParams<T> = {
14
- tableId?: string
15
- sort?: string
16
- sortOrder?: string
17
- sortType?: string
18
- limit?: number
19
- bookmark?: string
20
- version?: string
21
- indexer?: () => Promise<any>
22
- disableEscaping?: boolean
23
- rows?: T | Row[]
24
- }
25
-
26
14
  export function removeKeyNumbering(key: any): string {
27
15
  if (typeof key === "string" && key.match(QUERY_START_REGEX) != null) {
28
16
  const parts = key.split(":")
@@ -44,7 +32,7 @@ export class QueryBuilder<T> {
44
32
  #query: SearchFilters
45
33
  #limit: number
46
34
  #sort?: string
47
- #bookmark?: string
35
+ #bookmark?: string | number
48
36
  #sortOrder: string
49
37
  #sortType: string
50
38
  #includeDocs: boolean
@@ -130,7 +118,7 @@ export class QueryBuilder<T> {
130
118
  return this
131
119
  }
132
120
 
133
- setBookmark(bookmark?: string) {
121
+ setBookmark(bookmark?: string | number) {
134
122
  if (bookmark != null) {
135
123
  this.#bookmark = bookmark
136
124
  }
@@ -226,14 +214,20 @@ export class QueryBuilder<T> {
226
214
  }
227
215
  }
228
216
 
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 = {}) {
217
+ preprocess(
218
+ value: any,
219
+ {
220
+ escape,
221
+ lowercase,
222
+ wrap,
223
+ type,
224
+ }: {
225
+ escape?: boolean
226
+ lowercase?: boolean
227
+ wrap?: boolean
228
+ type?: string
229
+ } = {}
230
+ ): string | any {
237
231
  const hasVersion = !!this.#version
238
232
  // Determine if type needs wrapped
239
233
  const originalType = typeof value
@@ -561,7 +555,7 @@ async function runQuery<T>(
561
555
  url: string,
562
556
  body: any,
563
557
  cookie: string
564
- ): Promise<SearchResponse<T>> {
558
+ ): Promise<WithRequired<SearchResponse<T>, "totalRows">> {
565
559
  const response = await fetch(url, {
566
560
  body: JSON.stringify(body),
567
561
  method: "POST",
@@ -575,7 +569,7 @@ async function runQuery<T>(
575
569
  }
576
570
  const json = await response.json()
577
571
 
578
- let output: SearchResponse<T> = {
572
+ let output: WithRequired<SearchResponse<T>, "totalRows"> = {
579
573
  rows: [],
580
574
  totalRows: 0,
581
575
  }
@@ -613,63 +607,51 @@ async function recursiveSearch<T>(
613
607
  dbName: string,
614
608
  index: string,
615
609
  query: any,
616
- params: any
610
+ params: SearchParams
617
611
  ): Promise<any> {
618
612
  const bookmark = params.bookmark
619
613
  const rows = params.rows || []
620
- if (rows.length >= params.limit) {
614
+ if (params.limit && rows.length >= params.limit) {
621
615
  return rows
622
616
  }
623
617
  let pageSize = QueryBuilder.maxLimit
624
- if (rows.length > params.limit - QueryBuilder.maxLimit) {
618
+ if (params.limit && rows.length > params.limit - QueryBuilder.maxLimit) {
625
619
  pageSize = params.limit - rows.length
626
620
  }
627
- const page = await new QueryBuilder<T>(dbName, index, query)
621
+ const queryBuilder = new QueryBuilder<T>(dbName, index, query)
622
+ queryBuilder
628
623
  .setVersion(params.version)
629
- .setTable(params.tableId)
630
624
  .setBookmark(bookmark)
631
625
  .setLimit(pageSize)
632
626
  .setSort(params.sort)
633
627
  .setSortOrder(params.sortOrder)
634
628
  .setSortType(params.sortType)
635
- .run()
629
+
630
+ if (params.tableId) {
631
+ queryBuilder.setTable(params.tableId)
632
+ }
633
+
634
+ const page = await queryBuilder.run()
636
635
  if (!page.rows.length) {
637
636
  return rows
638
637
  }
639
638
  if (page.rows.length < QueryBuilder.maxLimit) {
640
639
  return [...rows, ...page.rows]
641
640
  }
642
- const newParams = {
641
+ const newParams: SearchParams = {
643
642
  ...params,
644
643
  bookmark: page.bookmark,
645
- rows: [...rows, ...page.rows],
644
+ rows: [...rows, ...page.rows] as Row[],
646
645
  }
647
646
  return await recursiveSearch(dbName, index, query, newParams)
648
647
  }
649
648
 
650
- /**
651
- * Performs a paginated search. A bookmark will be returned to allow the next
652
- * page to be fetched. There is a max limit off 200 results per page in a
653
- * paginated search.
654
- * @param dbName Which database to run a lucene query on
655
- * @param index Which search index to utilise
656
- * @param query The JSON query structure
657
- * @param params The search params including:
658
- * tableId {string} The table ID to search
659
- * sort {string} The sort column
660
- * sortOrder {string} The sort order ("ascending" or "descending")
661
- * sortType {string} Whether to treat sortable values as strings or
662
- * numbers. ("string" or "number")
663
- * limit {number} The desired page size
664
- * bookmark {string} The bookmark to resume from
665
- * @returns {Promise<{hasNextPage: boolean, rows: *[]}>}
666
- */
667
649
  export async function paginatedSearch<T>(
668
650
  dbName: string,
669
651
  index: string,
670
652
  query: SearchFilters,
671
- params: SearchParams<T>
672
- ) {
653
+ params: SearchParams
654
+ ): Promise<SearchResponse<T>> {
673
655
  let limit = params.limit
674
656
  if (limit == null || isNaN(limit) || limit < 0) {
675
657
  limit = 50
@@ -713,29 +695,12 @@ export async function paginatedSearch<T>(
713
695
  }
714
696
  }
715
697
 
716
- /**
717
- * Performs a full search, fetching multiple pages if required to return the
718
- * desired amount of results. There is a limit of 1000 results to avoid
719
- * heavy performance hits, and to avoid client components breaking from
720
- * handling too much data.
721
- * @param dbName Which database to run a lucene query on
722
- * @param index Which search index to utilise
723
- * @param query The JSON query structure
724
- * @param params The search params including:
725
- * tableId {string} The table ID to search
726
- * sort {string} The sort column
727
- * sortOrder {string} The sort order ("ascending" or "descending")
728
- * sortType {string} Whether to treat sortable values as strings or
729
- * numbers. ("string" or "number")
730
- * limit {number} The desired number of results
731
- * @returns {Promise<{rows: *}>}
732
- */
733
698
  export async function fullSearch<T>(
734
699
  dbName: string,
735
700
  index: string,
736
701
  query: SearchFilters,
737
- params: SearchParams<T>
738
- ) {
702
+ params: SearchParams
703
+ ): Promise<{ rows: Row[] }> {
739
704
  let limit = params.limit
740
705
  if (limit == null || isNaN(limit) || limit < 0) {
741
706
  limit = 1000
@@ -1,23 +1,39 @@
1
1
  import { newid } from "../../docIds/newid"
2
2
  import { getDB } from "../db"
3
- import { Database, EmptyFilterOption } from "@budibase/types"
4
- import { QueryBuilder, paginatedSearch, fullSearch } from "../lucene"
3
+ import {
4
+ Database,
5
+ EmptyFilterOption,
6
+ SortOrder,
7
+ SortType,
8
+ DocumentType,
9
+ SEPARATOR,
10
+ } from "@budibase/types"
11
+ import { fullSearch, paginatedSearch, QueryBuilder } from "../lucene"
5
12
 
6
13
  const INDEX_NAME = "main"
14
+ const TABLE_ID = DocumentType.TABLE + SEPARATOR + newid()
7
15
 
8
16
  const index = `function(doc) {
9
- let props = ["property", "number", "array"]
10
- for (let key of props) {
11
- if (Array.isArray(doc[key])) {
12
- for (let val of doc[key]) {
17
+ if (!doc._id.startsWith("ro_")) {
18
+ return
19
+ }
20
+ let keys = Object.keys(doc).filter(key => !key.startsWith("_"))
21
+ for (let key of keys) {
22
+ const value = doc[key]
23
+ if (Array.isArray(value)) {
24
+ for (let val of value) {
13
25
  index(key, val)
14
26
  }
15
- } else if (doc[key]) {
16
- index(key, doc[key])
27
+ } else if (value) {
28
+ index(key, value)
17
29
  }
18
30
  }
19
31
  }`
20
32
 
33
+ function rowId(id?: string) {
34
+ return DocumentType.ROW + SEPARATOR + (id || newid())
35
+ }
36
+
21
37
  describe("lucene", () => {
22
38
  let db: Database, dbName: string
23
39
 
@@ -25,10 +41,21 @@ describe("lucene", () => {
25
41
  dbName = `db-${newid()}`
26
42
  // create the DB for testing
27
43
  db = getDB(dbName)
28
- await db.put({ _id: newid(), property: "word", array: ["1", "4"] })
29
- await db.put({ _id: newid(), property: "word2", array: ["3", "1"] })
30
44
  await db.put({
31
- _id: newid(),
45
+ _id: rowId(),
46
+ tableId: TABLE_ID,
47
+ property: "word",
48
+ array: ["1", "4"],
49
+ })
50
+ await db.put({
51
+ _id: rowId(),
52
+ tableId: TABLE_ID,
53
+ property: "word2",
54
+ array: ["3", "1"],
55
+ })
56
+ await db.put({
57
+ _id: rowId(),
58
+ tableId: TABLE_ID,
32
59
  property: "word3",
33
60
  number: 1,
34
61
  array: ["1", "2"],
@@ -240,7 +267,8 @@ describe("lucene", () => {
240
267
  docs = Array(QueryBuilder.maxLimit * 2.5)
241
268
  .fill(0)
242
269
  .map((_, i) => ({
243
- _id: i.toString().padStart(3, "0"),
270
+ _id: rowId(i.toString().padStart(3, "0")),
271
+ tableId: TABLE_ID,
244
272
  property: `value_${i.toString().padStart(3, "0")}`,
245
273
  array: [],
246
274
  }))
@@ -338,10 +366,11 @@ describe("lucene", () => {
338
366
  },
339
367
  },
340
368
  {
369
+ tableId: TABLE_ID,
341
370
  limit: 1,
342
371
  sort: "property",
343
- sortType: "string",
344
- sortOrder: "desc",
372
+ sortType: SortType.STRING,
373
+ sortOrder: SortOrder.DESCENDING,
345
374
  }
346
375
  )
347
376
  expect(page.rows.length).toBe(1)
@@ -360,7 +389,10 @@ describe("lucene", () => {
360
389
  property: "wo",
361
390
  },
362
391
  },
363
- {}
392
+ {
393
+ tableId: TABLE_ID,
394
+ query: {},
395
+ }
364
396
  )
365
397
  expect(page.rows.length).toBe(3)
366
398
  })
package/src/index.ts CHANGED
@@ -32,7 +32,6 @@ export { default as env } from "./environment"
32
32
  export * as blacklist from "./blacklist"
33
33
  export * as docUpdates from "./docUpdates"
34
34
  export * from "./utils/Duration"
35
- export { SearchParams } from "./db"
36
35
  export * as docIds from "./docIds"
37
36
  export * as security from "./security"
38
37
  // Add context to tenancy for backwards compatibility
@@ -77,9 +77,15 @@ export function setupEnv(...envs: any[]) {
77
77
  throw new Error("CouchDB port not found")
78
78
  }
79
79
 
80
+ const couchSqlPort = getExposedV4Port(couch, 4984)
81
+ if (!couchSqlPort) {
82
+ throw new Error("CouchDB SQL port not found")
83
+ }
84
+
80
85
  const configs = [
81
86
  { key: "COUCH_DB_PORT", value: `${couchPort}` },
82
87
  { key: "COUCH_DB_URL", value: `http://127.0.0.1:${couchPort}` },
88
+ { key: "COUCH_DB_SQL_URL", value: `http://127.0.0.1:${couchSqlPort}` },
83
89
  ]
84
90
 
85
91
  for (const config of configs.filter(x => !!x.value)) {