From 11f49c95dc343179f030c60d5c27edcee812e721 Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Mon, 1 Jul 2024 09:47:58 +0100 Subject: [PATCH] Fix contains search on multi-user column. --- packages/backend-core/src/sql/sql.ts | 18 +++++++++++++++--- .../server/src/api/routes/tests/search.spec.ts | 9 +++++++++ packages/types/src/sdk/search.ts | 2 +- 3 files changed, 25 insertions(+), 4 deletions(-) diff --git a/packages/backend-core/src/sql/sql.ts b/packages/backend-core/src/sql/sql.ts index 34b950bf2c..3ba073aef0 100644 --- a/packages/backend-core/src/sql/sql.ts +++ b/packages/backend-core/src/sql/sql.ts @@ -246,7 +246,11 @@ class InternalBuilder { return `[${value.join(",")}]` } if (this.client === SqlClient.POSTGRES) { - iterate(mode, (key: string, value: Array) => { + iterate(mode, (key: string, value: any) => { + if (!Array.isArray(value)) { + value = [value] + } + const wrap = any ? "" : "'" const op = any ? "\\?| array" : "@>" const fieldNames = key.split(/\./g) @@ -261,7 +265,11 @@ class InternalBuilder { }) } else if (this.client === SqlClient.MY_SQL) { const jsonFnc = any ? "JSON_OVERLAPS" : "JSON_CONTAINS" - iterate(mode, (key: string, value: Array) => { + iterate(mode, (key: string, value: any) => { + if (!Array.isArray(value)) { + value = [value] + } + query = query[rawFnc]( `${not}COALESCE(${jsonFnc}(${key}, '${stringifyArray( value @@ -270,7 +278,11 @@ class InternalBuilder { }) } else { const andOr = mode === filters?.containsAny ? " OR " : " AND " - iterate(mode, (key: string, value: Array) => { + iterate(mode, (key: string, value: any) => { + if (!Array.isArray(value)) { + value = [value] + } + let statement = "" for (let i in value) { if (typeof value[i] === "string") { diff --git a/packages/server/src/api/routes/tests/search.spec.ts b/packages/server/src/api/routes/tests/search.spec.ts index 589f129f31..bc253ad78e 100644 --- a/packages/server/src/api/routes/tests/search.spec.ts +++ b/packages/server/src/api/routes/tests/search.spec.ts @@ -1938,6 +1938,15 @@ describe.each([ ]) }) + it("successfully finds a row searching with a string", async () => { + await expectQuery({ + contains: { "1:users": user1._id }, + }).toContainExactly([ + { users: [{ _id: user1._id }] }, + { users: [{ _id: user1._id }, { _id: user2._id }] }, + ]) + }) + it("fails to find nonexistent row", async () => { await expectQuery({ contains: { users: ["us_none"] } }).toFindNothing() }) diff --git a/packages/types/src/sdk/search.ts b/packages/types/src/sdk/search.ts index e5cbccf5c1..52428579b7 100644 --- a/packages/types/src/sdk/search.ts +++ b/packages/types/src/sdk/search.ts @@ -54,7 +54,7 @@ export interface SearchFilters { [key: string]: any[] } [SearchFilterOperator.CONTAINS]?: { - [key: string]: any[] + [key: string]: any[] | any } [SearchFilterOperator.NOT_CONTAINS]?: { [key: string]: any[]