budibase/packages/server/src/sdk/app/rows/search/utils.ts

131 lines
3.2 KiB
TypeScript

import {
FieldType,
Table,
DocumentType,
SEPARATOR,
BBReferenceFieldSubType,
SearchFilters,
SearchIndex,
SearchResponse,
Row,
RowSearchParams,
} from "@budibase/types"
import { db as dbCore, context } from "@budibase/backend-core"
import { helpers, utils } from "@budibase/shared-core"
export async function paginatedSearch(
query: SearchFilters,
params: RowSearchParams
): Promise<SearchResponse<Row>> {
const appId = context.getAppId()
return dbCore.paginatedSearch(appId!, SearchIndex.ROWS, query, params)
}
export async function fullSearch(
query: SearchFilters,
params: RowSearchParams
): Promise<{ rows: Row[] }> {
const appId = context.getAppId()
return dbCore.fullSearch(appId!, SearchIndex.ROWS, query, params)
}
function findColumnInQueries(
column: string,
options: RowSearchParams,
callback: (filter: any) => any
) {
if (!options.query) {
return
}
for (let filterBlock of Object.values(options.query)) {
if (typeof filterBlock !== "object") {
continue
}
for (let [key, filter] of Object.entries(filterBlock)) {
if (key.endsWith(column)) {
filterBlock[key] = callback(filter)
}
}
}
}
function userColumnMapping(
column: string,
options: RowSearchParams,
isDeprecatedSingleUserColumn: boolean = false,
isSql: boolean = false
) {
findColumnInQueries(column, options, (filterValue: any): any => {
const isArray = Array.isArray(filterValue),
isString = typeof filterValue === "string"
if (!isString && !isArray) {
return filterValue
}
const processString = (input: string) => {
const rowPrefix = DocumentType.ROW + SEPARATOR
if (input.startsWith(rowPrefix)) {
return dbCore.getGlobalIDFromUserMetadataID(input)
} else {
return input
}
}
let wrapper = (s: string) => s
if (isDeprecatedSingleUserColumn && filterValue && isSql) {
// Decreated single users are stored as stringified arrays of a single value
wrapper = (s: string) => JSON.stringify([s])
}
if (isArray) {
return filterValue.map(el => {
if (typeof el === "string") {
return wrapper(processString(el))
} else {
return el
}
})
} else {
return wrapper(processString(filterValue))
}
})
}
// maps through the search parameters to check if any of the inputs are invalid
// based on the table schema, converts them to something that is valid.
export function searchInputMapping(
table: Table,
options: RowSearchParams,
datasourceOptions: { isSql?: boolean } = {}
) {
if (!table?.schema) {
return options
}
for (let [key, column] of Object.entries(table.schema)) {
switch (column.type) {
case FieldType.BB_REFERENCE_SINGLE: {
const subtype = column.subtype
switch (subtype) {
case BBReferenceFieldSubType.USER:
userColumnMapping(key, options)
break
default:
utils.unreachable(subtype)
}
break
}
case FieldType.BB_REFERENCE: {
userColumnMapping(
key,
options,
helpers.schema.isDeprecatedSingleUserColumn(column),
datasourceOptions.isSql
)
break
}
}
}
return options
}