Merge branch 'master' into fix/js-parsing-feedback
This commit is contained in:
commit
8b325ed826
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"$schema": "node_modules/lerna/schemas/lerna-schema.json",
|
||||
"version": "3.5.2",
|
||||
"version": "3.5.3",
|
||||
"npmClient": "yarn",
|
||||
"concurrency": 20,
|
||||
"command": {
|
||||
|
|
|
@ -147,9 +147,7 @@ export class FlagSet<T extends { [name: string]: boolean }> {
|
|||
|
||||
for (const [name, value] of Object.entries(posthogFlags)) {
|
||||
if (!this.isFlagName(name)) {
|
||||
// We don't want an unexpected PostHog flag to break the app, so we
|
||||
// just log it and continue.
|
||||
console.warn(`Unexpected posthog flag "${name}": ${value}`)
|
||||
// We don't want an unexpected PostHog flag to break the app
|
||||
continue
|
||||
}
|
||||
|
||||
|
|
|
@ -1,31 +1,36 @@
|
|||
<script>
|
||||
<script lang="ts">
|
||||
import { sdk } from "@budibase/shared-core"
|
||||
import { FieldType } from "@budibase/types"
|
||||
import RelationshipField from "./RelationshipField.svelte"
|
||||
|
||||
export let defaultValue
|
||||
export let defaultValue: string
|
||||
export let type = FieldType.BB_REFERENCE
|
||||
|
||||
function updateUserIDs(value) {
|
||||
function updateUserIDs(value: string | string[]) {
|
||||
if (Array.isArray(value)) {
|
||||
return value.map(val => sdk.users.getGlobalUserID(val))
|
||||
return value.map(val => sdk.users.getGlobalUserID(val)!)
|
||||
} else {
|
||||
return sdk.users.getGlobalUserID(value)
|
||||
}
|
||||
}
|
||||
|
||||
function updateReferences(value) {
|
||||
function updateReferences(value: string) {
|
||||
if (sdk.users.containsUserID(value)) {
|
||||
return updateUserIDs(value)
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
$: updatedDefaultValue = updateReferences(defaultValue)
|
||||
|
||||
// This cannot be typed, as svelte does not provide typed inheritance
|
||||
$: allProps = $$props as any
|
||||
</script>
|
||||
|
||||
<RelationshipField
|
||||
{...$$props}
|
||||
{...allProps}
|
||||
{type}
|
||||
datasourceType={"user"}
|
||||
primaryDisplay={"email"}
|
||||
defaultValue={updateReferences(defaultValue)}
|
||||
defaultValue={updatedDefaultValue}
|
||||
/>
|
||||
|
|
|
@ -1,43 +1,60 @@
|
|||
<script lang="ts">
|
||||
import { getContext, onDestroy } from "svelte"
|
||||
import type { Readable } from "svelte/store"
|
||||
import { writable } from "svelte/store"
|
||||
import { Icon } from "@budibase/bbui"
|
||||
import { memo } from "@budibase/frontend-core"
|
||||
import Placeholder from "../Placeholder.svelte"
|
||||
import InnerForm from "./InnerForm.svelte"
|
||||
import type { FieldApi } from "."
|
||||
import type { FieldSchema, FieldType } from "@budibase/types"
|
||||
import type {
|
||||
FieldApi,
|
||||
FieldState,
|
||||
FieldValidation,
|
||||
FormField,
|
||||
} from "@/types"
|
||||
|
||||
interface FieldInfo {
|
||||
field: string
|
||||
type: FieldType
|
||||
defaultValue: string | undefined
|
||||
disabled: boolean
|
||||
readonly: boolean
|
||||
validation?: FieldValidation
|
||||
formStep: number
|
||||
}
|
||||
|
||||
export let label: string | undefined = undefined
|
||||
export let field: string | undefined = undefined
|
||||
export let fieldState: any
|
||||
export let fieldApi: FieldApi
|
||||
export let fieldSchema: any
|
||||
export let fieldState: FieldState | undefined
|
||||
export let fieldApi: FieldApi | undefined
|
||||
export let fieldSchema: FieldSchema | undefined
|
||||
export let defaultValue: string | undefined = undefined
|
||||
export let type: any
|
||||
export let type: FieldType
|
||||
export let disabled = false
|
||||
export let readonly = false
|
||||
export let validation: any
|
||||
export let validation: FieldValidation | undefined
|
||||
export let span = 6
|
||||
export let helpText: string | undefined = undefined
|
||||
|
||||
// Get contexts
|
||||
const formContext: any = getContext("form")
|
||||
const formStepContext: any = getContext("form-step")
|
||||
const fieldGroupContext: any = getContext("field-group")
|
||||
const formContext = getContext("form")
|
||||
const formStepContext = getContext("form-step")
|
||||
const fieldGroupContext = getContext("field-group")
|
||||
const { styleable, builderStore, Provider } = getContext("sdk")
|
||||
const component: any = getContext("component")
|
||||
const component = getContext("component")
|
||||
|
||||
// Register field with form
|
||||
const formApi = formContext?.formApi
|
||||
const labelPos = fieldGroupContext?.labelPosition || "above"
|
||||
|
||||
let formField: any
|
||||
let formField: Readable<FormField> | undefined
|
||||
let touched = false
|
||||
let labelNode: any
|
||||
let labelNode: HTMLElement | undefined
|
||||
|
||||
// Memoize values required to register the field to avoid loops
|
||||
const formStep = formStepContext || writable(1)
|
||||
const fieldInfo = memo({
|
||||
const fieldInfo = memo<FieldInfo>({
|
||||
field: field || $component.name,
|
||||
type,
|
||||
defaultValue,
|
||||
|
@ -66,16 +83,22 @@
|
|||
$: $component.editing && labelNode?.focus()
|
||||
|
||||
// Update form properties in parent component on every store change
|
||||
$: unsubscribe = formField?.subscribe((value: any) => {
|
||||
fieldState = value?.fieldState
|
||||
fieldApi = value?.fieldApi
|
||||
fieldSchema = value?.fieldSchema
|
||||
})
|
||||
$: unsubscribe = formField?.subscribe(
|
||||
(value?: {
|
||||
fieldState: FieldState
|
||||
fieldApi: FieldApi
|
||||
fieldSchema: FieldSchema
|
||||
}) => {
|
||||
fieldState = value?.fieldState
|
||||
fieldApi = value?.fieldApi
|
||||
fieldSchema = value?.fieldSchema
|
||||
}
|
||||
)
|
||||
|
||||
// Determine label class from position
|
||||
$: labelClass = labelPos === "above" ? "" : `spectrum-FieldLabel--${labelPos}`
|
||||
|
||||
const registerField = (info: any) => {
|
||||
const registerField = (info: FieldInfo) => {
|
||||
formField = formApi?.registerField(
|
||||
info.field,
|
||||
info.type,
|
||||
|
@ -87,10 +110,10 @@
|
|||
)
|
||||
}
|
||||
|
||||
const updateLabel = (e: any) => {
|
||||
const updateLabel = (e: Event) => {
|
||||
if (touched) {
|
||||
// @ts-expect-error and TODO updateProp isn't recognised - need builder TS conversion
|
||||
builderStore.actions.updateProp("label", e.target.textContent)
|
||||
const label = e.target as HTMLLabelElement
|
||||
builderStore.actions.updateProp("label", label.textContent)
|
||||
}
|
||||
touched = false
|
||||
}
|
||||
|
|
|
@ -9,17 +9,19 @@
|
|||
RelationshipFieldMetadata,
|
||||
Row,
|
||||
} from "@budibase/types"
|
||||
import type { FieldApi, FieldState } from "."
|
||||
import type { FieldApi, FieldState, FieldValidation } from "@/types"
|
||||
|
||||
type ValueType = string | string[]
|
||||
|
||||
export let field: string | undefined = undefined
|
||||
export let label: string | undefined = undefined
|
||||
export let placeholder: string | undefined = undefined
|
||||
export let disabled: boolean = false
|
||||
export let readonly: boolean = false
|
||||
export let validation: any
|
||||
export let validation: FieldValidation | undefined = undefined
|
||||
export let autocomplete: boolean = true
|
||||
export let defaultValue: string | string[] | undefined = undefined
|
||||
export let onChange: any
|
||||
export let defaultValue: ValueType | undefined = undefined
|
||||
export let onChange: (_props: { value: ValueType }) => void
|
||||
export let filter: SearchFilter[]
|
||||
export let datasourceType: "table" | "user" = "table"
|
||||
export let primaryDisplay: string | undefined = undefined
|
||||
|
@ -88,14 +90,14 @@
|
|||
// Ensure backwards compatibility
|
||||
$: enrichedDefaultValue = enrichDefaultValue(defaultValue)
|
||||
|
||||
$: emptyValue = multiselect ? [] : undefined
|
||||
// We need to cast value to pass it down, as those components aren't typed
|
||||
$: emptyValue = multiselect ? [] : null
|
||||
$: displayValue = missingIDs.length ? emptyValue : (selectedValue as any)
|
||||
$: displayValue = (missingIDs.length ? emptyValue : selectedValue) as any
|
||||
|
||||
// Ensures that we flatten any objects so that only the IDs of the selected
|
||||
// rows are passed down. Not sure how this can be an object to begin with?
|
||||
const parseSelectedValue = (
|
||||
value: any,
|
||||
value: ValueType | undefined,
|
||||
multiselect: boolean
|
||||
): undefined | string | string[] => {
|
||||
return multiselect ? flatten(value) : flatten(value)[0]
|
||||
|
@ -140,7 +142,7 @@
|
|||
|
||||
// Builds a map of all available options, in a consistent structure
|
||||
const processOptions = (
|
||||
realValue: any | any[],
|
||||
realValue: ValueType | undefined,
|
||||
rows: Row[],
|
||||
primaryDisplay?: string
|
||||
) => {
|
||||
|
@ -171,7 +173,7 @@
|
|||
|
||||
// Parses a row-like structure into a properly shaped option
|
||||
const parseOption = (
|
||||
option: any | BasicRelatedRow | Row,
|
||||
option: string | BasicRelatedRow | Row,
|
||||
primaryDisplay?: string
|
||||
): BasicRelatedRow | null => {
|
||||
if (!option || typeof option !== "object" || !option?._id) {
|
||||
|
|
|
@ -19,15 +19,3 @@ export { default as codescanner } from "./CodeScannerField.svelte"
|
|||
export { default as signaturesinglefield } from "./SignatureField.svelte"
|
||||
export { default as bbreferencefield } from "./BBReferenceField.svelte"
|
||||
export { default as bbreferencesinglefield } from "./BBReferenceSingleField.svelte"
|
||||
|
||||
export interface FieldApi {
|
||||
setValue(value: any): boolean
|
||||
deregister(): void
|
||||
}
|
||||
|
||||
export interface FieldState<T> {
|
||||
value: T
|
||||
fieldId: string
|
||||
disabled: boolean
|
||||
readonly: boolean
|
||||
}
|
||||
|
|
|
@ -1,7 +1,12 @@
|
|||
import { Component, Context, SDK } from "."
|
||||
import { Writable } from "svelte"
|
||||
import { Component, FieldGroupContext, FormContext, SDK } from "@/types"
|
||||
import { Readable } from "svelte/store"
|
||||
|
||||
declare module "svelte" {
|
||||
export function getContext(key: "sdk"): SDK
|
||||
export function getContext(key: "component"): Component
|
||||
export function getContext(key: "context"): Context
|
||||
export function getContext(key: "context"): Readable<Record<string, any>>
|
||||
export function getContext(key: "form"): FormContext | undefined
|
||||
export function getContext(key: "form-step"): Writable<number> | undefined
|
||||
export function getContext(key: "field-group"): FieldGroupContext | undefined
|
||||
}
|
||||
|
|
|
@ -14,8 +14,6 @@ import {
|
|||
} from "@/stores"
|
||||
import { get } from "svelte/store"
|
||||
import { initWebsocket } from "@/websocket"
|
||||
import { APIClient } from "@budibase/frontend-core"
|
||||
import type { ActionTypes } from "@/constants"
|
||||
import { Readable } from "svelte/store"
|
||||
import {
|
||||
Screen,
|
||||
|
@ -39,6 +37,7 @@ window.svelte = svelte
|
|||
// Initialise spectrum icons
|
||||
// eslint-disable-next-line local-rules/no-budibase-imports
|
||||
import loadSpectrumIcons from "@budibase/bbui/spectrum-icons-vite.js"
|
||||
|
||||
loadSpectrumIcons()
|
||||
|
||||
// Extend global window scope
|
||||
|
@ -73,32 +72,6 @@ declare global {
|
|||
}
|
||||
}
|
||||
|
||||
export interface SDK {
|
||||
API: APIClient
|
||||
styleable: any
|
||||
Provider: any
|
||||
ActionTypes: typeof ActionTypes
|
||||
fetchDatasourceSchema: any
|
||||
generateGoldenSample: any
|
||||
builderStore: Readable<{
|
||||
inBuilder: boolean
|
||||
}> & {
|
||||
actions: {
|
||||
highlightSetting: (key: string) => void
|
||||
addParentComponent: (
|
||||
componentId: string,
|
||||
fullAncestorType: string
|
||||
) => void
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export type Component = Readable<{
|
||||
id: string
|
||||
styles: any
|
||||
errorState: boolean
|
||||
}>
|
||||
|
||||
export type Context = Readable<Record<string, any>>
|
||||
|
||||
let app: ClientApp
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
import { Readable } from "svelte/store"
|
||||
|
||||
export type Component = Readable<{
|
||||
id: string
|
||||
name: string
|
||||
styles: any
|
||||
editing: boolean
|
||||
errorState: boolean
|
||||
}>
|
|
@ -0,0 +1,3 @@
|
|||
export interface FieldGroupContext {
|
||||
labelPosition: string
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
import { Readable } from "svelte/store"
|
||||
import { FieldSchema, FieldType } from "@budibase/types"
|
||||
|
||||
export interface FormContext {
|
||||
formApi?: {
|
||||
registerField: (
|
||||
field: string,
|
||||
type: FieldType,
|
||||
defaultValue: string | undefined,
|
||||
disabled: boolean,
|
||||
readonly: boolean,
|
||||
validation: FieldValidation | undefined,
|
||||
formStep: number
|
||||
) => Readable<FormField>
|
||||
}
|
||||
}
|
||||
|
||||
export type FieldValidation = () => string | undefined
|
||||
|
||||
export interface FormField {
|
||||
fieldState: FieldState
|
||||
fieldApi: FieldApi
|
||||
fieldSchema: FieldSchema
|
||||
}
|
||||
|
||||
export interface FieldApi {
|
||||
setValue(value: any): boolean
|
||||
deregister(): void
|
||||
}
|
||||
|
||||
export interface FieldState<T = any> {
|
||||
value: T
|
||||
fieldId: string
|
||||
disabled: boolean
|
||||
readonly: boolean
|
||||
error?: string
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
export * from "./components"
|
||||
export * from "./fields"
|
||||
export * from "./forms"
|
||||
export * from "./sdk"
|
|
@ -0,0 +1,24 @@
|
|||
import { ActionTypes } from "@/constants"
|
||||
import { APIClient } from "@budibase/frontend-core"
|
||||
import { Readable } from "svelte/store"
|
||||
|
||||
export interface SDK {
|
||||
API: APIClient
|
||||
styleable: any
|
||||
Provider: any
|
||||
ActionTypes: typeof ActionTypes
|
||||
fetchDatasourceSchema: any
|
||||
generateGoldenSample: any
|
||||
builderStore: Readable<{
|
||||
inBuilder: boolean
|
||||
}> & {
|
||||
actions: {
|
||||
highlightSetting: (key: string) => void
|
||||
addParentComponent: (
|
||||
componentId: string,
|
||||
fullAncestorType: string
|
||||
) => void
|
||||
updateProp: (key: string, value: any) => void
|
||||
}
|
||||
}
|
||||
}
|
|
@ -8,6 +8,7 @@ import {
|
|||
Row,
|
||||
SearchFilters,
|
||||
SortOrder,
|
||||
SortType,
|
||||
TableSchema,
|
||||
} from "@budibase/types"
|
||||
import { APIClient } from "../api/types"
|
||||
|
@ -71,6 +72,8 @@ export default abstract class BaseDataFetch<
|
|||
options: DataFetchOptions<TQuery> & {
|
||||
datasource: TDatasource
|
||||
|
||||
sortType: SortType | null
|
||||
|
||||
// Client side feature customisation
|
||||
clientSideSearching: boolean
|
||||
clientSideSorting: boolean
|
||||
|
@ -103,6 +106,7 @@ export default abstract class BaseDataFetch<
|
|||
// Sorting config
|
||||
sortColumn: null,
|
||||
sortOrder: SortOrder.ASCENDING,
|
||||
sortType: null,
|
||||
|
||||
// Pagination config
|
||||
paginate: true,
|
||||
|
@ -223,12 +227,31 @@ export default abstract class BaseDataFetch<
|
|||
this.options.sortColumn = this.getDefaultSortColumn(definition, schema)
|
||||
}
|
||||
|
||||
// If no sort order, default to ascending
|
||||
if (!this.options.sortOrder) {
|
||||
// If we don't have a sort column specified then just ensure we don't set
|
||||
// any sorting params
|
||||
if (!this.options.sortColumn) {
|
||||
this.options.sortOrder = SortOrder.ASCENDING
|
||||
this.options.sortType = null
|
||||
} else {
|
||||
// Ensure sortOrder matches the enum
|
||||
this.options.sortOrder = this.options.sortOrder.toLowerCase() as SortOrder
|
||||
// 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
|
||||
}
|
||||
}
|
||||
|
||||
// Build the query
|
||||
|
@ -271,6 +294,7 @@ export default abstract class BaseDataFetch<
|
|||
const {
|
||||
sortColumn,
|
||||
sortOrder,
|
||||
sortType,
|
||||
limit,
|
||||
clientSideSearching,
|
||||
clientSideSorting,
|
||||
|
@ -287,8 +311,8 @@ export default abstract class BaseDataFetch<
|
|||
}
|
||||
|
||||
// If we don't support sorting, do a client-side sort
|
||||
if (!this.features.supportsSort && clientSideSorting && sortColumn) {
|
||||
rows = sort(rows, sortColumn, sortOrder)
|
||||
if (!this.features.supportsSort && clientSideSorting && sortType) {
|
||||
rows = sort(rows, sortColumn as any, sortOrder, sortType)
|
||||
}
|
||||
|
||||
// If we don't support pagination, do a client-side limit
|
||||
|
|
|
@ -29,7 +29,8 @@ export default class TableFetch extends BaseDataFetch<TableDatasource, Table> {
|
|||
}
|
||||
|
||||
async getData() {
|
||||
const { datasource, limit, sortColumn, sortOrder, paginate } = this.options
|
||||
const { datasource, limit, sortColumn, sortOrder, sortType, paginate } =
|
||||
this.options
|
||||
const { tableId } = datasource
|
||||
const { cursor, query } = get(this.store)
|
||||
|
||||
|
@ -40,6 +41,7 @@ export default class TableFetch extends BaseDataFetch<TableDatasource, Table> {
|
|||
limit,
|
||||
sort: sortColumn,
|
||||
sortOrder: sortOrder ?? SortOrder.ASCENDING,
|
||||
sortType,
|
||||
paginate,
|
||||
bookmark: cursor,
|
||||
})
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import {
|
||||
SearchViewRowRequest,
|
||||
SortOrder,
|
||||
ViewDatasource,
|
||||
ViewV2Enriched,
|
||||
|
@ -41,7 +40,8 @@ export default class ViewV2Fetch extends BaseDataFetch<
|
|||
}
|
||||
|
||||
async getData() {
|
||||
const { datasource, limit, sortColumn, sortOrder, paginate } = this.options
|
||||
const { datasource, limit, sortColumn, sortOrder, sortType, paginate } =
|
||||
this.options
|
||||
const { cursor, query, definition } = get(this.store)
|
||||
|
||||
// If this is a calculation view and we have no calculations, return nothing
|
||||
|
@ -68,13 +68,14 @@ export default class ViewV2Fetch extends BaseDataFetch<
|
|||
}
|
||||
|
||||
try {
|
||||
const request: SearchViewRowRequest = {
|
||||
const request = {
|
||||
query,
|
||||
paginate,
|
||||
limit,
|
||||
bookmark: cursor,
|
||||
sort: sortColumn,
|
||||
sortOrder: sortOrder,
|
||||
sortType,
|
||||
}
|
||||
if (paginate) {
|
||||
const res = await this.API.viewV2.fetch(datasource.id, {
|
||||
|
|
|
@ -263,6 +263,7 @@ 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,
|
||||
|
|
|
@ -63,12 +63,14 @@ 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,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -38,7 +38,7 @@ import {
|
|||
import _ from "lodash"
|
||||
import tk from "timekeeper"
|
||||
import { encodeJSBinding } from "@budibase/string-templates"
|
||||
import { dataFilters, InMemorySearchQuery } from "@budibase/shared-core"
|
||||
import { dataFilters } 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,26 +200,31 @@ if (descriptions.length) {
|
|||
const isView = sourceType === "view"
|
||||
|
||||
class SearchAssertion {
|
||||
constructor(
|
||||
private readonly query: SearchRowRequest & {
|
||||
sortType?: SortType
|
||||
}
|
||||
) {}
|
||||
constructor(private readonly query: SearchRowRequest) {}
|
||||
|
||||
private async performSearch(): Promise<SearchResponse<Row>> {
|
||||
if (isInMemory) {
|
||||
const inMemoryQuery: RequiredKeys<InMemorySearchQuery> = {
|
||||
const inMemoryQuery: RequiredKeys<
|
||||
Omit<RowSearchParams, "tableId">
|
||||
> = {
|
||||
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 {
|
||||
const { sortType, ...query } = this.query
|
||||
return config.api.row.search(tableOrViewId, query)
|
||||
return config.api.row.search(tableOrViewId, this.query)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -395,9 +400,7 @@ if (descriptions.length) {
|
|||
}
|
||||
}
|
||||
|
||||
function expectSearch(
|
||||
query: SearchRowRequest & { sortType?: SortType }
|
||||
) {
|
||||
function expectSearch(query: SearchRowRequest) {
|
||||
return new SearchAssertion(query)
|
||||
}
|
||||
|
||||
|
@ -1116,27 +1119,26 @@ if (descriptions.length) {
|
|||
}).toMatchExactly([{ name: "foo" }, { name: "bar" }])
|
||||
})
|
||||
|
||||
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" }])
|
||||
})
|
||||
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" }])
|
||||
})
|
||||
})
|
||||
|
||||
!isInternal &&
|
||||
!isInMemory &&
|
||||
// This test was added because we automatically add in a sort by the
|
||||
|
@ -1317,26 +1319,25 @@ if (descriptions.length) {
|
|||
})
|
||||
})
|
||||
|
||||
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 }])
|
||||
})
|
||||
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 }])
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe("dates", () => {
|
||||
|
@ -1472,26 +1473,25 @@ if (descriptions.length) {
|
|||
}).toMatchExactly([{ dob: JAN_10TH }, { dob: JAN_1ST }])
|
||||
})
|
||||
|
||||
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 }])
|
||||
})
|
||||
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 }])
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
|
@ -1639,196 +1639,220 @@ if (descriptions.length) {
|
|||
])
|
||||
})
|
||||
|
||||
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" },
|
||||
])
|
||||
})
|
||||
|
||||
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("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 },
|
||||
])
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
!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"
|
||||
: ""
|
||||
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`
|
||||
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,
|
||||
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}` },
|
||||
])
|
||||
})
|
||||
|
||||
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}`,
|
||||
},
|
||||
date: {
|
||||
name: "date",
|
||||
type: FieldType.DATETIME,
|
||||
dateOnly: true,
|
||||
},
|
||||
})
|
||||
}).toFindNothing()
|
||||
})
|
||||
})
|
||||
|
||||
await createRows([
|
||||
{ dateid: NULL_DATE__ID, date: null },
|
||||
{ date: `${JAN_1ST}${SAVE_SUFFIX}` },
|
||||
{ date: `${JAN_10TH}${SAVE_SUFFIX}` },
|
||||
describe("notEqual", () => {
|
||||
it("successfully finds a row", async () => {
|
||||
await expectQuery({
|
||||
notEqual: {
|
||||
date: `${JAN_1ST}${SEARCH_SUFFIX}`,
|
||||
},
|
||||
}).toContainExactly([
|
||||
{ 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 }])
|
||||
})
|
||||
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 },
|
||||
])
|
||||
})
|
||||
})
|
||||
|
||||
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()
|
||||
})
|
||||
describe("oneOf", () => {
|
||||
it("successfully finds a row", async () => {
|
||||
await expectQuery({
|
||||
oneOf: { date: [`${JAN_1ST}${SEARCH_SUFFIX}`] },
|
||||
}).toContainExactly([{ date: JAN_1ST }])
|
||||
})
|
||||
|
||||
describe("notEqual", () => {
|
||||
it("successfully finds a row", async () => {
|
||||
await expectQuery({
|
||||
notEqual: {
|
||||
date: `${JAN_1ST}${SEARCH_SUFFIX}`,
|
||||
},
|
||||
}).toContainExactly([
|
||||
{ date: JAN_10TH },
|
||||
{ dateid: NULL_DATE__ID },
|
||||
])
|
||||
})
|
||||
it("fails to find nonexistent row", async () => {
|
||||
await expectQuery({
|
||||
oneOf: {
|
||||
date: [`${UNEXISTING_DATE}${SEARCH_SUFFIX}`],
|
||||
},
|
||||
}).toFindNothing()
|
||||
})
|
||||
})
|
||||
|
||||
it("fails to find nonexistent row", async () => {
|
||||
await expectQuery({
|
||||
notEqual: {
|
||||
date: `${JAN_30TH}${SEARCH_SUFFIX}`,
|
||||
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 },
|
||||
{ date: JAN_10TH },
|
||||
{ dateid: NULL_DATE__ID },
|
||||
])
|
||||
})
|
||||
},
|
||||
}).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("fails to find nonexistent row", async () => {
|
||||
await expectQuery({
|
||||
oneOf: {
|
||||
date: [`${UNEXISTING_DATE}${SEARCH_SUFFIX}`],
|
||||
it("successfully finds multiple rows", async () => {
|
||||
await expectQuery({
|
||||
range: {
|
||||
date: {
|
||||
low: `${JAN_1ST}${SEARCH_SUFFIX}`,
|
||||
high: `${JAN_10TH}${SEARCH_SUFFIX}`,
|
||||
},
|
||||
}).toFindNothing()
|
||||
})
|
||||
},
|
||||
}).toContainExactly([
|
||||
{ date: JAN_1ST },
|
||||
{ date: JAN_10TH },
|
||||
])
|
||||
})
|
||||
|
||||
describe("range", () => {
|
||||
it("successfully finds a row", async () => {
|
||||
await expectQuery({
|
||||
range: {
|
||||
date: {
|
||||
low: `${JAN_1ST}${SEARCH_SUFFIX}`,
|
||||
high: `${JAN_1ST}${SEARCH_SUFFIX}`,
|
||||
},
|
||||
it("successfully finds no rows", async () => {
|
||||
await expectQuery({
|
||||
range: {
|
||||
date: {
|
||||
low: `${JAN_30TH}${SEARCH_SUFFIX}`,
|
||||
high: `${JAN_30TH}${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}`,
|
||||
},
|
||||
},
|
||||
}).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",
|
||||
sortOrder: SortOrder.ASCENDING,
|
||||
}).toMatchExactly([
|
||||
{ dateid: NULL_DATE__ID },
|
||||
{ date: JAN_1ST },
|
||||
{ date: JAN_10TH },
|
||||
])
|
||||
})
|
||||
|
||||
describe("sort", () => {
|
||||
it("sorts descending", async () => {
|
||||
await expectSearch({
|
||||
query: {},
|
||||
sort: "date",
|
||||
sortOrder: SortOrder.DESCENDING,
|
||||
}).toMatchExactly([
|
||||
{ date: JAN_10TH },
|
||||
{ date: JAN_1ST },
|
||||
{ dateid: NULL_DATE__ID },
|
||||
])
|
||||
})
|
||||
|
||||
describe("sortType STRING", () => {
|
||||
it("sorts ascending", async () => {
|
||||
await expectSearch({
|
||||
query: {},
|
||||
sort: "date",
|
||||
sortType: SortType.STRING,
|
||||
sortOrder: SortOrder.ASCENDING,
|
||||
}).toMatchExactly([
|
||||
{ dateid: NULL_DATE__ID },
|
||||
|
@ -1841,6 +1865,7 @@ if (descriptions.length) {
|
|||
await expectSearch({
|
||||
query: {},
|
||||
sort: "date",
|
||||
sortType: SortType.STRING,
|
||||
sortOrder: SortOrder.DESCENDING,
|
||||
}).toMatchExactly([
|
||||
{ date: JAN_10TH },
|
||||
|
@ -1848,41 +1873,13 @@ 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 &&
|
||||
|
|
|
@ -24,6 +24,7 @@ import {
|
|||
SearchResponse,
|
||||
SearchViewRowRequest,
|
||||
SortOrder,
|
||||
SortType,
|
||||
StaticQuotaName,
|
||||
Table,
|
||||
TableSchema,
|
||||
|
@ -153,6 +154,7 @@ if (descriptions.length) {
|
|||
sort: {
|
||||
field: "fieldToSort",
|
||||
order: SortOrder.DESCENDING,
|
||||
type: SortType.STRING,
|
||||
},
|
||||
schema: {
|
||||
id: { visible: true },
|
||||
|
@ -215,6 +217,7 @@ if (descriptions.length) {
|
|||
sort: {
|
||||
field: "fieldToSort",
|
||||
order: SortOrder.DESCENDING,
|
||||
type: SortType.STRING,
|
||||
},
|
||||
schema: {
|
||||
id: { visible: true },
|
||||
|
@ -1144,6 +1147,7 @@ if (descriptions.length) {
|
|||
sort: {
|
||||
field: generator.word(),
|
||||
order: SortOrder.DESCENDING,
|
||||
type: SortType.STRING,
|
||||
},
|
||||
schema: {
|
||||
id: { visible: true },
|
||||
|
@ -3149,6 +3153,7 @@ if (descriptions.length) {
|
|||
{
|
||||
field: string
|
||||
order?: SortOrder
|
||||
type?: SortType
|
||||
},
|
||||
string[]
|
||||
][] = [
|
||||
|
@ -3156,6 +3161,7 @@ if (descriptions.length) {
|
|||
{
|
||||
field: "name",
|
||||
order: SortOrder.ASCENDING,
|
||||
type: SortType.STRING,
|
||||
},
|
||||
["Alice", "Bob", "Charly", "Danny"],
|
||||
],
|
||||
|
@ -3172,6 +3178,22 @@ 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",
|
||||
|
@ -3182,13 +3204,15 @@ if (descriptions.length) {
|
|||
[
|
||||
{
|
||||
field: "age",
|
||||
order: SortOrder.DESCENDING,
|
||||
},
|
||||
["Danny", "Alice", "Charly", "Bob"],
|
||||
["Bob", "Charly", "Alice", "Danny"],
|
||||
],
|
||||
[
|
||||
{
|
||||
field: "age",
|
||||
order: SortOrder.DESCENDING,
|
||||
type: SortType.NUMBER,
|
||||
},
|
||||
["Bob", "Charly", "Alice", "Danny"],
|
||||
],
|
||||
|
@ -3275,6 +3299,7 @@ if (descriptions.length) {
|
|||
sort: {
|
||||
field: "name",
|
||||
order: SortOrder.ASCENDING,
|
||||
type: SortType.STRING,
|
||||
},
|
||||
schema: viewSchema,
|
||||
})
|
||||
|
@ -3282,6 +3307,7 @@ if (descriptions.length) {
|
|||
const response = await config.api.viewV2.search(view.id, {
|
||||
sort: sortParams.field,
|
||||
sortOrder: sortParams.order,
|
||||
sortType: sortParams.type,
|
||||
query: {},
|
||||
})
|
||||
|
||||
|
|
|
@ -46,6 +46,7 @@ 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,
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import {
|
||||
Aggregation,
|
||||
AutoFieldSubType,
|
||||
CalculationType,
|
||||
DocumentType,
|
||||
EnrichedQueryJson,
|
||||
|
@ -421,11 +420,7 @@ export async function search(
|
|||
}
|
||||
} else if (sortField) {
|
||||
const sortType =
|
||||
sortField.type === FieldType.NUMBER ||
|
||||
(sortField.type === FieldType.AUTO &&
|
||||
sortField.subtype === AutoFieldSubType.AUTO_ID)
|
||||
? SortType.NUMBER
|
||||
: SortType.STRING
|
||||
sortField.type === FieldType.NUMBER ? SortType.NUMBER : SortType.STRING
|
||||
request.sort = {
|
||||
[mapToUserColumn(sortField.name)]: {
|
||||
direction: params.sortOrder || SortOrder.ASCENDING,
|
||||
|
|
|
@ -11,6 +11,7 @@ import {
|
|||
SortType,
|
||||
FieldConstraints,
|
||||
SortOrder,
|
||||
RowSearchParams,
|
||||
EmptyFilterOption,
|
||||
SearchResponse,
|
||||
Table,
|
||||
|
@ -24,8 +25,6 @@ import {
|
|||
isArraySearchOperator,
|
||||
isRangeSearchOperator,
|
||||
SearchFilter,
|
||||
WithRequired,
|
||||
SearchParams,
|
||||
} from "@budibase/types"
|
||||
import dayjs from "dayjs"
|
||||
import { OperatorOptions, SqlNumberTypeRangeMap } from "./constants"
|
||||
|
@ -522,19 +521,9 @@ 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: SearchQuery
|
||||
query: Omit<RowSearchParams, "tableId">
|
||||
): SearchResponse<T> {
|
||||
let result = runQuery(docs, query.query)
|
||||
if (query.sort) {
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
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"
|
||||
|
|
|
@ -8,7 +8,11 @@ import {
|
|||
SearchFilterKey,
|
||||
} from "../../../../sdk"
|
||||
import { Row } from "../../../../documents"
|
||||
import { PaginationResponse, SortOrder } from "../../../../api/web/pagination"
|
||||
import {
|
||||
PaginationResponse,
|
||||
SortOrder,
|
||||
SortType,
|
||||
} from "../../../../api/web/pagination"
|
||||
import { z } from "zod"
|
||||
|
||||
const fieldKey = z
|
||||
|
@ -66,6 +70,7 @@ 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(),
|
||||
|
@ -78,6 +83,7 @@ export type SearchViewRowRequest = Pick<
|
|||
SearchRowRequest,
|
||||
| "sort"
|
||||
| "sortOrder"
|
||||
| "sortType"
|
||||
| "limit"
|
||||
| "bookmark"
|
||||
| "paginate"
|
||||
|
|
|
@ -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<Omit<SearchParams, "sortType">, "tableId" | "query"> {}
|
||||
extends WithRequired<SearchParams, "tableId" | "query"> {}
|
||||
|
||||
export interface SearchResponse<T> {
|
||||
rows: T[]
|
||||
|
|
Loading…
Reference in New Issue