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:
commit
ba3f69ead9
|
@ -5,12 +5,12 @@ import {
|
||||||
knexClient,
|
knexClient,
|
||||||
} from "../../../integrations/tests/utils"
|
} from "../../../integrations/tests/utils"
|
||||||
import {
|
import {
|
||||||
db as dbCore,
|
|
||||||
context,
|
context,
|
||||||
|
db as dbCore,
|
||||||
MAX_VALID_DATE,
|
MAX_VALID_DATE,
|
||||||
MIN_VALID_DATE,
|
MIN_VALID_DATE,
|
||||||
utils,
|
|
||||||
SQLITE_DESIGN_DOC_ID,
|
SQLITE_DESIGN_DOC_ID,
|
||||||
|
utils,
|
||||||
} from "@budibase/backend-core"
|
} from "@budibase/backend-core"
|
||||||
|
|
||||||
import * as setup from "./utilities"
|
import * as setup from "./utilities"
|
||||||
|
@ -2560,4 +2560,48 @@ describe.each([
|
||||||
}).toContainExactly([{ name: "foo" }])
|
}).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])
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -22,21 +22,21 @@ import { HTTPError } from "@budibase/backend-core"
|
||||||
import pick from "lodash/pick"
|
import pick from "lodash/pick"
|
||||||
import { outputProcessing } from "../../../../utilities/rowProcessor"
|
import { outputProcessing } from "../../../../utilities/rowProcessor"
|
||||||
import sdk from "../../../"
|
import sdk from "../../../"
|
||||||
|
import { isSearchingByRowID } from "./utils"
|
||||||
|
|
||||||
export async function search(
|
function getPaginationAndLimitParameters(
|
||||||
options: RowSearchParams,
|
filters: SearchFilters,
|
||||||
table: Table
|
paginate: boolean | undefined,
|
||||||
): Promise<SearchResponse<Row>> {
|
bookmark: number | undefined,
|
||||||
const { tableId } = options
|
limit: number | undefined
|
||||||
const { countRows, paginate, query, ...params } = options
|
): PaginationJson | undefined {
|
||||||
const { limit } = params
|
|
||||||
let bookmark =
|
|
||||||
(params.bookmark && parseInt(params.bookmark as string)) || undefined
|
|
||||||
if (paginate && !bookmark) {
|
|
||||||
bookmark = 0
|
|
||||||
}
|
|
||||||
let paginateObj: 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) {
|
if (paginate && !limit) {
|
||||||
throw new Error("Cannot paginate query without a limit")
|
throw new Error("Cannot paginate query without a limit")
|
||||||
}
|
}
|
||||||
|
@ -49,11 +49,35 @@ export async function search(
|
||||||
if (bookmark) {
|
if (bookmark) {
|
||||||
paginateObj.offset = limit * bookmark
|
paginateObj.offset = limit * bookmark
|
||||||
}
|
}
|
||||||
} else if (params && limit) {
|
} else if (limit) {
|
||||||
paginateObj = {
|
paginateObj = {
|
||||||
limit: limit,
|
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
|
let sort: SortJson | undefined
|
||||||
if (params.sort) {
|
if (params.sort) {
|
||||||
const direction =
|
const direction =
|
||||||
|
|
|
@ -42,6 +42,7 @@ import {
|
||||||
getTableIDList,
|
getTableIDList,
|
||||||
} from "./filters"
|
} from "./filters"
|
||||||
import { dataFilters, PROTECTED_INTERNAL_COLUMNS } from "@budibase/shared-core"
|
import { dataFilters, PROTECTED_INTERNAL_COLUMNS } from "@budibase/shared-core"
|
||||||
|
import { isSearchingByRowID } from "./utils"
|
||||||
|
|
||||||
const builder = new sql.Sql(SqlClient.SQL_LITE)
|
const builder = new sql.Sql(SqlClient.SQL_LITE)
|
||||||
const MISSING_COLUMN_REGEX = new RegExp(`no such column: .+`)
|
const MISSING_COLUMN_REGEX = new RegExp(`no such column: .+`)
|
||||||
|
@ -264,6 +265,10 @@ export async function search(
|
||||||
|
|
||||||
const relationships = buildInternalRelationships(table)
|
const relationships = buildInternalRelationships(table)
|
||||||
|
|
||||||
|
const searchFilters: SearchFilters = {
|
||||||
|
...cleanupFilters(query, table, allTables),
|
||||||
|
documentType: DocumentType.ROW,
|
||||||
|
}
|
||||||
const request: QueryJson = {
|
const request: QueryJson = {
|
||||||
endpoint: {
|
endpoint: {
|
||||||
// not important, we query ourselves
|
// not important, we query ourselves
|
||||||
|
@ -271,10 +276,7 @@ export async function search(
|
||||||
entityId: table._id!,
|
entityId: table._id!,
|
||||||
operation: Operation.READ,
|
operation: Operation.READ,
|
||||||
},
|
},
|
||||||
filters: {
|
filters: searchFilters,
|
||||||
...cleanupFilters(query, table, allTables),
|
|
||||||
documentType: DocumentType.ROW,
|
|
||||||
},
|
|
||||||
table,
|
table,
|
||||||
meta: {
|
meta: {
|
||||||
table,
|
table,
|
||||||
|
@ -304,7 +306,8 @@ export async function search(
|
||||||
}
|
}
|
||||||
|
|
||||||
const bookmark: number = (params.bookmark as number) || 0
|
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
|
paginate = true
|
||||||
request.paginate = {
|
request.paginate = {
|
||||||
limit: params.limit + 1,
|
limit: params.limit + 1,
|
||||||
|
|
|
@ -108,3 +108,18 @@ export function searchInputMapping(table: Table, options: RowSearchParams) {
|
||||||
}
|
}
|
||||||
return options
|
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
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue