Merge pull request #14257 from Budibase/fix/search-by-row-id

Fix for searching by row ID (with a limit) when the row has many related rows
This commit is contained in:
Michael Drury 2024-07-30 12:41:29 +01:00 committed by GitHub
commit ba3f69ead9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 106 additions and 20 deletions

View File

@ -5,12 +5,12 @@ import {
knexClient,
} from "../../../integrations/tests/utils"
import {
db as dbCore,
context,
db as dbCore,
MAX_VALID_DATE,
MIN_VALID_DATE,
utils,
SQLITE_DESIGN_DOC_ID,
utils,
} from "@budibase/backend-core"
import * as setup from "./utilities"
@ -2560,4 +2560,48 @@ describe.each([
}).toContainExactly([{ name: "foo" }])
})
})
!isInMemory &&
describe("search by _id", () => {
let row: Row
beforeAll(async () => {
const toRelateTable = await createTable({
name: {
name: "name",
type: FieldType.STRING,
},
})
table = await createTable({
name: {
name: "name",
type: FieldType.STRING,
},
rel: {
name: "rel",
type: FieldType.LINK,
relationshipType: RelationshipType.MANY_TO_MANY,
tableId: toRelateTable._id!,
fieldName: "rel",
},
})
const [row1, row2] = await Promise.all([
config.api.row.save(toRelateTable._id!, { name: "tag 1" }),
config.api.row.save(toRelateTable._id!, { name: "tag 2" }),
])
row = await config.api.row.save(table._id!, {
name: "product 1",
rel: [row1._id, row2._id],
})
})
it("can filter by the row ID with limit 1", async () => {
await expectSearch({
query: {
equal: { _id: row._id },
},
limit: 1,
}).toContainExactly([row])
})
})
})

View File

@ -22,21 +22,21 @@ import { HTTPError } from "@budibase/backend-core"
import pick from "lodash/pick"
import { outputProcessing } from "../../../../utilities/rowProcessor"
import sdk from "../../../"
import { isSearchingByRowID } from "./utils"
export async function search(
options: RowSearchParams,
table: Table
): Promise<SearchResponse<Row>> {
const { tableId } = options
const { countRows, paginate, query, ...params } = options
const { limit } = params
let bookmark =
(params.bookmark && parseInt(params.bookmark as string)) || undefined
if (paginate && !bookmark) {
bookmark = 0
}
function getPaginationAndLimitParameters(
filters: SearchFilters,
paginate: boolean | undefined,
bookmark: number | undefined,
limit: number | undefined
): PaginationJson | undefined {
let paginateObj: PaginationJson | undefined
// only try set limits/pagination if we aren't doing a row ID search
if (isSearchingByRowID(filters)) {
return
}
if (paginate && !limit) {
throw new Error("Cannot paginate query without a limit")
}
@ -49,11 +49,35 @@ export async function search(
if (bookmark) {
paginateObj.offset = limit * bookmark
}
} else if (params && limit) {
} else if (limit) {
paginateObj = {
limit: limit,
}
}
return paginateObj
}
export async function search(
options: RowSearchParams,
table: Table
): Promise<SearchResponse<Row>> {
const { tableId } = options
const { countRows, paginate, query, ...params } = options
const { limit } = params
let bookmark =
(params.bookmark && parseInt(params.bookmark as string)) || undefined
if (paginate && !bookmark) {
bookmark = 0
}
let paginateObj = getPaginationAndLimitParameters(
query,
paginate,
bookmark,
limit
)
let sort: SortJson | undefined
if (params.sort) {
const direction =

View File

@ -42,6 +42,7 @@ import {
getTableIDList,
} from "./filters"
import { dataFilters, PROTECTED_INTERNAL_COLUMNS } from "@budibase/shared-core"
import { isSearchingByRowID } from "./utils"
const builder = new sql.Sql(SqlClient.SQL_LITE)
const MISSING_COLUMN_REGEX = new RegExp(`no such column: .+`)
@ -264,6 +265,10 @@ export async function search(
const relationships = buildInternalRelationships(table)
const searchFilters: SearchFilters = {
...cleanupFilters(query, table, allTables),
documentType: DocumentType.ROW,
}
const request: QueryJson = {
endpoint: {
// not important, we query ourselves
@ -271,10 +276,7 @@ export async function search(
entityId: table._id!,
operation: Operation.READ,
},
filters: {
...cleanupFilters(query, table, allTables),
documentType: DocumentType.ROW,
},
filters: searchFilters,
table,
meta: {
table,
@ -304,7 +306,8 @@ export async function search(
}
const bookmark: number = (params.bookmark as number) || 0
if (params.limit) {
// limits don't apply if we doing a row ID search
if (!isSearchingByRowID(searchFilters) && params.limit) {
paginate = true
request.paginate = {
limit: params.limit + 1,

View File

@ -108,3 +108,18 @@ export function searchInputMapping(table: Table, options: RowSearchParams) {
}
return options
}
export function isSearchingByRowID(query: SearchFilters): boolean {
for (let searchField of Object.values(query)) {
if (typeof searchField !== "object") {
continue
}
const hasId = Object.keys(searchField).find(
key => dbCore.removeKeyNumbering(key) === "_id" && searchField[key]
)
if (hasId) {
return true
}
}
return false
}