diff --git a/packages/backend-core/src/db/lucene.ts b/packages/backend-core/src/db/lucene.ts index dd9de20400..9614bd6df2 100644 --- a/packages/backend-core/src/db/lucene.ts +++ b/packages/backend-core/src/db/lucene.ts @@ -44,23 +44,23 @@ export function removeKeyNumbering(key: any): string { * Optionally takes a base lucene query object. */ export class QueryBuilder { - dbName: string - index: string - query: SearchFilters - limit: number - sort?: string - bookmark?: string - sortOrder: string - sortType: string + #dbName: string + #index: string + #query: SearchFilters + #limit: number + #sort?: string + #bookmark?: string + #sortOrder: string + #sortType: string #includeDocs: boolean - version?: string - indexBuilder?: () => Promise - noEscaping = false + #version?: string + #indexBuilder?: () => Promise + #noEscaping = false constructor(dbName: string, index: string, base?: SearchFilters) { - this.dbName = dbName - this.index = index - this.query = { + this.#dbName = dbName + this.#index = index + this.#query = { allOr: false, string: {}, fuzzy: {}, @@ -75,65 +75,65 @@ export class QueryBuilder { containsAny: {}, ...base, } - this.limit = 50 - this.sortOrder = "ascending" - this.sortType = "string" + this.#limit = 50 + this.#sortOrder = "ascending" + this.#sortType = "string" this.#includeDocs = true } disableEscaping() { - this.noEscaping = true + this.#noEscaping = true return this } setIndexBuilder(builderFn: () => Promise) { - this.indexBuilder = builderFn + this.#indexBuilder = builderFn return this } setVersion(version?: string) { if (version != null) { - this.version = version + this.#version = version } return this } setTable(tableId: string) { - this.query.equal!.tableId = tableId + this.#query.equal!.tableId = tableId return this } setLimit(limit?: number) { if (limit != null) { - this.limit = limit + this.#limit = limit } return this } setSort(sort?: string) { if (sort != null) { - this.sort = sort + this.#sort = sort } return this } setSortOrder(sortOrder?: string) { if (sortOrder != null) { - this.sortOrder = sortOrder + this.#sortOrder = sortOrder } return this } setSortType(sortType?: string) { if (sortType != null) { - this.sortType = sortType + this.#sortType = sortType } return this } setBookmark(bookmark?: string) { if (bookmark != null) { - this.bookmark = bookmark + this.#bookmark = bookmark } return this } @@ -149,17 +149,17 @@ export class QueryBuilder { } addString(key: string, partial: string) { - this.query.string![key] = partial + this.#query.string![key] = partial return this } addFuzzy(key: string, fuzzy: string) { - this.query.fuzzy![key] = fuzzy + this.#query.fuzzy![key] = fuzzy return this } addRange(key: string, low: string | number, high: string | number) { - this.query.range![key] = { + this.#query.range![key] = { low, high, } @@ -167,51 +167,51 @@ export class QueryBuilder { } addEqual(key: string, value: any) { - this.query.equal![key] = value + this.#query.equal![key] = value return this } addNotEqual(key: string, value: any) { - this.query.notEqual![key] = value + this.#query.notEqual![key] = value return this } addEmpty(key: string, value: any) { - this.query.empty![key] = value + this.#query.empty![key] = value return this } addNotEmpty(key: string, value: any) { - this.query.notEmpty![key] = value + this.#query.notEmpty![key] = value return this } addOneOf(key: string, value: any) { - this.query.oneOf![key] = value + this.#query.oneOf![key] = value return this } addContains(key: string, value: any) { - this.query.contains![key] = value + this.#query.contains![key] = value return this } addNotContains(key: string, value: any) { - this.query.notContains![key] = value + this.#query.notContains![key] = value return this } addContainsAny(key: string, value: any) { - this.query.containsAny![key] = value + this.#query.containsAny![key] = value return this } setAllOr() { - this.query.allOr = true + this.#query.allOr = true } handleSpaces(input: string) { - if (this.noEscaping) { + if (this.#noEscaping) { return input } else { return input.replace(/ /g, "_") @@ -226,7 +226,7 @@ export class QueryBuilder { * @returns {string|*} */ preprocess(value: any, { escape, lowercase, wrap, type }: any = {}) { - const hasVersion = !!this.version + const hasVersion = !!this.#version // Determine if type needs wrapped const originalType = typeof value // Convert to lowercase @@ -234,7 +234,7 @@ export class QueryBuilder { value = value.toLowerCase ? value.toLowerCase() : value } // Escape characters - if (!this.noEscaping && escape && originalType === "string") { + if (!this.#noEscaping && escape && originalType === "string") { value = `${value}`.replace(/[ #+\-&|!(){}\]^"~*?:\\]/g, "\\$&") } @@ -249,7 +249,7 @@ export class QueryBuilder { isMultiCondition() { let count = 0 - for (let filters of Object.values(this.query)) { + for (let filters of Object.values(this.#query)) { // not contains is one massive filter in allOr mode if (typeof filters === "object") { count += Object.keys(filters).length @@ -279,13 +279,13 @@ export class QueryBuilder { buildSearchQuery() { const builder = this - let allOr = this.query && this.query.allOr + let allOr = this.#query && this.#query.allOr let query = allOr ? "" : "*:*" const allPreProcessingOpts = { escape: true, lowercase: true, wrap: true } let tableId - if (this.query.equal!.tableId) { - tableId = this.query.equal!.tableId - delete this.query.equal!.tableId + if (this.#query.equal!.tableId) { + tableId = this.#query.equal!.tableId + delete this.#query.equal!.tableId } const equal = (key: string, value: any) => { @@ -370,8 +370,8 @@ export class QueryBuilder { } // Construct the actual lucene search query string from JSON structure - if (this.query.string) { - build(this.query.string, (key: string, value: any) => { + if (this.#query.string) { + build(this.#query.string, (key: string, value: any) => { if (!value) { return null } @@ -383,8 +383,8 @@ export class QueryBuilder { return `${key}:${value}*` }) } - if (this.query.range) { - build(this.query.range, (key: string, value: any) => { + if (this.#query.range) { + build(this.#query.range, (key: string, value: any) => { if (!value) { return null } @@ -399,8 +399,8 @@ export class QueryBuilder { return `${key}:[${low} TO ${high}]` }) } - if (this.query.fuzzy) { - build(this.query.fuzzy, (key: string, value: any) => { + if (this.#query.fuzzy) { + build(this.#query.fuzzy, (key: string, value: any) => { if (!value) { return null } @@ -412,34 +412,34 @@ export class QueryBuilder { return `${key}:${value}~` }) } - if (this.query.equal) { - build(this.query.equal, equal) + if (this.#query.equal) { + build(this.#query.equal, equal) } - if (this.query.notEqual) { - build(this.query.notEqual, (key: string, value: any) => { + if (this.#query.notEqual) { + build(this.#query.notEqual, (key: string, value: any) => { if (!value) { return null } return `!${key}:${builder.preprocess(value, allPreProcessingOpts)}` }) } - if (this.query.empty) { - build(this.query.empty, (key: string) => `!${key}:["" TO *]`) + if (this.#query.empty) { + build(this.#query.empty, (key: string) => `!${key}:["" TO *]`) } - if (this.query.notEmpty) { - build(this.query.notEmpty, (key: string) => `${key}:["" TO *]`) + if (this.#query.notEmpty) { + build(this.#query.notEmpty, (key: string) => `${key}:["" TO *]`) } - if (this.query.oneOf) { - build(this.query.oneOf, oneOf) + if (this.#query.oneOf) { + build(this.#query.oneOf, oneOf) } - if (this.query.contains) { - build(this.query.contains, contains) + if (this.#query.contains) { + build(this.#query.contains, contains) } - if (this.query.notContains) { - build(this.compressFilters(this.query.notContains), notContains) + if (this.#query.notContains) { + build(this.compressFilters(this.#query.notContains), notContains) } - if (this.query.containsAny) { - build(this.query.containsAny, containsAny) + if (this.#query.containsAny) { + build(this.#query.containsAny, containsAny) } // make sure table ID is always added as an AND if (tableId) { @@ -453,29 +453,31 @@ export class QueryBuilder { buildSearchBody() { let body: any = { q: this.buildSearchQuery(), - limit: Math.min(this.limit, 200), + limit: Math.min(this.#limit, 200), include_docs: this.#includeDocs, } - if (this.bookmark) { - body.bookmark = this.bookmark + if (this.#bookmark) { + body.bookmark = this.#bookmark } - if (this.sort) { - const order = this.sortOrder === "descending" ? "-" : "" - const type = `<${this.sortType}>` - body.sort = `${order}${this.handleSpaces(this.sort)}${type}` + if (this.#sort) { + const order = this.#sortOrder === "descending" ? "-" : "" + const type = `<${this.#sortType}>` + body.sort = `${order}${this.handleSpaces(this.#sort)}${type}` } return body } async run() { const { url, cookie } = getCouchInfo() - const fullPath = `${url}/${this.dbName}/_design/database/_search/${this.index}` + const fullPath = `${url}/${this.#dbName}/_design/database/_search/${ + this.#index + }` const body = this.buildSearchBody() try { return await runQuery(fullPath, body, cookie) } catch (err: any) { - if (err.status === 404 && this.indexBuilder) { - await this.indexBuilder() + if (err.status === 404 && this.#indexBuilder) { + await this.#indexBuilder() return await runQuery(fullPath, body, cookie) } else { throw err