From 6a1867939c81b286ad1eeb97f29de039a71d622c Mon Sep 17 00:00:00 2001 From: Mel O'Hagan Date: Tue, 19 Jul 2022 14:11:40 +0100 Subject: [PATCH 1/8] Contains now works for internalSearch array type --- packages/frontend-core/src/constants.js | 2 +- .../src/api/controllers/row/internalSearch.js | 55 ++++++++++++------- 2 files changed, 36 insertions(+), 21 deletions(-) diff --git a/packages/frontend-core/src/constants.js b/packages/frontend-core/src/constants.js index 00af4127e4..4ac2a02f6c 100644 --- a/packages/frontend-core/src/constants.js +++ b/packages/frontend-core/src/constants.js @@ -35,7 +35,7 @@ export const OperatorOptions = { label: "Less than", }, Contains: { - value: "equal", + value: "contains", label: "Contains", }, NotContains: { diff --git a/packages/server/src/api/controllers/row/internalSearch.js b/packages/server/src/api/controllers/row/internalSearch.js index e6090ad8f0..8a04fc2bd0 100644 --- a/packages/server/src/api/controllers/row/internalSearch.js +++ b/packages/server/src/api/controllers/row/internalSearch.js @@ -19,6 +19,7 @@ class QueryBuilder { empty: {}, notEmpty: {}, oneOf: {}, + contains: {}, ...base, } this.limit = 50 @@ -119,6 +120,11 @@ class QueryBuilder { return this } + addContains(key, value) { + this.query.contains[key] = value + return this + } + /** * Preprocesses a value before going into a lucene search. * Transforms strings to lowercase and wraps strings and bools in quotes. @@ -164,6 +170,31 @@ class QueryBuilder { return `${key}:${builder.preprocess(value, allPreProcessingOpts)}` } + const contains = (key, value) => { + if (!value && value !== 0) { + return null + } + return `${key}:${builder.preprocess(value, { escape: true })}` + } + + const oneOf = (key, value) => { + if (!Array.isArray(value)) { + if (typeof value === "string") { + value = value.split(",") + } else { + return "" + } + } + let orStatement = `${builder.preprocess(value[0], allPreProcessingOpts)}` + for (let i = 1; i < value.length; i++) { + orStatement += ` OR ${builder.preprocess( + value[i], + allPreProcessingOpts + )}` + } + return `${key}:(${orStatement})` + } + function build(structure, queryFn) { for (let [key, value] of Object.entries(structure)) { key = builder.preprocess(key.replace(/ /g, "_"), { @@ -239,26 +270,10 @@ class QueryBuilder { build(this.query.notEmpty, key => `${key}:["" TO *]`) } if (this.query.oneOf) { - build(this.query.oneOf, (key, value) => { - if (!Array.isArray(value)) { - if (typeof value === "string") { - value = value.split(",") - } else { - return "" - } - } - let orStatement = `${builder.preprocess( - value[0], - allPreProcessingOpts - )}` - for (let i = 1; i < value.length; i++) { - orStatement += ` OR ${builder.preprocess( - value[i], - allPreProcessingOpts - )}` - } - return `${key}:(${orStatement})` - }) + build(this.query.oneOf, oneOf) + } + if (this.query.contains) { + build(this.query.contains, contains) } // make sure table ID is always added as an AND if (tableId) { From 1345ec831a15d8bbf0e915896d1008af9355cadc Mon Sep 17 00:00:00 2001 From: Mel O'Hagan Date: Tue, 19 Jul 2022 14:17:09 +0100 Subject: [PATCH 2/8] Test prettier precommit --- packages/server/src/api/controllers/row/internalSearch.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/server/src/api/controllers/row/internalSearch.js b/packages/server/src/api/controllers/row/internalSearch.js index 8a04fc2bd0..c951baec3e 100644 --- a/packages/server/src/api/controllers/row/internalSearch.js +++ b/packages/server/src/api/controllers/row/internalSearch.js @@ -20,7 +20,7 @@ class QueryBuilder { notEmpty: {}, oneOf: {}, contains: {}, - ...base, + ...base } this.limit = 50 this.sortOrder = "ascending" From e5dc38845b90a0d343a5556cc60f764b0fd57ca6 Mon Sep 17 00:00:00 2001 From: Mel O'Hagan Date: Tue, 19 Jul 2022 17:25:41 +0100 Subject: [PATCH 3/8] Added contains support for datasource pluses --- .../src/api/controllers/row/internalSearch.js | 4 +- packages/server/src/definitions/datasource.ts | 3 ++ packages/server/src/integrations/base/sql.ts | 52 ++++++++++++++----- 3 files changed, 44 insertions(+), 15 deletions(-) diff --git a/packages/server/src/api/controllers/row/internalSearch.js b/packages/server/src/api/controllers/row/internalSearch.js index c951baec3e..a81620c90c 100644 --- a/packages/server/src/api/controllers/row/internalSearch.js +++ b/packages/server/src/api/controllers/row/internalSearch.js @@ -19,8 +19,8 @@ class QueryBuilder { empty: {}, notEmpty: {}, oneOf: {}, - contains: {}, - ...base + contains: {} , + ...base, } this.limit = 50 this.sortOrder = "ascending" diff --git a/packages/server/src/definitions/datasource.ts b/packages/server/src/definitions/datasource.ts index 90c81abe9f..9752fc947a 100644 --- a/packages/server/src/definitions/datasource.ts +++ b/packages/server/src/definitions/datasource.ts @@ -131,6 +131,9 @@ export interface SearchFilters { oneOf?: { [key: string]: any[] } + contains?: { + [key: string]: any + } } export interface SortJson { diff --git a/packages/server/src/integrations/base/sql.ts b/packages/server/src/integrations/base/sql.ts index fbbc42151a..3e7bcbf317 100644 --- a/packages/server/src/integrations/base/sql.ts +++ b/packages/server/src/integrations/base/sql.ts @@ -142,6 +142,21 @@ class InternalBuilder { } } } + + const like = (key: string, value: any) => { + const fnc = allOr ? "orWhere" : "where" + // postgres supports ilike, nothing else does + if (this.client === SqlClients.POSTGRES) { + query = query[fnc](key, "ilike", `%${value}%`) + } else { + const rawFnc = `${fnc}Raw` + // @ts-ignore + query = query[rawFnc](`LOWER(${likeKey(this.client, key)}) LIKE ?`, [ + `%${value}%`, + ]) + } + } + if (!filters) { return query } @@ -168,19 +183,7 @@ class InternalBuilder { }) } if (filters.fuzzy) { - iterate(filters.fuzzy, (key, value) => { - const fnc = allOr ? "orWhere" : "where" - // postgres supports ilike, nothing else does - if (this.client === SqlClients.POSTGRES) { - query = query[fnc](key, "ilike", `%${value}%`) - } else { - const rawFnc = `${fnc}Raw` - // @ts-ignore - query = query[rawFnc](`LOWER(${likeKey(this.client, key)}) LIKE ?`, [ - `%${value}%`, - ]) - } - }) + iterate(filters.fuzzy, like) } if (filters.range) { iterate(filters.range, (key, value) => { @@ -223,6 +226,29 @@ class InternalBuilder { query = query[fnc](key) }) } + if (filters.contains) { + const fnc = allOr ? "orWhere" : "where" + const rawFnc = `${fnc}Raw` + if (this.client === SqlClients.POSTGRES) { + iterate(filters.contains, (key: string, value: any) => { + const fieldNames = key.split(/\./g) + const tableName = fieldNames[0] + const columnName = fieldNames[1] + // @ts-ignore + query = query[rawFnc](`"${tableName}"."${columnName}"::jsonb @> '["${value}"]'`) + }) + } else if (this.client === SqlClients.MY_SQL) { + iterate(filters.contains, (key: string, value: any) => { + if (typeof value === "string") { + value = `"${value}"` + } + // @ts-ignore + query = query[rawFnc](`JSON_CONTAINS(${key}, '${value}')`) + }) + } else { + iterate(filters.contains, like) + } + } return query } From f376fc30b385badcf97e4450ab53906bd275694f Mon Sep 17 00:00:00 2001 From: Mel O'Hagan Date: Tue, 19 Jul 2022 17:28:56 +0100 Subject: [PATCH 4/8] lint fix --- packages/server/src/api/controllers/row/internalSearch.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/server/src/api/controllers/row/internalSearch.js b/packages/server/src/api/controllers/row/internalSearch.js index a81620c90c..8a04fc2bd0 100644 --- a/packages/server/src/api/controllers/row/internalSearch.js +++ b/packages/server/src/api/controllers/row/internalSearch.js @@ -19,7 +19,7 @@ class QueryBuilder { empty: {}, notEmpty: {}, oneOf: {}, - contains: {} , + contains: {}, ...base, } this.limit = 50 From 4a7c7349e65c9bda0da14e7c395883ce67edf8c4 Mon Sep 17 00:00:00 2001 From: Mel O'Hagan Date: Tue, 19 Jul 2022 18:03:50 +0100 Subject: [PATCH 5/8] Yarn lock --- packages/server/yarn.lock | 38 +++++++++++++++++++------------------- packages/worker/yarn.lock | 38 +++++++++++++++++++------------------- 2 files changed, 38 insertions(+), 38 deletions(-) diff --git a/packages/server/yarn.lock b/packages/server/yarn.lock index 06c6070132..e15c64cf25 100644 --- a/packages/server/yarn.lock +++ b/packages/server/yarn.lock @@ -1094,12 +1094,12 @@ resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== -"@budibase/backend-core@1.1.15-alpha.1": - version "1.1.15-alpha.1" - resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-1.1.15-alpha.1.tgz#fb2b726a9afe301aaedbf09a5bcfa82ef14fa7b9" - integrity sha512-tVujXhAA7E8h9DbmAeRmje/CcJKwWvPIk8og6o46kmkdLx+7lwm4AG4ImrsR9PoRtvhkdUClAUwuGtFGcsafwg== +"@budibase/backend-core@1.1.15-alpha.3": + version "1.1.15-alpha.3" + resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-1.1.15-alpha.3.tgz#2c082606f17b2adeaff2a88b18c3a247abc3f3aa" + integrity sha512-qg3hEmZjMRJyZK2OnvEUhQ7gCXZzSJ6036XHX8dUWfSiiUD60R1qLiAjSKDOPGDlZ4T3zsAYUAzJkyc++dUYbQ== dependencies: - "@budibase/types" "^1.1.15-alpha.1" + "@budibase/types" "^1.1.15-alpha.3" "@techpass/passport-openidconnect" "0.3.2" aws-sdk "2.1030.0" bcrypt "5.0.1" @@ -1177,13 +1177,13 @@ svelte-flatpickr "^3.2.3" svelte-portal "^1.0.0" -"@budibase/pro@1.1.15-alpha.1": - version "1.1.15-alpha.1" - resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-1.1.15-alpha.1.tgz#8013b5bdb6adea291bf29a32f9c572e5cc1f9fc8" - integrity sha512-8DwIs12un59YnLNlqUFQgGqclf4Dmpp76Yo4cVDeRkaKDvbRJoUUK7jkYsDpstU6FVXD8m6/0l8Pwr3gWN5iyQ== +"@budibase/pro@1.1.15-alpha.3": + version "1.1.15-alpha.3" + resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-1.1.15-alpha.3.tgz#15ff94878e677092b631e3bdde6ff7fb3403f8b9" + integrity sha512-Xw32Q7Mbc6HtsPv1viWqBewMUMVNSZPvBmjYUEl5zezwsAaoHV5Tbbak5noLIHYfxmZO/kBWbBgVDxEx4ja3jQ== dependencies: - "@budibase/backend-core" "1.1.15-alpha.1" - "@budibase/types" "1.1.15-alpha.1" + "@budibase/backend-core" "1.1.15-alpha.3" + "@budibase/types" "1.1.15-alpha.3" node-fetch "^2.6.1" "@budibase/standard-components@^0.9.139": @@ -1204,15 +1204,15 @@ svelte-apexcharts "^1.0.2" svelte-flatpickr "^3.1.0" -"@budibase/types@1.1.15-alpha.1": - version "1.1.15-alpha.1" - resolved "https://registry.yarnpkg.com/@budibase/types/-/types-1.1.15-alpha.1.tgz#4abb0830e3c1dca4a49bc974371edda922f8253b" - integrity sha512-x00f0/JY2CayjGEBR9R2cH/87nFV1dg2bZHXdMIWN6djcQjBsMjkaq+Qx2xJtWPMcld9yufPbBWdfgVQsiPc0A== +"@budibase/types@1.1.15-alpha.3": + version "1.1.15-alpha.3" + resolved "https://registry.yarnpkg.com/@budibase/types/-/types-1.1.15-alpha.3.tgz#6e46c4fa3fdea3544871d0b529b4c8baeadc597b" + integrity sha512-vIZuXnjExC34yMXCStp174jzMSObH3FIfHHCl373x/Jkgh0uAqDYxQe0hFYiuBH81OrD4TGbOD1+qL5pqODyVw== -"@budibase/types@^1.1.15-alpha.1": - version "1.1.16" - resolved "https://registry.yarnpkg.com/@budibase/types/-/types-1.1.16.tgz#4dd1f0b1e630abd46749414d74a1fdd07820df54" - integrity sha512-jaOdsCOx0CJ2tyKodTI6PMo9CNHTo1nsMMrRi/XFIFQtGOypkiNoskb5u0Ee3GtpN6LNXgwPdrYnh+vcIL9lRw== +"@budibase/types@^1.1.15-alpha.3": + version "1.1.17" + resolved "https://registry.yarnpkg.com/@budibase/types/-/types-1.1.17.tgz#fa58e86f3858c04a8b79094193ff87ac57a499fe" + integrity sha512-3sTH3tjPd+NEk5CIN23bgwyGXXNYq/hwaxKMbYLKGr45K6m9WDZs6saWxJ2JwguNtKGB9RggzCVR/DFJH2zI1A== "@bull-board/api@3.7.0": version "3.7.0" diff --git a/packages/worker/yarn.lock b/packages/worker/yarn.lock index 8f79dc881c..d6a6d76c7c 100644 --- a/packages/worker/yarn.lock +++ b/packages/worker/yarn.lock @@ -291,12 +291,12 @@ resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== -"@budibase/backend-core@1.1.15-alpha.1": - version "1.1.15-alpha.1" - resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-1.1.15-alpha.1.tgz#fb2b726a9afe301aaedbf09a5bcfa82ef14fa7b9" - integrity sha512-tVujXhAA7E8h9DbmAeRmje/CcJKwWvPIk8og6o46kmkdLx+7lwm4AG4ImrsR9PoRtvhkdUClAUwuGtFGcsafwg== +"@budibase/backend-core@1.1.15-alpha.3": + version "1.1.15-alpha.3" + resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-1.1.15-alpha.3.tgz#2c082606f17b2adeaff2a88b18c3a247abc3f3aa" + integrity sha512-qg3hEmZjMRJyZK2OnvEUhQ7gCXZzSJ6036XHX8dUWfSiiUD60R1qLiAjSKDOPGDlZ4T3zsAYUAzJkyc++dUYbQ== dependencies: - "@budibase/types" "^1.1.15-alpha.1" + "@budibase/types" "^1.1.15-alpha.3" "@techpass/passport-openidconnect" "0.3.2" aws-sdk "2.1030.0" bcrypt "5.0.1" @@ -324,24 +324,24 @@ uuid "8.3.2" zlib "1.0.5" -"@budibase/pro@1.1.15-alpha.1": - version "1.1.15-alpha.1" - resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-1.1.15-alpha.1.tgz#8013b5bdb6adea291bf29a32f9c572e5cc1f9fc8" - integrity sha512-8DwIs12un59YnLNlqUFQgGqclf4Dmpp76Yo4cVDeRkaKDvbRJoUUK7jkYsDpstU6FVXD8m6/0l8Pwr3gWN5iyQ== +"@budibase/pro@1.1.15-alpha.3": + version "1.1.15-alpha.3" + resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-1.1.15-alpha.3.tgz#15ff94878e677092b631e3bdde6ff7fb3403f8b9" + integrity sha512-Xw32Q7Mbc6HtsPv1viWqBewMUMVNSZPvBmjYUEl5zezwsAaoHV5Tbbak5noLIHYfxmZO/kBWbBgVDxEx4ja3jQ== dependencies: - "@budibase/backend-core" "1.1.15-alpha.1" - "@budibase/types" "1.1.15-alpha.1" + "@budibase/backend-core" "1.1.15-alpha.3" + "@budibase/types" "1.1.15-alpha.3" node-fetch "^2.6.1" -"@budibase/types@1.1.15-alpha.1": - version "1.1.15-alpha.1" - resolved "https://registry.yarnpkg.com/@budibase/types/-/types-1.1.15-alpha.1.tgz#4abb0830e3c1dca4a49bc974371edda922f8253b" - integrity sha512-x00f0/JY2CayjGEBR9R2cH/87nFV1dg2bZHXdMIWN6djcQjBsMjkaq+Qx2xJtWPMcld9yufPbBWdfgVQsiPc0A== +"@budibase/types@1.1.15-alpha.3": + version "1.1.15-alpha.3" + resolved "https://registry.yarnpkg.com/@budibase/types/-/types-1.1.15-alpha.3.tgz#6e46c4fa3fdea3544871d0b529b4c8baeadc597b" + integrity sha512-vIZuXnjExC34yMXCStp174jzMSObH3FIfHHCl373x/Jkgh0uAqDYxQe0hFYiuBH81OrD4TGbOD1+qL5pqODyVw== -"@budibase/types@^1.1.15-alpha.1": - version "1.1.16" - resolved "https://registry.yarnpkg.com/@budibase/types/-/types-1.1.16.tgz#4dd1f0b1e630abd46749414d74a1fdd07820df54" - integrity sha512-jaOdsCOx0CJ2tyKodTI6PMo9CNHTo1nsMMrRi/XFIFQtGOypkiNoskb5u0Ee3GtpN6LNXgwPdrYnh+vcIL9lRw== +"@budibase/types@^1.1.15-alpha.3": + version "1.1.17" + resolved "https://registry.yarnpkg.com/@budibase/types/-/types-1.1.17.tgz#fa58e86f3858c04a8b79094193ff87ac57a499fe" + integrity sha512-3sTH3tjPd+NEk5CIN23bgwyGXXNYq/hwaxKMbYLKGr45K6m9WDZs6saWxJ2JwguNtKGB9RggzCVR/DFJH2zI1A== "@cspotcode/source-map-consumer@0.8.0": version "0.8.0" From cd0bac74e222c0d6419694208e34ef30b55827c4 Mon Sep 17 00:00:00 2001 From: Mel O'Hagan Date: Tue, 19 Jul 2022 18:04:27 +0100 Subject: [PATCH 6/8] Prettier --- packages/server/src/integrations/base/sql.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/server/src/integrations/base/sql.ts b/packages/server/src/integrations/base/sql.ts index 3e7bcbf317..62db482a24 100644 --- a/packages/server/src/integrations/base/sql.ts +++ b/packages/server/src/integrations/base/sql.ts @@ -235,7 +235,9 @@ class InternalBuilder { const tableName = fieldNames[0] const columnName = fieldNames[1] // @ts-ignore - query = query[rawFnc](`"${tableName}"."${columnName}"::jsonb @> '["${value}"]'`) + query = query[rawFnc]( + `"${tableName}"."${columnName}"::jsonb @> '["${value}"]'` + ) }) } else if (this.client === SqlClients.MY_SQL) { iterate(filters.contains, (key: string, value: any) => { From 6b81d3d727ee816be33df401c543b7d9884b292e Mon Sep 17 00:00:00 2001 From: Mel O'Hagan Date: Tue, 19 Jul 2022 18:34:30 +0100 Subject: [PATCH 7/8] Handle numeric types Postgres --- packages/server/src/integrations/base/sql.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/server/src/integrations/base/sql.ts b/packages/server/src/integrations/base/sql.ts index 62db482a24..750564c6ff 100644 --- a/packages/server/src/integrations/base/sql.ts +++ b/packages/server/src/integrations/base/sql.ts @@ -234,9 +234,12 @@ class InternalBuilder { const fieldNames = key.split(/\./g) const tableName = fieldNames[0] const columnName = fieldNames[1] + if (typeof value === "string") { + value = `"${value}"` + } // @ts-ignore query = query[rawFnc]( - `"${tableName}"."${columnName}"::jsonb @> '["${value}"]'` + `"${tableName}"."${columnName}"::jsonb @> '[${value}]'` ) }) } else if (this.client === SqlClients.MY_SQL) { From f653345296a67c80ab31c3919f55dcdc862f0a73 Mon Sep 17 00:00:00 2001 From: Mel O'Hagan Date: Thu, 21 Jul 2022 10:28:54 +0100 Subject: [PATCH 8/8] Added unit tests for contains filter --- .../server/src/integrations/tests/sql.spec.js | 54 +++++++++++++++++-- 1 file changed, 50 insertions(+), 4 deletions(-) diff --git a/packages/server/src/integrations/tests/sql.spec.js b/packages/server/src/integrations/tests/sql.spec.js index c2e65c56b7..55c762573a 100644 --- a/packages/server/src/integrations/tests/sql.spec.js +++ b/packages/server/src/integrations/tests/sql.spec.js @@ -1,4 +1,5 @@ const Sql = require("../base/sql") +const { SqlClients } = require("../utils") const TABLE_NAME = "test" @@ -46,7 +47,7 @@ function generateDeleteJson(table = TABLE_NAME, filters = {}) { describe("SQL query builder", () => { const limit = 500 - const client = "pg" + const client = SqlClients.POSTGRES let sql beforeEach(() => { @@ -173,15 +174,15 @@ describe("SQL query builder", () => { }) it("should work with MS-SQL", () => { - const query = new Sql("mssql", 10)._query(generateReadJson()) + const query = new Sql(SqlClients.MS_SQL, 10)._query(generateReadJson()) expect(query).toEqual({ bindings: [10], sql: `select * from (select top (@p0) * from [${TABLE_NAME}]) as [${TABLE_NAME}]` }) }) - it("should work with mySQL", () => { - const query = new Sql("mysql", 10)._query(generateReadJson()) + it("should work with MySQL", () => { + const query = new Sql(SqlClients.MY_SQL, 10)._query(generateReadJson()) expect(query).toEqual({ bindings: [10], sql: `select * from (select * from \`${TABLE_NAME}\` limit ?) as \`${TABLE_NAME}\`` @@ -238,4 +239,49 @@ describe("SQL query builder", () => { sql: `select * from (select * from "${TABLE_NAME}" where "${TABLE_NAME}"."property" > $1 limit $2) as "${TABLE_NAME}"` }) }) + + it("should use like expression for MS-SQL when filter is contains", () => { + const query = new Sql(SqlClients.MS_SQL, 10)._query(generateReadJson({ + filters: { + contains: { + age: 20, + name: "John" + } + } + })) + expect(query).toEqual({ + bindings: [10, "%20%", "%John%"], + sql: `select * from (select top (@p0) * from [${TABLE_NAME}] where LOWER(${TABLE_NAME}.age) LIKE @p1 and LOWER(${TABLE_NAME}.name) LIKE @p2) as [${TABLE_NAME}]` + }) + }) + + it("should use JSON_CONTAINS expression for MySQL when filter is contains", () => { + const query = new Sql(SqlClients.MY_SQL, 10)._query(generateReadJson({ + filters: { + contains: { + age: 20, + name: "John" + } + } + })) + expect(query).toEqual({ + bindings: [10], + sql: `select * from (select * from \`${TABLE_NAME}\` where JSON_CONTAINS(${TABLE_NAME}.age, '20') and JSON_CONTAINS(${TABLE_NAME}.name, '"John"') limit ?) as \`${TABLE_NAME}\`` + }) + }) + + it("should use jsonb operator expression for PostgreSQL when filter is contains", () => { + const query = new Sql(SqlClients.POSTGRES, 10)._query(generateReadJson({ + filters: { + contains: { + age: 20, + name: "John" + } + } + })) + expect(query).toEqual({ + bindings: [10], + sql: `select * from (select * from \"${TABLE_NAME}\" where \"${TABLE_NAME}\".\"age\"::jsonb @> '[20]' and \"${TABLE_NAME}\".\"name\"::jsonb @> '["John"]' limit $1) as \"${TABLE_NAME}\"` + }) + }) })