Merge branch 'master' of github.com:Budibase/budibase into fix/group-perf-improvements

This commit is contained in:
mike12345567 2025-03-11 13:36:12 +00:00
commit 889f8fe59a
29 changed files with 328 additions and 360 deletions

View File

@ -1,6 +1,6 @@
{
"$schema": "node_modules/lerna/schemas/lerna-schema.json",
"version": "3.4.24",
"version": "3.5.0",
"npmClient": "yarn",
"concurrency": 20,
"command": {

View File

@ -18,7 +18,6 @@
"eslint-plugin-jest": "28.9.0",
"eslint-plugin-local-rules": "3.0.2",
"eslint-plugin-svelte": "2.46.1",
"svelte-preprocess": "^6.0.3",
"husky": "^8.0.3",
"kill-port": "^1.6.1",
"lerna": "7.4.2",
@ -29,7 +28,9 @@
"prettier-plugin-svelte": "^2.3.0",
"proper-lockfile": "^4.1.2",
"svelte": "4.2.19",
"svelte-check": "^4.1.5",
"svelte-eslint-parser": "0.43.0",
"svelte-preprocess": "^6.0.3",
"typescript": "5.7.2",
"typescript-eslint": "8.17.0",
"yargs": "^17.7.2"

View File

@ -100,7 +100,6 @@
"jest": "29.7.0",
"jsdom": "^21.1.1",
"resize-observer-polyfill": "^1.5.1",
"svelte-check": "^4.1.0",
"svelte-jester": "^1.3.2",
"vite": "^4.5.0",
"vite-plugin-static-copy": "^0.17.0",

View File

@ -16,7 +16,8 @@
},
"scripts": {
"build": "vite build",
"dev": "vite build --watch --mode=dev"
"dev": "vite build --watch --mode=dev",
"check:types": "yarn svelte-check"
},
"dependencies": {
"@budibase/bbui": "*",

View File

@ -10,7 +10,9 @@ export const API = createAPIClient({
// Attach client specific headers
attachHeaders: headers => {
// Attach app ID header
headers["x-budibase-app-id"] = window["##BUDIBASE_APP_ID##"]
if (window["##BUDIBASE_APP_ID##"]) {
headers["x-budibase-app-id"] = window["##BUDIBASE_APP_ID##"]
}
// Attach client header if not inside the builder preview
if (!window["##BUDIBASE_IN_BUILDER##"]) {

View File

@ -141,6 +141,7 @@
var(--spectrum-global-dimension-size-300)
);
display: -webkit-box;
line-clamp: 2;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;

View File

@ -7,6 +7,7 @@
import { isGridEvent } from "@/utils/grid"
import { DNDPlaceholderID } from "@/constants"
import type { Component } from "@budibase/types"
import { DropPosition } from "@budibase/types"
type ChildCoords = {
placeholder: boolean
@ -287,7 +288,7 @@
}
// Convert parent + index into target + mode
let legacyDropTarget, legacyDropMode
let legacyDropTarget, legacyDropMode: DropPosition
const parent: Component | null = findComponentById(
get(screenStore).activeScreen?.props,
drop.parent
@ -309,16 +310,16 @@
// Use inside if no existing children
if (!children?.length) {
legacyDropTarget = parent._id
legacyDropMode = "inside"
legacyDropMode = DropPosition.INSIDE
} else if (drop.index === 0) {
legacyDropTarget = children[0]?._id
legacyDropMode = "above"
legacyDropMode = DropPosition.ABOVE
} else {
legacyDropTarget = children[drop.index - 1]?._id
legacyDropMode = "below"
legacyDropMode = DropPosition.BELOW
}
if (legacyDropTarget && legacyDropMode) {
if (legacyDropTarget && legacyDropMode && source.id) {
dropping = true
await builderStore.actions.moveComponent(
source.id,

View File

@ -17,6 +17,7 @@
Devices,
GridDragMode,
} from "@/utils/grid"
import { DropPosition } from "@budibase/types"
type GridDragSide =
| "top"
@ -222,7 +223,7 @@
// If holding ctrl/cmd then leave behind a duplicate of this component
if (mode === GridDragMode.Move && (e.ctrlKey || e.metaKey)) {
builderStore.actions.duplicateComponent(id, "above", false)
builderStore.actions.duplicateComponent(id, DropPosition.ABOVE, false)
}
// Find grid parent and read from DOM

View File

@ -115,7 +115,7 @@ const createBuilderStore = () => {
component: string,
parent: string,
index: number,
props: Record<string, any>
props?: Record<string, any>
) => {
eventStore.actions.dispatchEvent("drop-new-component", {
component,

View File

@ -7,6 +7,7 @@
"target": "ESNext",
"moduleResolution": "bundler",
"resolveJsonModule": true,
"skipLibCheck": true,
"paths": {
"@budibase/*": [
"../*/src/index.ts",

View File

@ -16,8 +16,5 @@
"lodash": "4.17.21",
"shortid": "2.2.15",
"socket.io-client": "^4.7.5"
},
"devDependencies": {
"svelte-check": "^4.1.0"
}
}

View File

@ -8,7 +8,6 @@ import {
Row,
SearchFilters,
SortOrder,
SortType,
TableSchema,
} from "@budibase/types"
import { APIClient } from "../api/types"
@ -72,8 +71,6 @@ export default abstract class BaseDataFetch<
options: DataFetchOptions<TQuery> & {
datasource: TDatasource
sortType: SortType | null
// Client side feature customisation
clientSideSearching: boolean
clientSideSorting: boolean
@ -106,7 +103,6 @@ export default abstract class BaseDataFetch<
// Sorting config
sortColumn: null,
sortOrder: SortOrder.ASCENDING,
sortType: null,
// Pagination config
paginate: true,
@ -227,31 +223,12 @@ export default abstract class BaseDataFetch<
this.options.sortColumn = this.getDefaultSortColumn(definition, schema)
}
// If we don't have a sort column specified then just ensure we don't set
// any sorting params
if (!this.options.sortColumn) {
// If no sort order, default to ascending
if (!this.options.sortOrder) {
this.options.sortOrder = SortOrder.ASCENDING
this.options.sortType = null
} else {
// Otherwise determine what sort type to use base on sort column
this.options.sortType = SortType.STRING
const fieldSchema = schema?.[this.options.sortColumn]
if (
fieldSchema?.type === FieldType.NUMBER ||
fieldSchema?.type === FieldType.BIGINT ||
("calculationType" in fieldSchema && fieldSchema?.calculationType)
) {
this.options.sortType = SortType.NUMBER
}
// If no sort order, default to ascending
if (!this.options.sortOrder) {
this.options.sortOrder = SortOrder.ASCENDING
} else {
// Ensure sortOrder matches the enum
this.options.sortOrder =
this.options.sortOrder.toLowerCase() as SortOrder
}
// Ensure sortOrder matches the enum
this.options.sortOrder = this.options.sortOrder.toLowerCase() as SortOrder
}
// Build the query
@ -294,7 +271,6 @@ export default abstract class BaseDataFetch<
const {
sortColumn,
sortOrder,
sortType,
limit,
clientSideSearching,
clientSideSorting,
@ -311,8 +287,8 @@ export default abstract class BaseDataFetch<
}
// If we don't support sorting, do a client-side sort
if (!this.features.supportsSort && clientSideSorting && sortType) {
rows = sort(rows, sortColumn as any, sortOrder, sortType)
if (!this.features.supportsSort && clientSideSorting && sortColumn) {
rows = sort(rows, sortColumn, sortOrder)
}
// If we don't support pagination, do a client-side limit

View File

@ -29,8 +29,7 @@ export default class TableFetch extends BaseDataFetch<TableDatasource, Table> {
}
async getData() {
const { datasource, limit, sortColumn, sortOrder, sortType, paginate } =
this.options
const { datasource, limit, sortColumn, sortOrder, paginate } = this.options
const { tableId } = datasource
const { cursor, query } = get(this.store)
@ -41,7 +40,6 @@ export default class TableFetch extends BaseDataFetch<TableDatasource, Table> {
limit,
sort: sortColumn,
sortOrder: sortOrder ?? SortOrder.ASCENDING,
sortType,
paginate,
bookmark: cursor,
})

View File

@ -1,4 +1,5 @@
import {
SearchViewRowRequest,
SortOrder,
ViewDatasource,
ViewV2Enriched,
@ -40,8 +41,7 @@ export default class ViewV2Fetch extends BaseDataFetch<
}
async getData() {
const { datasource, limit, sortColumn, sortOrder, sortType, paginate } =
this.options
const { datasource, limit, sortColumn, sortOrder, paginate } = this.options
const { cursor, query, definition } = get(this.store)
// If this is a calculation view and we have no calculations, return nothing
@ -68,14 +68,13 @@ export default class ViewV2Fetch extends BaseDataFetch<
}
try {
const request = {
const request: SearchViewRowRequest = {
query,
paginate,
limit,
bookmark: cursor,
sort: sortColumn,
sortOrder: sortOrder,
sortType,
}
if (paginate) {
const res = await this.API.viewV2.fetch(datasource.id, {

View File

@ -1,6 +1,6 @@
export { createAPIClient } from "./api"
export type { APIClient } from "./api"
export { fetchData, DataFetchMap } from "./fetch"
export { fetchData, DataFetchMap, type DataFetchType } from "./fetch"
export * as Constants from "./constants"
export * from "./stores"
export * from "./utils"

View File

@ -0,0 +1,7 @@
import { vitePreprocess } from "@sveltejs/vite-plugin-svelte"
const config = {
preprocess: vitePreprocess(),
}
export default config

View File

@ -263,7 +263,6 @@ export async function search(ctx: Ctx<SearchRowRequest, SearchRowResponse>) {
limit: searchRequest.limit,
sort: searchRequest.sort ?? undefined,
sortOrder: searchRequest.sortOrder,
sortType: searchRequest.sortType ?? undefined,
countRows: searchRequest.countRows,
version: searchRequest.version,
disableEscaping: searchRequest.disableEscaping,

View File

@ -63,14 +63,12 @@ function getSortOptions(request: SearchViewRowRequest, view: ViewV2) {
return {
sort: request.sort,
sortOrder: request.sortOrder,
sortType: request.sortType ?? undefined,
}
}
if (view.sort) {
return {
sort: view.sort.field,
sortOrder: view.sort.order,
sortType: view.sort.type,
}
}

View File

@ -38,7 +38,7 @@ import {
import _ from "lodash"
import tk from "timekeeper"
import { encodeJSBinding } from "@budibase/string-templates"
import { dataFilters } from "@budibase/shared-core"
import { dataFilters, InMemorySearchQuery } from "@budibase/shared-core"
import { Knex } from "knex"
import { generator, structures, mocks } from "@budibase/backend-core/tests"
import { DEFAULT_EMPLOYEE_TABLE_SCHEMA } from "../../../db/defaultData/datasource_bb_default"
@ -200,31 +200,26 @@ if (descriptions.length) {
const isView = sourceType === "view"
class SearchAssertion {
constructor(private readonly query: SearchRowRequest) {}
constructor(
private readonly query: SearchRowRequest & {
sortType?: SortType
}
) {}
private async performSearch(): Promise<SearchResponse<Row>> {
if (isInMemory) {
const inMemoryQuery: RequiredKeys<
Omit<RowSearchParams, "tableId">
> = {
const inMemoryQuery: RequiredKeys<InMemorySearchQuery> = {
sort: this.query.sort ?? undefined,
query: { ...this.query.query },
paginate: this.query.paginate,
bookmark: this.query.bookmark ?? undefined,
limit: this.query.limit,
sortOrder: this.query.sortOrder,
sortType: this.query.sortType ?? undefined,
version: this.query.version,
disableEscaping: this.query.disableEscaping,
countRows: this.query.countRows,
viewId: undefined,
fields: undefined,
indexer: undefined,
rows: undefined,
}
return dataFilters.search(_.cloneDeep(rows), inMemoryQuery)
} else {
return config.api.row.search(tableOrViewId, this.query)
const { sortType, ...query } = this.query
return config.api.row.search(tableOrViewId, query)
}
}
@ -400,7 +395,9 @@ if (descriptions.length) {
}
}
function expectSearch(query: SearchRowRequest) {
function expectSearch(
query: SearchRowRequest & { sortType?: SortType }
) {
return new SearchAssertion(query)
}
@ -1119,25 +1116,26 @@ if (descriptions.length) {
}).toMatchExactly([{ name: "foo" }, { name: "bar" }])
})
describe("sortType STRING", () => {
it("sorts ascending", async () => {
await expectSearch({
query: {},
sort: "name",
sortType: SortType.STRING,
sortOrder: SortOrder.ASCENDING,
}).toMatchExactly([{ name: "bar" }, { name: "foo" }])
})
isInMemory &&
describe("sortType STRING", () => {
it("sorts ascending", async () => {
await expectSearch({
query: {},
sort: "name",
sortType: SortType.STRING,
sortOrder: SortOrder.ASCENDING,
}).toMatchExactly([{ name: "bar" }, { name: "foo" }])
})
it("sorts descending", async () => {
await expectSearch({
query: {},
sort: "name",
sortType: SortType.STRING,
sortOrder: SortOrder.DESCENDING,
}).toMatchExactly([{ name: "foo" }, { name: "bar" }])
it("sorts descending", async () => {
await expectSearch({
query: {},
sort: "name",
sortType: SortType.STRING,
sortOrder: SortOrder.DESCENDING,
}).toMatchExactly([{ name: "foo" }, { name: "bar" }])
})
})
})
!isInternal &&
!isInMemory &&
@ -1319,25 +1317,26 @@ if (descriptions.length) {
})
})
describe("sortType NUMBER", () => {
it("sorts ascending", async () => {
await expectSearch({
query: {},
sort: "age",
sortType: SortType.NUMBER,
sortOrder: SortOrder.ASCENDING,
}).toMatchExactly([{ age: 1 }, { age: 10 }])
})
isInMemory &&
describe("sortType NUMBER", () => {
it("sorts ascending", async () => {
await expectSearch({
query: {},
sort: "age",
sortType: SortType.NUMBER,
sortOrder: SortOrder.ASCENDING,
}).toMatchExactly([{ age: 1 }, { age: 10 }])
})
it("sorts descending", async () => {
await expectSearch({
query: {},
sort: "age",
sortType: SortType.NUMBER,
sortOrder: SortOrder.DESCENDING,
}).toMatchExactly([{ age: 10 }, { age: 1 }])
it("sorts descending", async () => {
await expectSearch({
query: {},
sort: "age",
sortType: SortType.NUMBER,
sortOrder: SortOrder.DESCENDING,
}).toMatchExactly([{ age: 10 }, { age: 1 }])
})
})
})
})
describe("dates", () => {
@ -1473,25 +1472,26 @@ if (descriptions.length) {
}).toMatchExactly([{ dob: JAN_10TH }, { dob: JAN_1ST }])
})
describe("sortType STRING", () => {
it("sorts ascending", async () => {
await expectSearch({
query: {},
sort: "dob",
sortType: SortType.STRING,
sortOrder: SortOrder.ASCENDING,
}).toMatchExactly([{ dob: JAN_1ST }, { dob: JAN_10TH }])
})
isInMemory &&
describe("sortType STRING", () => {
it("sorts ascending", async () => {
await expectSearch({
query: {},
sort: "dob",
sortType: SortType.STRING,
sortOrder: SortOrder.ASCENDING,
}).toMatchExactly([{ dob: JAN_1ST }, { dob: JAN_10TH }])
})
it("sorts descending", async () => {
await expectSearch({
query: {},
sort: "dob",
sortType: SortType.STRING,
sortOrder: SortOrder.DESCENDING,
}).toMatchExactly([{ dob: JAN_10TH }, { dob: JAN_1ST }])
it("sorts descending", async () => {
await expectSearch({
query: {},
sort: "dob",
sortType: SortType.STRING,
sortOrder: SortOrder.DESCENDING,
}).toMatchExactly([{ dob: JAN_10TH }, { dob: JAN_1ST }])
})
})
})
})
})
@ -1639,220 +1639,196 @@ if (descriptions.length) {
])
})
describe("sortType STRING", () => {
it("sorts ascending", async () => {
await expectSearch({
query: {},
sort: "time",
sortType: SortType.STRING,
sortOrder: SortOrder.ASCENDING,
}).toMatchExactly([
{ timeid: NULL_TIME__ID },
{ time: "00:00:00" },
{ time: "10:00:00" },
{ time: "10:45:00" },
{ time: "12:00:00" },
{ time: "15:30:00" },
])
})
it("sorts descending", async () => {
await expectSearch({
query: {},
sort: "time",
sortType: SortType.STRING,
sortOrder: SortOrder.DESCENDING,
}).toMatchExactly([
{ time: "15:30:00" },
{ time: "12:00:00" },
{ time: "10:45:00" },
{ time: "10:00:00" },
{ time: "00:00:00" },
{ timeid: NULL_TIME__ID },
])
})
})
})
})
describe("datetime - date only", () => {
describe.each([true, false])(
"saved with timestamp: %s",
saveWithTimestamp => {
describe.each([true, false])(
"search with timestamp: %s",
searchWithTimestamp => {
const SAVE_SUFFIX = saveWithTimestamp
? "T00:00:00.000Z"
: ""
const SEARCH_SUFFIX = searchWithTimestamp
? "T00:00:00.000Z"
: ""
const JAN_1ST = `2020-01-01`
const JAN_10TH = `2020-01-10`
const JAN_30TH = `2020-01-30`
const UNEXISTING_DATE = `2020-01-03`
const NULL_DATE__ID = `null_date__id`
beforeAll(async () => {
tableOrViewId = await createTableOrView({
dateid: {
name: "dateid",
type: FieldType.STRING,
},
date: {
name: "date",
type: FieldType.DATETIME,
dateOnly: true,
},
})
await createRows([
{ dateid: NULL_DATE__ID, date: null },
{ date: `${JAN_1ST}${SAVE_SUFFIX}` },
{ date: `${JAN_10TH}${SAVE_SUFFIX}` },
isInMemory &&
describe("sortType STRING", () => {
it("sorts ascending", async () => {
await expectSearch({
query: {},
sort: "time",
sortType: SortType.STRING,
sortOrder: SortOrder.ASCENDING,
}).toMatchExactly([
{ timeid: NULL_TIME__ID },
{ time: "00:00:00" },
{ time: "10:00:00" },
{ time: "10:45:00" },
{ time: "12:00:00" },
{ time: "15:30:00" },
])
})
describe("equal", () => {
it("successfully finds a row", async () => {
await expectQuery({
equal: { date: `${JAN_1ST}${SEARCH_SUFFIX}` },
}).toContainExactly([{ date: JAN_1ST }])
})
it("successfully finds an ISO8601 row", async () => {
await expectQuery({
equal: { date: `${JAN_10TH}${SEARCH_SUFFIX}` },
}).toContainExactly([{ date: JAN_10TH }])
})
it("finds a row with ISO8601 timestamp", async () => {
await expectQuery({
equal: { date: `${JAN_1ST}${SEARCH_SUFFIX}` },
}).toContainExactly([{ date: JAN_1ST }])
})
it("fails to find nonexistent row", async () => {
await expectQuery({
equal: {
date: `${UNEXISTING_DATE}${SEARCH_SUFFIX}`,
},
}).toFindNothing()
})
it("sorts descending", async () => {
await expectSearch({
query: {},
sort: "time",
sortType: SortType.STRING,
sortOrder: SortOrder.DESCENDING,
}).toMatchExactly([
{ time: "15:30:00" },
{ time: "12:00:00" },
{ time: "10:45:00" },
{ time: "10:00:00" },
{ time: "00:00:00" },
{ timeid: NULL_TIME__ID },
])
})
})
})
})
describe("notEqual", () => {
it("successfully finds a row", async () => {
await expectQuery({
notEqual: {
date: `${JAN_1ST}${SEARCH_SUFFIX}`,
!isInMemory &&
describe("datetime - date only", () => {
describe.each([true, false])(
"saved with timestamp: %s",
saveWithTimestamp => {
describe.each([true, false])(
"search with timestamp: %s",
searchWithTimestamp => {
const SAVE_SUFFIX = saveWithTimestamp
? "T00:00:00.000Z"
: ""
const SEARCH_SUFFIX = searchWithTimestamp
? "T00:00:00.000Z"
: ""
const JAN_1ST = `2020-01-01`
const JAN_10TH = `2020-01-10`
const JAN_30TH = `2020-01-30`
const UNEXISTING_DATE = `2020-01-03`
const NULL_DATE__ID = `null_date__id`
beforeAll(async () => {
tableOrViewId = await createTableOrView({
dateid: {
name: "dateid",
type: FieldType.STRING,
},
}).toContainExactly([
{ date: JAN_10TH },
{ dateid: NULL_DATE__ID },
date: {
name: "date",
type: FieldType.DATETIME,
dateOnly: true,
},
})
await createRows([
{ dateid: NULL_DATE__ID, date: null },
{ date: `${JAN_1ST}${SAVE_SUFFIX}` },
{ date: `${JAN_10TH}${SAVE_SUFFIX}` },
])
})
it("fails to find nonexistent row", async () => {
await expectQuery({
notEqual: {
date: `${JAN_30TH}${SEARCH_SUFFIX}`,
},
}).toContainExactly([
{ date: JAN_1ST },
{ date: JAN_10TH },
{ dateid: NULL_DATE__ID },
])
})
})
describe("equal", () => {
it("successfully finds a row", async () => {
await expectQuery({
equal: { date: `${JAN_1ST}${SEARCH_SUFFIX}` },
}).toContainExactly([{ date: JAN_1ST }])
})
describe("oneOf", () => {
it("successfully finds a row", async () => {
await expectQuery({
oneOf: { date: [`${JAN_1ST}${SEARCH_SUFFIX}`] },
}).toContainExactly([{ date: JAN_1ST }])
})
it("successfully finds an ISO8601 row", async () => {
await expectQuery({
equal: { date: `${JAN_10TH}${SEARCH_SUFFIX}` },
}).toContainExactly([{ date: JAN_10TH }])
})
it("fails to find nonexistent row", async () => {
await expectQuery({
oneOf: {
date: [`${UNEXISTING_DATE}${SEARCH_SUFFIX}`],
},
}).toFindNothing()
})
})
it("finds a row with ISO8601 timestamp", async () => {
await expectQuery({
equal: { date: `${JAN_1ST}${SEARCH_SUFFIX}` },
}).toContainExactly([{ date: JAN_1ST }])
})
describe("range", () => {
it("successfully finds a row", async () => {
await expectQuery({
range: {
date: {
low: `${JAN_1ST}${SEARCH_SUFFIX}`,
high: `${JAN_1ST}${SEARCH_SUFFIX}`,
it("fails to find nonexistent row", async () => {
await expectQuery({
equal: {
date: `${UNEXISTING_DATE}${SEARCH_SUFFIX}`,
},
},
}).toContainExactly([{ date: JAN_1ST }])
}).toFindNothing()
})
})
it("successfully finds multiple rows", async () => {
await expectQuery({
range: {
date: {
low: `${JAN_1ST}${SEARCH_SUFFIX}`,
high: `${JAN_10TH}${SEARCH_SUFFIX}`,
describe("notEqual", () => {
it("successfully finds a row", async () => {
await expectQuery({
notEqual: {
date: `${JAN_1ST}${SEARCH_SUFFIX}`,
},
},
}).toContainExactly([
{ date: JAN_1ST },
{ date: JAN_10TH },
])
})
}).toContainExactly([
{ date: JAN_10TH },
{ dateid: NULL_DATE__ID },
])
})
it("successfully finds no rows", async () => {
await expectQuery({
range: {
date: {
low: `${JAN_30TH}${SEARCH_SUFFIX}`,
high: `${JAN_30TH}${SEARCH_SUFFIX}`,
it("fails to find nonexistent row", async () => {
await expectQuery({
notEqual: {
date: `${JAN_30TH}${SEARCH_SUFFIX}`,
},
},
}).toFindNothing()
})
})
describe("sort", () => {
it("sorts ascending", async () => {
await expectSearch({
query: {},
sort: "date",
sortOrder: SortOrder.ASCENDING,
}).toMatchExactly([
{ dateid: NULL_DATE__ID },
{ date: JAN_1ST },
{ date: JAN_10TH },
])
}).toContainExactly([
{ date: JAN_1ST },
{ date: JAN_10TH },
{ dateid: NULL_DATE__ID },
])
})
})
it("sorts descending", async () => {
await expectSearch({
query: {},
sort: "date",
sortOrder: SortOrder.DESCENDING,
}).toMatchExactly([
{ date: JAN_10TH },
{ date: JAN_1ST },
{ dateid: NULL_DATE__ID },
])
describe("oneOf", () => {
it("successfully finds a row", async () => {
await expectQuery({
oneOf: { date: [`${JAN_1ST}${SEARCH_SUFFIX}`] },
}).toContainExactly([{ date: JAN_1ST }])
})
it("fails to find nonexistent row", async () => {
await expectQuery({
oneOf: {
date: [`${UNEXISTING_DATE}${SEARCH_SUFFIX}`],
},
}).toFindNothing()
})
})
describe("sortType STRING", () => {
describe("range", () => {
it("successfully finds a row", async () => {
await expectQuery({
range: {
date: {
low: `${JAN_1ST}${SEARCH_SUFFIX}`,
high: `${JAN_1ST}${SEARCH_SUFFIX}`,
},
},
}).toContainExactly([{ date: JAN_1ST }])
})
it("successfully finds multiple rows", async () => {
await expectQuery({
range: {
date: {
low: `${JAN_1ST}${SEARCH_SUFFIX}`,
high: `${JAN_10TH}${SEARCH_SUFFIX}`,
},
},
}).toContainExactly([
{ date: JAN_1ST },
{ date: JAN_10TH },
])
})
it("successfully finds no rows", async () => {
await expectQuery({
range: {
date: {
low: `${JAN_30TH}${SEARCH_SUFFIX}`,
high: `${JAN_30TH}${SEARCH_SUFFIX}`,
},
},
}).toFindNothing()
})
})
describe("sort", () => {
it("sorts ascending", async () => {
await expectSearch({
query: {},
sort: "date",
sortType: SortType.STRING,
sortOrder: SortOrder.ASCENDING,
}).toMatchExactly([
{ dateid: NULL_DATE__ID },
@ -1865,7 +1841,6 @@ if (descriptions.length) {
await expectSearch({
query: {},
sort: "date",
sortType: SortType.STRING,
sortOrder: SortOrder.DESCENDING,
}).toMatchExactly([
{ date: JAN_10TH },
@ -1873,13 +1848,41 @@ if (descriptions.length) {
{ dateid: NULL_DATE__ID },
])
})
isInMemory &&
describe("sortType STRING", () => {
it("sorts ascending", async () => {
await expectSearch({
query: {},
sort: "date",
sortType: SortType.STRING,
sortOrder: SortOrder.ASCENDING,
}).toMatchExactly([
{ dateid: NULL_DATE__ID },
{ date: JAN_1ST },
{ date: JAN_10TH },
])
})
it("sorts descending", async () => {
await expectSearch({
query: {},
sort: "date",
sortType: SortType.STRING,
sortOrder: SortOrder.DESCENDING,
}).toMatchExactly([
{ date: JAN_10TH },
{ date: JAN_1ST },
{ dateid: NULL_DATE__ID },
])
})
})
})
})
}
)
}
)
})
}
)
}
)
})
isInternal &&
!isInMemory &&

View File

@ -24,7 +24,6 @@ import {
SearchResponse,
SearchViewRowRequest,
SortOrder,
SortType,
StaticQuotaName,
Table,
TableSchema,
@ -154,7 +153,6 @@ if (descriptions.length) {
sort: {
field: "fieldToSort",
order: SortOrder.DESCENDING,
type: SortType.STRING,
},
schema: {
id: { visible: true },
@ -217,7 +215,6 @@ if (descriptions.length) {
sort: {
field: "fieldToSort",
order: SortOrder.DESCENDING,
type: SortType.STRING,
},
schema: {
id: { visible: true },
@ -1147,7 +1144,6 @@ if (descriptions.length) {
sort: {
field: generator.word(),
order: SortOrder.DESCENDING,
type: SortType.STRING,
},
schema: {
id: { visible: true },
@ -3153,7 +3149,6 @@ if (descriptions.length) {
{
field: string
order?: SortOrder
type?: SortType
},
string[]
][] = [
@ -3161,7 +3156,6 @@ if (descriptions.length) {
{
field: "name",
order: SortOrder.ASCENDING,
type: SortType.STRING,
},
["Alice", "Bob", "Charly", "Danny"],
],
@ -3178,22 +3172,6 @@ if (descriptions.length) {
},
["Danny", "Charly", "Bob", "Alice"],
],
[
{
field: "name",
order: SortOrder.DESCENDING,
type: SortType.STRING,
},
["Danny", "Charly", "Bob", "Alice"],
],
[
{
field: "age",
order: SortOrder.ASCENDING,
type: SortType.NUMBER,
},
["Danny", "Alice", "Charly", "Bob"],
],
[
{
field: "age",
@ -3204,15 +3182,13 @@ if (descriptions.length) {
[
{
field: "age",
order: SortOrder.DESCENDING,
},
["Bob", "Charly", "Alice", "Danny"],
["Danny", "Alice", "Charly", "Bob"],
],
[
{
field: "age",
order: SortOrder.DESCENDING,
type: SortType.NUMBER,
},
["Bob", "Charly", "Alice", "Danny"],
],
@ -3299,7 +3275,6 @@ if (descriptions.length) {
sort: {
field: "name",
order: SortOrder.ASCENDING,
type: SortType.STRING,
},
schema: viewSchema,
})
@ -3307,7 +3282,6 @@ if (descriptions.length) {
const response = await config.api.viewV2.search(view.id, {
sort: sortParams.field,
sortOrder: sortParams.order,
sortType: sortParams.type,
query: {},
})

View File

@ -46,7 +46,6 @@ export async function search(
query: options.query,
sort: options.sort,
sortOrder: options.sortOrder,
sortType: options.sortType,
limit: options.limit,
bookmark: options.bookmark,
paginate: options.paginate,

View File

@ -1,5 +1,6 @@
import {
Aggregation,
AutoFieldSubType,
CalculationType,
DocumentType,
EnrichedQueryJson,
@ -420,7 +421,11 @@ export async function search(
}
} else if (sortField) {
const sortType =
sortField.type === FieldType.NUMBER ? SortType.NUMBER : SortType.STRING
sortField.type === FieldType.NUMBER ||
(sortField.type === FieldType.AUTO &&
sortField.subtype === AutoFieldSubType.AUTO_ID)
? SortType.NUMBER
: SortType.STRING
request.sort = {
[mapToUserColumn(sortField.name)]: {
direction: params.sortOrder || SortOrder.ASCENDING,

View File

@ -11,7 +11,6 @@ import {
SortType,
FieldConstraints,
SortOrder,
RowSearchParams,
EmptyFilterOption,
SearchResponse,
Table,
@ -25,6 +24,8 @@ import {
isArraySearchOperator,
isRangeSearchOperator,
SearchFilter,
WithRequired,
SearchParams,
} from "@budibase/types"
import dayjs from "dayjs"
import { OperatorOptions, SqlNumberTypeRangeMap } from "./constants"
@ -521,9 +522,19 @@ export function fixupFilterArrays(filters: SearchFilters) {
return filters
}
type SearchQuery = WithRequired<
Pick<
SearchParams,
"query" | "sort" | "sortOrder" | "sortType" | "limit" | "countRows"
>,
"query"
>
export type InMemorySearchQuery = SearchQuery
export function search<T extends Record<string, any>>(
docs: T[],
query: Omit<RowSearchParams, "tableId">
query: SearchQuery
): SearchResponse<T> {
let result = runQuery(docs, query.query)
if (query.sort) {

View File

@ -1,5 +1,6 @@
export * from "./constants"
export * as dataFilters from "./filters"
export type * from "./filters"
export * as helpers from "./helpers"
export * as utils from "./utils"
export * as sdk from "./sdk"

View File

@ -8,11 +8,7 @@ import {
SearchFilterKey,
} from "../../../../sdk"
import { Row } from "../../../../documents"
import {
PaginationResponse,
SortOrder,
SortType,
} from "../../../../api/web/pagination"
import { PaginationResponse, SortOrder } from "../../../../api/web/pagination"
import { z } from "zod"
const fieldKey = z
@ -70,7 +66,6 @@ const searchRowRequest = z.object({
limit: z.number().optional(),
sort: z.string().nullish(),
sortOrder: z.nativeEnum(SortOrder).optional(),
sortType: z.nativeEnum(SortType).nullish(),
version: z.string().optional(),
disableEscaping: z.boolean().optional(),
countRows: z.boolean().optional(),
@ -83,7 +78,6 @@ export type SearchViewRowRequest = Pick<
SearchRowRequest,
| "sort"
| "sortOrder"
| "sortType"
| "limit"
| "bookmark"
| "paginate"

View File

@ -50,7 +50,7 @@ export interface SearchParams {
// when searching for rows we want a more extensive search type that requires certain properties
export interface RowSearchParams
extends WithRequired<SearchParams, "tableId" | "query"> {}
extends WithRequired<Omit<SearchParams, "sortType">, "tableId" | "query"> {}
export interface SearchResponse<T> {
rows: T[]

View File

@ -20499,10 +20499,10 @@ supports-preserve-symlinks-flag@^1.0.0:
resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09"
integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==
svelte-check@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/svelte-check/-/svelte-check-4.1.0.tgz#4389c1c88aa24f3d06fe0df94f9075a55017256d"
integrity sha512-AflEZYqI578KuDZcpcorPSf597LStxlkN7XqXi38u09zlHODVKd7c+7OuubGzbhgGRUqNTdQCZ+Ga96iRXEf2g==
svelte-check@^4.1.5:
version "4.1.5"
resolved "https://registry.yarnpkg.com/svelte-check/-/svelte-check-4.1.5.tgz#afdb3f8050c123064124d5aa7821365c7befa7a4"
integrity sha512-Gb0T2IqBNe1tLB9EB1Qh+LOe+JB8wt2/rNBDGvkxQVvk8vNeAoG+vZgFB/3P5+zC7RWlyBlzm9dVjZFph/maIg==
dependencies:
"@jridgewell/trace-mapping" "^0.3.25"
chokidar "^4.0.1"