Not Contains filter for MySQL

This commit is contained in:
Mel O'Hagan 2022-07-27 11:40:46 +01:00
parent 291e13641d
commit ce9a6a7141
5 changed files with 141 additions and 75 deletions

View File

@ -132,7 +132,10 @@ export interface SearchFilters {
[key: string]: any[]
}
contains?: {
[key: string]: any
[key: string]: any[]
}
notContains?: {
[key: string]: any[]
}
}

View File

@ -157,6 +157,54 @@ class InternalBuilder {
}
}
const contains = (mode: object) => {
const fnc = allOr ? "orWhere" : "where"
const rawFnc = `${fnc}Raw`
const not = mode === filters?.notContains ? "NOT " : ""
function stringifyArray(value: Array<any>): string {
for (let i in value) {
if (typeof value[i] === "string") {
value[i] = `"${value[i]}"`
}
}
return `'[${value.join(",")}]'`
}
if (this.client === SqlClients.POSTGRES) {
iterate(mode, (key: string, value: Array<any>) => {
const fieldNames = key.split(/\./g)
const tableName = fieldNames[0]
const columnName = fieldNames[1]
// @ts-ignore
query = query[rawFnc](
`"${tableName}"."${columnName}"::jsonb @> ${stringifyArray(value)}`
)
})
} else if (this.client === SqlClients.MY_SQL) {
iterate(mode, (key: string, value: Array<any>) => {
// @ts-ignore
query = query[rawFnc](
`${not}JSON_CONTAINS(${key}, ${stringifyArray(value)})`
)
})
} else {
iterate(mode, (key: string, value: Array<any>) => {
let andStatement = ""
for (let i in value) {
if (typeof value[i] === "string") {
value[i] = `%"${value[i]}"%`
} else {
value[i] = `%${value[i]}%`
}
andStatement +=
(andStatement ? " AND " : "") +
`LOWER(${likeKey(this.client, key)}) LIKE ?`
}
// @ts-ignore
query = query[rawFnc](andStatement, value)
})
}
}
if (!filters) {
return query
}
@ -227,50 +275,10 @@ class InternalBuilder {
})
}
if (filters.contains) {
const fnc = allOr ? "orWhere" : "where"
const rawFnc = `${fnc}Raw`
function stringifyArray(value: Array<any>): string {
for (let i in value) {
if (typeof value[i] === "string") {
value[i] = `"${value[i]}"`
}
}
return `'[${value.join(",")}]'`
}
if (this.client === SqlClients.POSTGRES) {
iterate(filters.contains, (key: string, value: Array<any>) => {
const fieldNames = key.split(/\./g)
const tableName = fieldNames[0]
const columnName = fieldNames[1]
// @ts-ignore
query = query[rawFnc](
`"${tableName}"."${columnName}"::jsonb @> ${stringifyArray(value)}`
)
})
} else if (this.client === SqlClients.MY_SQL) {
iterate(filters.contains, (key: string, value: Array<any>) => {
// @ts-ignore
query = query[rawFnc](
`JSON_CONTAINS(${key}, ${stringifyArray(value)})`
)
})
} else {
iterate(filters.contains, (key: string, value: Array<any>) => {
let andStatement = ""
for (let i in value) {
if (typeof value[i] === "string") {
value[i] = `%"${value[i]}"%`
} else {
value[i] = `%${value[i]}%`
}
andStatement +=
(andStatement ? " AND " : "") +
`LOWER(${likeKey(this.client, key)}) LIKE ?`
}
// @ts-ignore
query = query[rawFnc](andStatement, value)
})
}
contains(filters.contains)
}
if (filters.notContains) {
contains(filters.notContains)
}
return query
}

View File

@ -284,4 +284,49 @@ describe("SQL query builder", () => {
sql: `select * from (select * from \"${TABLE_NAME}\" where \"${TABLE_NAME}\".\"age\"::jsonb @> '[20]' and \"${TABLE_NAME}\".\"name\"::jsonb @> '["John"]' limit $1) as \"${TABLE_NAME}\"`
})
})
it("should use like expression for MS-SQL when filter is notContains", () => {
const query = new Sql(SqlClients.MS_SQL, 10)._query(generateReadJson({
filters: {
notContains: {
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 NOT JSON_CONTAINS expression for MySQL when filter is notContains", () => {
const query = new Sql(SqlClients.MY_SQL, 10)._query(generateReadJson({
filters: {
notContains: {
age: [20],
name: ["John"]
}
}
}))
expect(query).toEqual({
bindings: [10],
sql: `select * from (select * from \`${TABLE_NAME}\` where NOT JSON_CONTAINS(${TABLE_NAME}.age, '[20]') and NOT JSON_CONTAINS(${TABLE_NAME}.name, '["John"]') limit ?) as \`${TABLE_NAME}\``
})
})
it("should use jsonb operator expression for PostgreSQL when filter is notContains", () => {
const query = new Sql(SqlClients.POSTGRES, 10)._query(generateReadJson({
filters: {
notContains: {
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}\"`
})
})
})

View File

@ -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.24":
version "1.1.24"
resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-1.1.24.tgz#35b15c4a2ff3eaed0a612ff04095017250a30e6d"
integrity sha512-Ab5Ju+2Cggfkz14+BasVgHSrxz73l6U6EtcF2Lb8EwkoLHYUT8llhW4hgrO6fXt5QaQ7mhsoiTUZesyn8Xy/Bg==
"@budibase/backend-core@1.1.25-alpha.1":
version "1.1.25-alpha.1"
resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-1.1.25-alpha.1.tgz#5a3953ddf84bf9db4c4943c86405a72835989ee9"
integrity sha512-C9D9QMXle99vfqXQdsUW/M7SjUpIsYP1ooKamy2+z/qBO3uBpBg6BkASpfNn0iMXZoz0DvQrmsOAuCoMxPvx2A==
dependencies:
"@budibase/types" "^1.1.24"
"@budibase/types" "^1.1.25-alpha.1"
"@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.24":
version "1.1.24"
resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-1.1.24.tgz#4fd5d1b162308cb52d2cebbe9ede05b1f952b727"
integrity sha512-YGloe5IhBVRuGrGgRF1/qvmTICkxmwWAg7cxYPxBszI8IT7Ilisvsm2aBaDFfRYIe9bjzV2PzziiIkwA5qXlXw==
"@budibase/pro@1.1.25-alpha.1":
version "1.1.25-alpha.1"
resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-1.1.25-alpha.1.tgz#ae9527d193832e87f767c7b92c3460fa7cec4cec"
integrity sha512-ALyBFVdMhbat+HZB+DSRHcW2WG4POXdPAOcppreAEgAjUornSdhu9OV1yZOSe8G6DI+VycVP+DHpqVZdwbIJeQ==
dependencies:
"@budibase/backend-core" "1.1.24"
"@budibase/types" "1.1.24"
"@budibase/backend-core" "1.1.25-alpha.1"
"@budibase/types" "1.1.25-alpha.1"
node-fetch "^2.6.1"
"@budibase/standard-components@^0.9.139":
@ -1204,10 +1204,15 @@
svelte-apexcharts "^1.0.2"
svelte-flatpickr "^3.1.0"
"@budibase/types@1.1.24", "@budibase/types@^1.1.24":
version "1.1.24"
resolved "https://registry.yarnpkg.com/@budibase/types/-/types-1.1.24.tgz#87aaab20dc2093b88e036ccb5385ee8a39167cf6"
integrity sha512-ZlzDDBN3uWHCy80lyIZt3IBZB2AQBDgFt9G8b6r5S2rNlPZJQ5u1m58bIcan3NbDNLQOjATZ9opkejIB2qcZnw==
"@budibase/types@1.1.25-alpha.1":
version "1.1.25-alpha.1"
resolved "https://registry.yarnpkg.com/@budibase/types/-/types-1.1.25-alpha.1.tgz#cea7dae315fa5a1981090720068f51e5d72dd427"
integrity sha512-XyRdR03nUJYUJ7bEbthUovJFUsv7TGp39s4ch2O11FrZppRGWeT5anTKyTUoLbZ6Trtkx0iEFf9ubvt3Dk9ctw==
"@budibase/types@^1.1.25-alpha.1":
version "1.1.25"
resolved "https://registry.yarnpkg.com/@budibase/types/-/types-1.1.25.tgz#4d52ac31368de37500a2ae8f8dc02a662d58d49a"
integrity sha512-K74BqAZiM+4URVvGPXhAVE3r+lLQoQ/LOFY30fAvAOv6WMJsw5r7NpF4m1l7bevPxZ6+ku1q/RnoI9aRGqdLlg==
"@bull-board/api@3.7.0":
version "3.7.0"

View File

@ -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.24":
version "1.1.24"
resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-1.1.24.tgz#35b15c4a2ff3eaed0a612ff04095017250a30e6d"
integrity sha512-Ab5Ju+2Cggfkz14+BasVgHSrxz73l6U6EtcF2Lb8EwkoLHYUT8llhW4hgrO6fXt5QaQ7mhsoiTUZesyn8Xy/Bg==
"@budibase/backend-core@1.1.25-alpha.1":
version "1.1.25-alpha.1"
resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-1.1.25-alpha.1.tgz#5a3953ddf84bf9db4c4943c86405a72835989ee9"
integrity sha512-C9D9QMXle99vfqXQdsUW/M7SjUpIsYP1ooKamy2+z/qBO3uBpBg6BkASpfNn0iMXZoz0DvQrmsOAuCoMxPvx2A==
dependencies:
"@budibase/types" "^1.1.24"
"@budibase/types" "^1.1.25-alpha.1"
"@techpass/passport-openidconnect" "0.3.2"
aws-sdk "2.1030.0"
bcrypt "5.0.1"
@ -324,19 +324,24 @@
uuid "8.3.2"
zlib "1.0.5"
"@budibase/pro@1.1.24":
version "1.1.24"
resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-1.1.24.tgz#4fd5d1b162308cb52d2cebbe9ede05b1f952b727"
integrity sha512-YGloe5IhBVRuGrGgRF1/qvmTICkxmwWAg7cxYPxBszI8IT7Ilisvsm2aBaDFfRYIe9bjzV2PzziiIkwA5qXlXw==
"@budibase/pro@1.1.25-alpha.1":
version "1.1.25-alpha.1"
resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-1.1.25-alpha.1.tgz#ae9527d193832e87f767c7b92c3460fa7cec4cec"
integrity sha512-ALyBFVdMhbat+HZB+DSRHcW2WG4POXdPAOcppreAEgAjUornSdhu9OV1yZOSe8G6DI+VycVP+DHpqVZdwbIJeQ==
dependencies:
"@budibase/backend-core" "1.1.24"
"@budibase/types" "1.1.24"
"@budibase/backend-core" "1.1.25-alpha.1"
"@budibase/types" "1.1.25-alpha.1"
node-fetch "^2.6.1"
"@budibase/types@1.1.24", "@budibase/types@^1.1.24":
version "1.1.24"
resolved "https://registry.yarnpkg.com/@budibase/types/-/types-1.1.24.tgz#87aaab20dc2093b88e036ccb5385ee8a39167cf6"
integrity sha512-ZlzDDBN3uWHCy80lyIZt3IBZB2AQBDgFt9G8b6r5S2rNlPZJQ5u1m58bIcan3NbDNLQOjATZ9opkejIB2qcZnw==
"@budibase/types@1.1.25-alpha.1":
version "1.1.25-alpha.1"
resolved "https://registry.yarnpkg.com/@budibase/types/-/types-1.1.25-alpha.1.tgz#cea7dae315fa5a1981090720068f51e5d72dd427"
integrity sha512-XyRdR03nUJYUJ7bEbthUovJFUsv7TGp39s4ch2O11FrZppRGWeT5anTKyTUoLbZ6Trtkx0iEFf9ubvt3Dk9ctw==
"@budibase/types@^1.1.25-alpha.1":
version "1.1.25"
resolved "https://registry.yarnpkg.com/@budibase/types/-/types-1.1.25.tgz#4d52ac31368de37500a2ae8f8dc02a662d58d49a"
integrity sha512-K74BqAZiM+4URVvGPXhAVE3r+lLQoQ/LOFY30fAvAOv6WMJsw5r7NpF4m1l7bevPxZ6+ku1q/RnoI9aRGqdLlg==
"@cspotcode/source-map-consumer@0.8.0":
version "0.8.0"