Make QueryBuilder vars private

This commit is contained in:
adrinr 2023-03-15 20:05:58 +01:00
parent 06245fee98
commit a91e4b4da1
1 changed files with 80 additions and 78 deletions

View File

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