diff --git a/packages/server/src/api/controllers/row/views.ts b/packages/server/src/api/controllers/row/views.ts index c38a415aa2..bcf9c6b441 100644 --- a/packages/server/src/api/controllers/row/views.ts +++ b/packages/server/src/api/controllers/row/views.ts @@ -58,8 +58,9 @@ export async function searchView( }) }) } else { + const operator = query.allOr ? "$or" : "$and" query = { - $and: { + [operator]: { conditions: [query, body.query], }, } diff --git a/packages/server/src/sdk/app/rows/search/internal/sqs.ts b/packages/server/src/sdk/app/rows/search/internal/sqs.ts index 4bfa8f8fa5..0c7946339e 100644 --- a/packages/server/src/sdk/app/rows/search/internal/sqs.ts +++ b/packages/server/src/sdk/app/rows/search/internal/sqs.ts @@ -139,34 +139,29 @@ function cleanupFilters( allTables.some(table => table.schema[key]) const splitter = new dataFilters.ColumnSplitter(allTables) - - const prefixFilters = (filters: SearchFilters) => { - for (const filterKey of Object.keys(filters) as (keyof SearchFilters)[]) { - if (isLogicalSearchOperator(filterKey)) { - for (const condition of filters[filterKey]!.conditions) { - prefixFilters(condition) - } - } else { - const filter = filters[filterKey]! - if (typeof filter !== "object") { - continue - } - for (const key of Object.keys(filter)) { - const { numberPrefix, relationshipPrefix, column } = splitter.run(key) - if (keyInAnyTable(column)) { - filter[ - `${numberPrefix || ""}${ - relationshipPrefix || "" - }${mapToUserColumn(column)}` - ] = filter[key] - delete filter[key] - } + for (const filterKey of Object.keys(filters) as (keyof SearchFilters)[]) { + if (!isLogicalSearchOperator(filterKey)) { + const filter = filters[filterKey]! + if (typeof filter !== "object") { + continue + } + for (const key of Object.keys(filter)) { + const { numberPrefix, relationshipPrefix, column } = splitter.run(key) + if (keyInAnyTable(column)) { + filter[ + `${numberPrefix || ""}${relationshipPrefix || ""}${mapToUserColumn( + column + )}` + ] = filter[key] + delete filter[key] } } } } - prefixFilters(filters) - return filters + + return dataFilters.recurseLogicalOperators(filters, (f: SearchFilters) => { + return cleanupFilters(f, table, allTables) + }) } function buildTableMap(tables: Table[]) { diff --git a/packages/server/src/sdk/app/rows/sqlAlias.ts b/packages/server/src/sdk/app/rows/sqlAlias.ts index 664e64057b..535709791d 100644 --- a/packages/server/src/sdk/app/rows/sqlAlias.ts +++ b/packages/server/src/sdk/app/rows/sqlAlias.ts @@ -12,6 +12,7 @@ import { getSQLClient } from "./utils" import { cloneDeep } from "lodash" import datasources from "../datasources" import { BudibaseInternalDB } from "../../../db/utils" +import { dataFilters } from "@budibase/shared-core" type PerformQueryFunction = ( datasource: Datasource, @@ -199,16 +200,20 @@ export default class AliasTables { ) } if (json.filters) { - for (let [filterKey, filter] of Object.entries(json.filters)) { - if (typeof filter !== "object") { - continue + const aliasFilters = (filters: SearchFilters): SearchFilters => { + for (let [filterKey, filter] of Object.entries(filters)) { + if (typeof filter !== "object") { + continue + } + const aliasedFilters: typeof filter = {} + for (let key of Object.keys(filter)) { + aliasedFilters[this.aliasField(key)] = filter[key] + } + filters[filterKey as keyof SearchFilters] = aliasedFilters } - const aliasedFilters: typeof filter = {} - for (let key of Object.keys(filter)) { - aliasedFilters[this.aliasField(key)] = filter[key] - } - json.filters[filterKey as keyof SearchFilters] = aliasedFilters + return dataFilters.recurseLogicalOperators(filters, aliasFilters) } + json.filters = aliasFilters(json.filters) } if (json.meta?.table) { this.getAlias(json.meta.table.name) diff --git a/packages/shared-core/src/filters.ts b/packages/shared-core/src/filters.ts index 2dd485a8a0..fa0b5c92ed 100644 --- a/packages/shared-core/src/filters.ts +++ b/packages/shared-core/src/filters.ts @@ -113,6 +113,20 @@ export const NoEmptyFilterStrings = [ OperatorOptions.In.value, ] as (keyof SearchQueryFields)[] +export function recurseLogicalOperators( + filters: SearchFilters, + fn: (f: SearchFilters) => SearchFilters +) { + for (const logical of Object.values(LogicalOperator)) { + if (filters[logical]) { + filters[logical]!.conditions = filters[logical]!.conditions.map( + condition => fn(condition) + ) + } + } + return filters +} + /** * Removes any fields that contain empty strings that would cause inconsistent * behaviour with how backend tables are filtered (no value means no filter). @@ -145,6 +159,7 @@ export const cleanupQuery = (query: SearchFilters) => { } } } + query = recurseLogicalOperators(query, cleanupQuery) return query } @@ -410,6 +425,7 @@ export function fixupFilterArrays(filters: SearchFilters) { } } } + recurseLogicalOperators(filters, fixupFilterArrays) return filters }