diff --git a/packages/server/src/sdk/app/rows/search.ts b/packages/server/src/sdk/app/rows/search.ts index e840c1ca51..a3c695c61c 100644 --- a/packages/server/src/sdk/app/rows/search.ts +++ b/packages/server/src/sdk/app/rows/search.ts @@ -1,9 +1,12 @@ import { EmptyFilterOption, + FieldType, Row, RowSearchParams, SearchResponse, SortOrder, + Table, + TableSchema, } from "@budibase/types" import { isExternalTableID } from "../../../integrations/utils" import * as internal from "./search/internal" @@ -87,10 +90,10 @@ export async function search( options.fields = visibleTableFields } - options.query = removeInvalidFilters(options.query, [ - "_id", - ...options.fields, - ]) + options.query = removeInvalidFilters( + options.query, + await getQueriableFields(options.fields, table.schema) + ) let result: SearchResponse if (isExternalTable) { @@ -134,3 +137,48 @@ export async function fetchView( ): Promise { return pickApi(tableId).fetchView(viewName, params) } + +async function getQueriableFields( + fields: string[], + schema: TableSchema +): Promise { + const handledTables: Record = {} + const extractTableFields = async ( + fromField: string, + tableId: string + ): Promise => { + const result = [] + if (handledTables[tableId]) { + return [] + } + const table = await sdk.tables.getTable(tableId) + handledTables[tableId] = table + + for (const field of Object.keys(table.schema)) { + const formattedColumn = `${fromField}.${field}` + const subSchema = table.schema[field] + if (subSchema.type === FieldType.LINK) { + result.push( + ...(await extractTableFields(formattedColumn, subSchema.tableId)) + ) + } else { + result.push(formattedColumn) + } + } + return result + } + + const result = [ + "_id", // Querying by _id is always allowed, even if it's never part of the schema + ] + + for (const field of fields) { + if (schema[field].type === FieldType.LINK) { + result.push(...(await extractTableFields(field, schema[field].tableId))) + } else { + result.push(field) + } + } + + return result +}