From d61d5f51cc5c791041a23e0e41469e5603f59575 Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Mon, 15 Apr 2024 15:31:46 +0100 Subject: [PATCH] Add tests for array column types, fixing some bugs along the way. --- .../src/api/routes/tests/search.spec.ts | 74 +++++++++++++++++++ packages/server/src/constants/index.ts | 2 + packages/server/src/integrations/base/sql.ts | 5 ++ packages/server/src/sdk/app/rows/search.ts | 6 +- 4 files changed, 86 insertions(+), 1 deletion(-) diff --git a/packages/server/src/api/routes/tests/search.spec.ts b/packages/server/src/api/routes/tests/search.spec.ts index f6945cbe46..5b71ec9044 100644 --- a/packages/server/src/api/routes/tests/search.spec.ts +++ b/packages/server/src/api/routes/tests/search.spec.ts @@ -476,4 +476,78 @@ describe.each([ }) }) }) + + describe("array of strings", () => { + beforeAll(async () => { + await createTable({ + numbers: { + name: "numbers", + type: FieldType.ARRAY, + constraints: { inclusion: ["one", "two", "three"] }, + }, + }) + await createRows([{ numbers: ["one", "two"] }, { numbers: ["three"] }]) + }) + + describe("contains", () => { + it("successfully finds a row", () => + expectQuery({ contains: { numbers: ["one"] } }).toContainExactly([ + { numbers: ["one", "two"] }, + ])) + + it("fails to find nonexistent row", () => + expectQuery({ contains: { numbers: ["none"] } }).toFindNothing()) + + it("fails to find row containing all", () => + expectQuery({ + contains: { numbers: ["one", "two", "three"] }, + }).toFindNothing()) + + it("finds all with empty list", () => + expectQuery({ contains: { numbers: [] } }).toContainExactly([ + { numbers: ["one", "two"] }, + { numbers: ["three"] }, + ])) + }) + + describe("notContains", () => { + it("successfully finds a row", () => + expectQuery({ notContains: { numbers: ["one"] } }).toContainExactly([ + { numbers: ["three"] }, + ])) + + it("fails to find nonexistent row", () => + expectQuery({ + notContains: { numbers: ["one", "two", "three"] }, + }).toContainExactly([ + { numbers: ["one", "two"] }, + { numbers: ["three"] }, + ])) + + it("finds all with empty list", () => + expectQuery({ notContains: { numbers: [] } }).toContainExactly([ + { numbers: ["one", "two"] }, + { numbers: ["three"] }, + ])) + }) + + describe("containsAny", () => { + it("successfully finds rows", () => + expectQuery({ + containsAny: { numbers: ["one", "two", "three"] }, + }).toContainExactly([ + { numbers: ["one", "two"] }, + { numbers: ["three"] }, + ])) + + it("fails to find nonexistent row", () => + expectQuery({ containsAny: { numbers: ["none"] } }).toFindNothing()) + + it("finds all with empty list", () => + expectQuery({ containsAny: { numbers: [] } }).toContainExactly([ + { numbers: ["one", "two"] }, + { numbers: ["three"] }, + ])) + }) + }) }) diff --git a/packages/server/src/constants/index.ts b/packages/server/src/constants/index.ts index 42a1b53224..37c275c8a3 100644 --- a/packages/server/src/constants/index.ts +++ b/packages/server/src/constants/index.ts @@ -20,6 +20,7 @@ export enum FilterTypes { NOT_EMPTY = "notEmpty", CONTAINS = "contains", NOT_CONTAINS = "notContains", + CONTAINS_ANY = "containsAny", ONE_OF = "oneOf", } @@ -30,6 +31,7 @@ export const NoEmptyFilterStrings = [ FilterTypes.NOT_EQUAL, FilterTypes.CONTAINS, FilterTypes.NOT_CONTAINS, + FilterTypes.CONTAINS_ANY, ] export const CanSwitchTypes = [ diff --git a/packages/server/src/integrations/base/sql.ts b/packages/server/src/integrations/base/sql.ts index f5828f9419..259abec106 100644 --- a/packages/server/src/integrations/base/sql.ts +++ b/packages/server/src/integrations/base/sql.ts @@ -233,6 +233,11 @@ class InternalBuilder { (statement ? andOr : "") + `LOWER(${likeKey(this.client, key)}) LIKE ?` } + + if (statement === "") { + return + } + // @ts-ignore query = query[rawFnc](`${not}(${statement})`, value) }) diff --git a/packages/server/src/sdk/app/rows/search.ts b/packages/server/src/sdk/app/rows/search.ts index f681bfeb90..5a016c821f 100644 --- a/packages/server/src/sdk/app/rows/search.ts +++ b/packages/server/src/sdk/app/rows/search.ts @@ -29,6 +29,10 @@ function pickApi(tableId: any) { return internal } +function isEmptyArray(value: any) { + return Array.isArray(value) && value.length === 0 +} + // don't do a pure falsy check, as 0 is included // https://github.com/Budibase/budibase/issues/10118 export function removeEmptyFilters(filters: SearchFilters) { @@ -47,7 +51,7 @@ export function removeEmptyFilters(filters: SearchFilters) { for (let [key, value] of Object.entries( filters[filterType] as object )) { - if (value == null || value === "") { + if (value == null || value === "" || isEmptyArray(value)) { // @ts-ignore delete filters[filterField][key] }