@budibase/server 2.3.18-alpha.13 → 2.3.18-alpha.15

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/jest.config.ts CHANGED
@@ -39,6 +39,7 @@ const config: Config.InitialOptions = {
39
39
  ],
40
40
  collectCoverageFrom: [
41
41
  "src/**/*.{js,ts}",
42
+ "../backend-core/src/**/*.{js,ts}",
42
43
  // The use of coverage with couchdb view functions breaks tests
43
44
  "!src/db/views/staticViews.*",
44
45
  ],
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@budibase/server",
3
3
  "email": "hi@budibase.com",
4
- "version": "2.3.18-alpha.13",
4
+ "version": "2.3.18-alpha.15",
5
5
  "description": "Budibase Web Server",
6
6
  "main": "src/index.ts",
7
7
  "repository": {
@@ -43,11 +43,11 @@
43
43
  "license": "GPL-3.0",
44
44
  "dependencies": {
45
45
  "@apidevtools/swagger-parser": "10.0.3",
46
- "@budibase/backend-core": "2.3.18-alpha.13",
47
- "@budibase/client": "2.3.18-alpha.13",
48
- "@budibase/pro": "2.3.18-alpha.12",
49
- "@budibase/string-templates": "2.3.18-alpha.13",
50
- "@budibase/types": "2.3.18-alpha.13",
46
+ "@budibase/backend-core": "2.3.18-alpha.15",
47
+ "@budibase/client": "2.3.18-alpha.15",
48
+ "@budibase/pro": "2.3.18-alpha.14",
49
+ "@budibase/string-templates": "2.3.18-alpha.15",
50
+ "@budibase/types": "2.3.18-alpha.15",
51
51
  "@bull-board/api": "3.7.0",
52
52
  "@bull-board/koa": "3.9.4",
53
53
  "@elastic/elasticsearch": "7.10.0",
@@ -87,6 +87,7 @@
87
87
  "koa-send": "5.0.0",
88
88
  "koa-session": "5.12.0",
89
89
  "koa-static": "5.0.0",
90
+ "koa-useragent": "^4.1.0",
90
91
  "koa2-ratelimit": "1.1.1",
91
92
  "lodash": "4.17.21",
92
93
  "memorystream": "0.3.1",
@@ -173,5 +174,5 @@
173
174
  "optionalDependencies": {
174
175
  "oracledb": "5.3.0"
175
176
  },
176
- "gitHead": "95ce3bb56c8db6b69b3b9081c0eeb0fff5180518"
177
+ "gitHead": "78287e11da6973374962afac59b35c598a54efc3"
177
178
  }
@@ -24,8 +24,7 @@ import { breakExternalTableId, isSQL } from "../../../integrations/utils"
24
24
  import { processObjectSync } from "@budibase/string-templates"
25
25
  import { cloneDeep } from "lodash/fp"
26
26
  import { processFormulas, processDates } from "../../../utilities/rowProcessor"
27
- import { context } from "@budibase/backend-core"
28
- import { removeKeyNumbering } from "./utils"
27
+ import { db as dbCore } from "@budibase/backend-core"
29
28
  import sdk from "../../../sdk"
30
29
 
31
30
  export interface ManyRelationship {
@@ -61,7 +60,7 @@ function buildFilters(
61
60
  let prefix = 1
62
61
  for (let operator of Object.values(filters)) {
63
62
  for (let field of Object.keys(operator || {})) {
64
- if (removeKeyNumbering(field) === "_id") {
63
+ if (dbCore.removeKeyNumbering(field) === "_id") {
65
64
  if (primary) {
66
65
  const parts = breakRowIdField(operator[field])
67
66
  for (let field of primary) {
@@ -1,531 +1,18 @@
1
- import { SearchIndexes } from "../../../db/utils"
2
- import { removeKeyNumbering } from "./utils"
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
- let limit = params.limit
479
- if (limit == null || isNaN(limit) || limit < 0) {
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
- * Performs a full search, fetching multiple pages if required to return the
510
- * desired amount of results. There is a limit of 1000 results to avoid
511
- * heavy performance hits, and to avoid client components breaking from
512
- * handling too much data.
513
- * @param query {object} The JSON query structure
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 { BBContext, Row, Table } from "@budibase/types"
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"
package/src/app.ts CHANGED
@@ -32,6 +32,7 @@ import { initialise as initialiseWebsockets } from "./websocket"
32
32
  import { startup } from "./startup"
33
33
  const Sentry = require("@sentry/node")
34
34
  const destroyable = require("server-destroy")
35
+ const { userAgent } = require("koa-useragent")
35
36
 
36
37
  const app = new Koa()
37
38
 
@@ -53,6 +54,7 @@ app.use(
53
54
  )
54
55
 
55
56
  app.use(middleware.logging)
57
+ app.use(userAgent)
56
58
 
57
59
  if (env.isProd()) {
58
60
  env._set("NODE_ENV", "production")
package/src/db/index.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { init as coreInit } from "@budibase/backend-core"
1
+ import * as core from "@budibase/backend-core"
2
2
  import env from "../environment"
3
3
 
4
4
  export function init() {
@@ -12,5 +12,5 @@ export function init() {
12
12
  dbConfig.allDbs = true
13
13
  }
14
14
 
15
- coreInit({ db: dbConfig })
15
+ core.init({ db: dbConfig })
16
16
  }
package/src/db/utils.ts CHANGED
@@ -9,10 +9,6 @@ export const AppStatus = {
9
9
  DEPLOYED: "published",
10
10
  }
11
11
 
12
- export const SearchIndexes = {
13
- ROWS: "rows",
14
- }
15
-
16
12
  export const BudibaseInternalDB = {
17
13
  _id: "bb_internal",
18
14
  type: dbCore.BUDIBASE_DATASOURCE_TYPE,
@@ -1,6 +1,6 @@
1
1
  import { context } from "@budibase/backend-core"
2
- import { DocumentType, SEPARATOR, ViewName, SearchIndexes } from "../utils"
3
- import { LinkDocument, Row } from "@budibase/types"
2
+ import { DocumentType, SEPARATOR, ViewName } from "../utils"
3
+ import { LinkDocument, Row, SearchIndex } from "@budibase/types"
4
4
  const SCREEN_PREFIX = DocumentType.SCREEN + SEPARATOR
5
5
 
6
6
  /**************************************************
@@ -91,7 +91,7 @@ async function searchIndex(indexName: string, fnString: string) {
91
91
 
92
92
  export async function createAllSearchIndex() {
93
93
  await searchIndex(
94
- SearchIndexes.ROWS,
94
+ SearchIndex.ROWS,
95
95
  function (doc: Row) {
96
96
  function idx(input: Row, prev?: string) {
97
97
  for (let key of Object.keys(input)) {
@@ -6,11 +6,11 @@ import {
6
6
  SearchFilters,
7
7
  SortDirection,
8
8
  } from "@budibase/types"
9
+ import { db as dbCore } from "@budibase/backend-core"
9
10
  import { QueryOptions } from "../../definitions/datasource"
10
11
  import { isIsoDateString, SqlClient } from "../utils"
11
12
  import SqlTableQueryBuilder from "./sqlTable"
12
13
  import environment from "../../environment"
13
- import { removeKeyNumbering } from "./utils"
14
14
 
15
15
  const envLimit = environment.SQL_MAX_ROWS
16
16
  ? parseInt(environment.SQL_MAX_ROWS)
@@ -136,7 +136,7 @@ class InternalBuilder {
136
136
  fn: (key: string, value: any) => void
137
137
  ) {
138
138
  for (let [key, value] of Object.entries(structure)) {
139
- const updatedKey = removeKeyNumbering(key)
139
+ const updatedKey = dbCore.removeKeyNumbering(key)
140
140
  const isRelationshipField = updatedKey.includes(".")
141
141
  if (!opts.relationship && !isRelationshipField) {
142
142
  fn(`${opts.tableName}.${updatedKey}`, value)