Refactor SearchFilterGroup type to be less verbose and more clearly named.
This commit is contained in:
parent
714029b9a0
commit
b7979f4719
|
@ -27,20 +27,18 @@ import {
|
|||
ViewV2Schema,
|
||||
ViewV2Type,
|
||||
JsonTypes,
|
||||
FilterGroupLogicalOperator,
|
||||
UILogicalOperator,
|
||||
EmptyFilterOption,
|
||||
JsonFieldSubType,
|
||||
SearchFilterGroup,
|
||||
UISearchFilter,
|
||||
LegacyFilter,
|
||||
SearchViewRowRequest,
|
||||
SearchFilterChild,
|
||||
} from "@budibase/types"
|
||||
import { generator, mocks } from "@budibase/backend-core/tests"
|
||||
import { DatabaseName, getDatasource } from "../../../integrations/tests/utils"
|
||||
import merge from "lodash/merge"
|
||||
import { quotas } from "@budibase/pro"
|
||||
import { db, roles, features } from "@budibase/backend-core"
|
||||
import { single } from "validate.js"
|
||||
|
||||
describe.each([
|
||||
["lucene", undefined],
|
||||
|
@ -158,11 +156,11 @@ describe.each([
|
|||
tableId: table._id!,
|
||||
primaryDisplay: "id",
|
||||
queryUI: {
|
||||
logicalOperator: FilterGroupLogicalOperator.ALL,
|
||||
logicalOperator: UILogicalOperator.ALL,
|
||||
onEmptyFilter: EmptyFilterOption.RETURN_ALL,
|
||||
groups: [
|
||||
{
|
||||
logicalOperator: FilterGroupLogicalOperator.ALL,
|
||||
logicalOperator: UILogicalOperator.ALL,
|
||||
filters: [
|
||||
{
|
||||
operator: BasicOperator.EQUAL,
|
||||
|
@ -2388,10 +2386,10 @@ describe.each([
|
|||
name: generator.guid(),
|
||||
queryUI: {
|
||||
onEmptyFilter: EmptyFilterOption.RETURN_ALL,
|
||||
logicalOperator: FilterGroupLogicalOperator.ALL,
|
||||
logicalOperator: UILogicalOperator.ALL,
|
||||
groups: [
|
||||
{
|
||||
logicalOperator: FilterGroupLogicalOperator.ALL,
|
||||
logicalOperator: UILogicalOperator.ALL,
|
||||
filters: [
|
||||
{
|
||||
operator: BasicOperator.EQUAL,
|
||||
|
@ -2452,10 +2450,10 @@ describe.each([
|
|||
name: generator.guid(),
|
||||
queryUI: {
|
||||
onEmptyFilter: EmptyFilterOption.RETURN_ALL,
|
||||
logicalOperator: FilterGroupLogicalOperator.ALL,
|
||||
logicalOperator: UILogicalOperator.ALL,
|
||||
groups: [
|
||||
{
|
||||
logicalOperator: FilterGroupLogicalOperator.ALL,
|
||||
logicalOperator: UILogicalOperator.ALL,
|
||||
filters: [
|
||||
{
|
||||
operator: BasicOperator.EQUAL,
|
||||
|
@ -2741,10 +2739,10 @@ describe.each([
|
|||
name: generator.guid(),
|
||||
queryUI: {
|
||||
onEmptyFilter: EmptyFilterOption.RETURN_ALL,
|
||||
logicalOperator: FilterGroupLogicalOperator.ALL,
|
||||
logicalOperator: UILogicalOperator.ALL,
|
||||
groups: [
|
||||
{
|
||||
logicalOperator: FilterGroupLogicalOperator.ALL,
|
||||
logicalOperator: UILogicalOperator.ALL,
|
||||
filters: [
|
||||
{
|
||||
operator: BasicOperator.NOT_EQUAL,
|
||||
|
@ -2799,10 +2797,10 @@ describe.each([
|
|||
name: generator.guid(),
|
||||
queryUI: {
|
||||
onEmptyFilter: EmptyFilterOption.RETURN_ALL,
|
||||
logicalOperator: FilterGroupLogicalOperator.ALL,
|
||||
logicalOperator: UILogicalOperator.ALL,
|
||||
groups: [
|
||||
{
|
||||
logicalOperator: FilterGroupLogicalOperator.ALL,
|
||||
logicalOperator: UILogicalOperator.ALL,
|
||||
filters: [
|
||||
{
|
||||
operator: BasicOperator.NOT_EQUAL,
|
||||
|
@ -2894,10 +2892,10 @@ describe.each([
|
|||
name: generator.guid(),
|
||||
queryUI: {
|
||||
onEmptyFilter: EmptyFilterOption.RETURN_ALL,
|
||||
logicalOperator: FilterGroupLogicalOperator.ALL,
|
||||
logicalOperator: UILogicalOperator.ALL,
|
||||
groups: [
|
||||
{
|
||||
logicalOperator: FilterGroupLogicalOperator.ALL,
|
||||
logicalOperator: UILogicalOperator.ALL,
|
||||
filters: [
|
||||
{
|
||||
operator: BasicOperator.EQUAL,
|
||||
|
@ -3338,10 +3336,10 @@ describe.each([
|
|||
type: ViewV2Type.CALCULATION,
|
||||
queryUI: {
|
||||
onEmptyFilter: EmptyFilterOption.RETURN_ALL,
|
||||
logicalOperator: FilterGroupLogicalOperator.ALL,
|
||||
logicalOperator: UILogicalOperator.ALL,
|
||||
groups: [
|
||||
{
|
||||
logicalOperator: FilterGroupLogicalOperator.ALL,
|
||||
logicalOperator: UILogicalOperator.ALL,
|
||||
filters: [
|
||||
{
|
||||
operator: BasicOperator.EQUAL,
|
||||
|
@ -3631,10 +3629,10 @@ describe.each([
|
|||
name: generator.guid(),
|
||||
queryUI: {
|
||||
onEmptyFilter: EmptyFilterOption.RETURN_ALL,
|
||||
logicalOperator: FilterGroupLogicalOperator.ALL,
|
||||
logicalOperator: UILogicalOperator.ALL,
|
||||
groups: [
|
||||
{
|
||||
logicalOperator: FilterGroupLogicalOperator.ALL,
|
||||
logicalOperator: UILogicalOperator.ALL,
|
||||
filters: [
|
||||
{
|
||||
operator: BasicOperator.EQUAL,
|
||||
|
@ -3711,52 +3709,31 @@ describe.each([
|
|||
|
||||
interface TestCase {
|
||||
name: string
|
||||
query: SearchFilterGroup
|
||||
query: UISearchFilter
|
||||
insert: Row[]
|
||||
expected: Row[]
|
||||
searchOpts?: SearchViewRowRequest
|
||||
}
|
||||
|
||||
function defaultQuery(
|
||||
query: Partial<SearchFilterGroup>
|
||||
): SearchFilterGroup {
|
||||
return {
|
||||
onEmptyFilter: EmptyFilterOption.RETURN_ALL,
|
||||
logicalOperator: FilterGroupLogicalOperator.ALL,
|
||||
groups: [],
|
||||
...query,
|
||||
}
|
||||
}
|
||||
|
||||
function defaultGroup(
|
||||
group: Partial<SearchFilterChild>
|
||||
): SearchFilterChild {
|
||||
return {
|
||||
logicalOperator: FilterGroupLogicalOperator.ALL,
|
||||
filters: [],
|
||||
...group,
|
||||
}
|
||||
}
|
||||
|
||||
function simpleQuery(...filters: LegacyFilter[]): SearchFilterGroup {
|
||||
return defaultQuery({ groups: [defaultGroup({ filters })] })
|
||||
function simpleQuery(...filters: LegacyFilter[]): UISearchFilter {
|
||||
return { groups: [{ filters }] }
|
||||
}
|
||||
|
||||
const testCases: TestCase[] = [
|
||||
{
|
||||
name: "empty query return all",
|
||||
insert: [{ string: "foo" }],
|
||||
query: defaultQuery({
|
||||
query: {
|
||||
onEmptyFilter: EmptyFilterOption.RETURN_ALL,
|
||||
}),
|
||||
},
|
||||
expected: [{ string: "foo" }],
|
||||
},
|
||||
{
|
||||
name: "empty query return none",
|
||||
insert: [{ string: "foo" }],
|
||||
query: defaultQuery({
|
||||
query: {
|
||||
onEmptyFilter: EmptyFilterOption.RETURN_NONE,
|
||||
}),
|
||||
},
|
||||
expected: [],
|
||||
},
|
||||
{
|
||||
|
|
|
@ -14,8 +14,8 @@ export function ensureQueryUISet(view: ViewV2) {
|
|||
// We're changing it in the change that this comment is part of to also
|
||||
// include SearchFilters objects. These are created when we receive an
|
||||
// update to a ViewV2 that contains a queryUI and not a query field. We
|
||||
// can convert SearchFilterGroup (the type of queryUI) to SearchFilters,
|
||||
// but not LegacyFilter[], they are incompatible due to SearchFilterGroup
|
||||
// can convert UISearchFilter (the type of queryUI) to SearchFilters,
|
||||
// but not LegacyFilter[], they are incompatible due to UISearchFilter
|
||||
// and SearchFilters being recursive types.
|
||||
//
|
||||
// So despite the type saying that `view.query` is a LegacyFilter[] |
|
||||
|
|
|
@ -19,8 +19,8 @@ import {
|
|||
RangeOperator,
|
||||
LogicalOperator,
|
||||
isLogicalSearchOperator,
|
||||
SearchFilterGroup,
|
||||
FilterGroupLogicalOperator,
|
||||
UISearchFilter,
|
||||
UILogicalOperator,
|
||||
isBasicSearchOperator,
|
||||
isArraySearchOperator,
|
||||
isRangeSearchOperator,
|
||||
|
@ -561,7 +561,7 @@ export const buildQueryLegacy = (
|
|||
}
|
||||
|
||||
/**
|
||||
* Converts a **SearchFilterGroup** filter definition into a grouped
|
||||
* Converts a **UISearchFilter** filter definition into a grouped
|
||||
* search query of type **SearchFilters**
|
||||
*
|
||||
* Legacy support remains for the old **SearchFilter[]** format.
|
||||
|
@ -573,49 +573,41 @@ export const buildQueryLegacy = (
|
|||
*/
|
||||
export function buildQuery(filter: undefined): undefined
|
||||
export function buildQuery(
|
||||
filter: SearchFilterGroup | LegacyFilter[]
|
||||
filter: UISearchFilter | LegacyFilter[]
|
||||
): SearchFilters
|
||||
export function buildQuery(
|
||||
filter?: SearchFilterGroup | LegacyFilter[]
|
||||
filter?: UISearchFilter | LegacyFilter[]
|
||||
): SearchFilters | undefined {
|
||||
if (!filter) {
|
||||
return
|
||||
}
|
||||
|
||||
let parsedFilter: SearchFilterGroup
|
||||
let parsedFilter: UISearchFilter
|
||||
if (Array.isArray(filter)) {
|
||||
parsedFilter = processSearchFilters(filter)
|
||||
} else {
|
||||
parsedFilter = filter
|
||||
}
|
||||
|
||||
const operatorMap = {
|
||||
[FilterGroupLogicalOperator.ALL]: LogicalOperator.AND,
|
||||
[FilterGroupLogicalOperator.ANY]: LogicalOperator.OR,
|
||||
}
|
||||
const operator = logicalOperatorFromUI(
|
||||
parsedFilter.logicalOperator || UILogicalOperator.ALL
|
||||
)
|
||||
const groups = parsedFilter.groups || []
|
||||
const conditions: SearchFilters[] = groups.map(group => {
|
||||
const filters = group.filters || []
|
||||
return { [operator]: { conditions: filters.map(x => buildCondition(x)) } }
|
||||
})
|
||||
|
||||
const globalOnEmpty = parsedFilter.onEmptyFilter
|
||||
? parsedFilter.onEmptyFilter
|
||||
: null
|
||||
|
||||
const globalOperator = operatorMap[parsedFilter.logicalOperator]
|
||||
|
||||
const ret = {
|
||||
...(globalOnEmpty ? { onEmptyFilter: globalOnEmpty } : {}),
|
||||
[globalOperator]: {
|
||||
conditions: parsedFilter.groups?.map(group => {
|
||||
return {
|
||||
[operatorMap[group.logicalOperator]]: {
|
||||
conditions: group.filters
|
||||
?.map(x => buildCondition(x))
|
||||
.filter(filter => filter),
|
||||
},
|
||||
onEmptyFilter: parsedFilter.onEmptyFilter,
|
||||
[operator]: { conditions },
|
||||
}
|
||||
}),
|
||||
},
|
||||
}
|
||||
|
||||
return ret
|
||||
function logicalOperatorFromUI(operator: UILogicalOperator): LogicalOperator {
|
||||
return operator === UILogicalOperator.ALL
|
||||
? LogicalOperator.AND
|
||||
: LogicalOperator.OR
|
||||
}
|
||||
|
||||
// The frontend can send single values for array fields sometimes, so to handle
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
import {
|
||||
LegacyFilter,
|
||||
SearchFilterGroup,
|
||||
FilterGroupLogicalOperator,
|
||||
UISearchFilter,
|
||||
UILogicalOperator,
|
||||
SearchFilters,
|
||||
BasicOperator,
|
||||
ArrayOperator,
|
||||
isLogicalSearchOperator,
|
||||
EmptyFilterOption,
|
||||
SearchFilterChild,
|
||||
SearchFilterGroup,
|
||||
} from "@budibase/types"
|
||||
import * as Constants from "./constants"
|
||||
import { removeKeyNumbering } from "./filters"
|
||||
|
@ -132,18 +132,18 @@ export function isSupportedUserSearch(query: SearchFilters) {
|
|||
|
||||
/**
|
||||
* Processes the filter config. Filters are migrated from
|
||||
* SearchFilter[] to SearchFilterGroup
|
||||
* SearchFilter[] to UISearchFilter
|
||||
*
|
||||
* If filters is not an array, the migration is skipped
|
||||
*
|
||||
* @param {LegacyFilter[] | SearchFilterGroup} filters
|
||||
* @param {LegacyFilter[] | UISearchFilter} filters
|
||||
*/
|
||||
export const processSearchFilters = (
|
||||
filters: LegacyFilter[]
|
||||
): SearchFilterGroup => {
|
||||
): UISearchFilter => {
|
||||
// Base search config.
|
||||
const defaultCfg: SearchFilterGroup = {
|
||||
logicalOperator: FilterGroupLogicalOperator.ALL,
|
||||
const defaultCfg: UISearchFilter = {
|
||||
logicalOperator: UILogicalOperator.ALL,
|
||||
onEmptyFilter: EmptyFilterOption.RETURN_ALL,
|
||||
groups: [],
|
||||
}
|
||||
|
@ -159,11 +159,11 @@ export const processSearchFilters = (
|
|||
"formulaType",
|
||||
]
|
||||
|
||||
let baseGroup: SearchFilterChild = {
|
||||
logicalOperator: FilterGroupLogicalOperator.ALL,
|
||||
let baseGroup: SearchFilterGroup = {
|
||||
logicalOperator: UILogicalOperator.ALL,
|
||||
}
|
||||
|
||||
return filters.reduce((acc: SearchFilterGroup, filter: LegacyFilter) => {
|
||||
return filters.reduce((acc: UISearchFilter, filter: LegacyFilter) => {
|
||||
// Sort the properties for easier debugging
|
||||
const filterPropertyKeys = (Object.keys(filter) as (keyof LegacyFilter)[])
|
||||
.sort((a, b) => {
|
||||
|
@ -180,7 +180,7 @@ export const processSearchFilters = (
|
|||
acc.onEmptyFilter = value
|
||||
} else if (key === "operator" && value === "allOr") {
|
||||
// Group 1 logical operator
|
||||
baseGroup.logicalOperator = FilterGroupLogicalOperator.ANY
|
||||
baseGroup.logicalOperator = UILogicalOperator.ANY
|
||||
}
|
||||
|
||||
return acc
|
||||
|
|
|
@ -1,9 +1,5 @@
|
|||
import { FieldType } from "../../documents"
|
||||
import {
|
||||
EmptyFilterOption,
|
||||
FilterGroupLogicalOperator,
|
||||
SearchFilters,
|
||||
} from "../../sdk"
|
||||
import { EmptyFilterOption, UILogicalOperator, SearchFilters } from "../../sdk"
|
||||
|
||||
export type LegacyFilter = {
|
||||
operator: keyof SearchFilters | "rangeLow" | "rangeHigh"
|
||||
|
@ -14,15 +10,15 @@ export type LegacyFilter = {
|
|||
externalType?: string
|
||||
}
|
||||
|
||||
export type SearchFilterChild = {
|
||||
logicalOperator: FilterGroupLogicalOperator
|
||||
groups?: SearchFilterChild[]
|
||||
export type SearchFilterGroup = {
|
||||
logicalOperator?: UILogicalOperator
|
||||
groups?: SearchFilterGroup[]
|
||||
filters?: LegacyFilter[]
|
||||
}
|
||||
|
||||
// this is a type purely used by the UI
|
||||
export type SearchFilterGroup = {
|
||||
logicalOperator: FilterGroupLogicalOperator
|
||||
onEmptyFilter: EmptyFilterOption
|
||||
groups: SearchFilterChild[]
|
||||
export type UISearchFilter = {
|
||||
logicalOperator?: UILogicalOperator
|
||||
onEmptyFilter?: EmptyFilterOption
|
||||
groups?: SearchFilterGroup[]
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { LegacyFilter, SearchFilterGroup, SortOrder, SortType } from "../../api"
|
||||
import { LegacyFilter, UISearchFilter, SortOrder, SortType } from "../../api"
|
||||
import { UIFieldMetadata } from "./table"
|
||||
import { Document } from "../document"
|
||||
import { DBView, SearchFilters } from "../../sdk"
|
||||
|
@ -92,7 +92,7 @@ export interface ViewV2 {
|
|||
tableId: string
|
||||
query?: LegacyFilter[] | SearchFilters
|
||||
// duplicate to store UI information about filters
|
||||
queryUI?: SearchFilterGroup
|
||||
queryUI?: UISearchFilter
|
||||
sort?: {
|
||||
field: string
|
||||
order?: SortOrder
|
||||
|
|
|
@ -203,7 +203,7 @@ export enum EmptyFilterOption {
|
|||
RETURN_NONE = "none",
|
||||
}
|
||||
|
||||
export enum FilterGroupLogicalOperator {
|
||||
export enum UILogicalOperator {
|
||||
ALL = "all",
|
||||
ANY = "any",
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue