diff --git a/.github/workflows/deploy-release.yml b/.github/workflows/deploy-release.yml
index 0fb8a5fea0..024b97597f 100644
--- a/.github/workflows/deploy-release.yml
+++ b/.github/workflows/deploy-release.yml
@@ -68,6 +68,13 @@ jobs:
]
env:
KUBECONFIG_FILE: '${{ secrets.RELEASE_KUBECONFIG }}'
+
+ - name: Re roll the services
+ uses: actions-hub/kubectl@master
+ env:
+ KUBE_CONFIG: ${{ secrets.RELEASE_KUBECONFIG }}
+ with:
+ args: rollout restart deployment proxy-service -n budibase && kubectl rollout restart deployment app-service -n budibase && kubectl rollout restart deployment worker-service -n budibase
- name: Discord Webhook Action
uses: tsickert/discord-webhook@v4.0.0
diff --git a/.github/workflows/release-develop.yml b/.github/workflows/release-develop.yml
index 772fcf933d..067d0eb0dd 100644
--- a/.github/workflows/release-develop.yml
+++ b/.github/workflows/release-develop.yml
@@ -121,6 +121,13 @@ jobs:
env:
KUBECONFIG_FILE: '${{ secrets.RELEASE_KUBECONFIG }}'
+ - name: Re roll the services
+ uses: actions-hub/kubectl@master
+ env:
+ KUBE_CONFIG: ${{ secrets.RELEASE_KUBECONFIG }}
+ with:
+ args: rollout restart deployment proxy-service -n budibase && kubectl rollout restart deployment app-service -n budibase && kubectl rollout restart deployment worker-service -n budibase
+
- name: Discord Webhook Action
uses: tsickert/discord-webhook@v4.0.0
with:
diff --git a/lerna.json b/lerna.json
index 389d00264d..2ad36f4f7f 100644
--- a/lerna.json
+++ b/lerna.json
@@ -1,5 +1,5 @@
{
- "version": "1.2.39-alpha.5",
+ "version": "1.2.39-alpha.7",
"npmClient": "yarn",
"packages": [
"packages/*"
diff --git a/packages/backend-core/package.json b/packages/backend-core/package.json
index 93484c3b11..575c3357f3 100644
--- a/packages/backend-core/package.json
+++ b/packages/backend-core/package.json
@@ -1,6 +1,6 @@
{
"name": "@budibase/backend-core",
- "version": "1.2.39-alpha.5",
+ "version": "1.2.39-alpha.7",
"description": "Budibase backend core libraries used in server and worker",
"main": "dist/src/index.js",
"types": "dist/src/index.d.ts",
@@ -20,7 +20,7 @@
"test:watch": "jest --watchAll"
},
"dependencies": {
- "@budibase/types": "1.2.39-alpha.5",
+ "@budibase/types": "1.2.39-alpha.7",
"@techpass/passport-openidconnect": "0.3.2",
"aws-sdk": "2.1030.0",
"bcrypt": "5.0.1",
diff --git a/packages/bbui/package.json b/packages/bbui/package.json
index cedd4722a4..0b8c664def 100644
--- a/packages/bbui/package.json
+++ b/packages/bbui/package.json
@@ -1,7 +1,7 @@
{
"name": "@budibase/bbui",
"description": "A UI solution used in the different Budibase projects.",
- "version": "1.2.39-alpha.5",
+ "version": "1.2.39-alpha.7",
"license": "MPL-2.0",
"svelte": "src/index.js",
"module": "dist/bbui.es.js",
@@ -38,7 +38,7 @@
],
"dependencies": {
"@adobe/spectrum-css-workflow-icons": "^1.2.1",
- "@budibase/string-templates": "1.2.39-alpha.5",
+ "@budibase/string-templates": "1.2.39-alpha.7",
"@spectrum-css/actionbutton": "^1.0.1",
"@spectrum-css/actiongroup": "^1.0.1",
"@spectrum-css/avatar": "^3.0.2",
diff --git a/packages/bbui/src/Form/Core/Multiselect.svelte b/packages/bbui/src/Form/Core/Multiselect.svelte
index 9dd5a25a4f..eb39e39042 100644
--- a/packages/bbui/src/Form/Core/Multiselect.svelte
+++ b/packages/bbui/src/Form/Core/Multiselect.svelte
@@ -23,7 +23,7 @@
$: toggleOption = makeToggleOption(selectedLookupMap, value)
const getFieldText = (value, map, placeholder) => {
- if (value?.length) {
+ if (Array.isArray(value) && value.length > 0) {
if (!map) {
return ""
}
@@ -36,7 +36,7 @@
const getSelectedLookupMap = value => {
let map = {}
- if (value?.length) {
+ if (Array.isArray(value) && value.length > 0) {
value.forEach(option => {
if (option) {
map[option] = true
diff --git a/packages/builder/package.json b/packages/builder/package.json
index 82ae021e12..224541f838 100644
--- a/packages/builder/package.json
+++ b/packages/builder/package.json
@@ -1,6 +1,6 @@
{
"name": "@budibase/builder",
- "version": "1.2.39-alpha.5",
+ "version": "1.2.39-alpha.7",
"license": "GPL-3.0",
"private": true,
"scripts": {
@@ -69,10 +69,10 @@
}
},
"dependencies": {
- "@budibase/bbui": "1.2.39-alpha.5",
- "@budibase/client": "1.2.39-alpha.5",
- "@budibase/frontend-core": "1.2.39-alpha.5",
- "@budibase/string-templates": "1.2.39-alpha.5",
+ "@budibase/bbui": "1.2.39-alpha.7",
+ "@budibase/client": "1.2.39-alpha.7",
+ "@budibase/frontend-core": "1.2.39-alpha.7",
+ "@budibase/string-templates": "1.2.39-alpha.7",
"@sentry/browser": "5.19.1",
"@spectrum-css/page": "^3.0.1",
"@spectrum-css/vars": "^3.0.1",
diff --git a/packages/builder/src/components/backend/DataTable/DataTable.svelte b/packages/builder/src/components/backend/DataTable/DataTable.svelte
index 37742626cd..7cb368830e 100644
--- a/packages/builder/src/components/backend/DataTable/DataTable.svelte
+++ b/packages/builder/src/components/backend/DataTable/DataTable.svelte
@@ -167,6 +167,7 @@
{/if}
diff --git a/packages/builder/src/components/backend/DataTable/buttons/ImportButton.svelte b/packages/builder/src/components/backend/DataTable/buttons/ImportButton.svelte
index 6b9c3dd6dd..99d39a0d48 100644
--- a/packages/builder/src/components/backend/DataTable/buttons/ImportButton.svelte
+++ b/packages/builder/src/components/backend/DataTable/buttons/ImportButton.svelte
@@ -3,11 +3,12 @@
import ImportModal from "../modals/ImportModal.svelte"
export let tableId
+ export let disabled
let modal
-
+
Import
diff --git a/packages/builder/src/components/design/settings/controls/FilterEditor/FilterDrawer.svelte b/packages/builder/src/components/design/settings/controls/FilterEditor/FilterDrawer.svelte
index fcec0e35ac..9bd66a4cb1 100644
--- a/packages/builder/src/components/design/settings/controls/FilterEditor/FilterDrawer.svelte
+++ b/packages/builder/src/components/design/settings/controls/FilterEditor/FilterDrawer.svelte
@@ -3,6 +3,7 @@
Body,
Button,
Combobox,
+ Multiselect,
DatePicker,
DrawerContent,
Icon,
@@ -97,6 +98,16 @@
if (expression.noValue) {
expression.value = null
}
+ if (
+ operator === Constants.OperatorOptions.In.value &&
+ !Array.isArray(expression.value)
+ ) {
+ if (expression.value) {
+ expression.value = [expression.value]
+ } else {
+ expression.value = []
+ }
+ }
}
const getFieldOptions = field => {
@@ -169,7 +180,13 @@
/>
{:else if ["string", "longform", "number", "formula"].includes(filter.type)}
- {:else if ["options", "array"].includes(filter.type)}
+ {:else if filter.type === "array" || (filter.type === "options" && filter.operator === "oneOf")}
+
+ {:else if filter.type === "options"}
{
} else if (type === "number") {
return numOps
} else if (type === "options") {
- return [Op.Equals, Op.NotEquals, Op.Empty, Op.NotEmpty]
+ return [Op.Equals, Op.NotEquals, Op.Empty, Op.NotEmpty, Op.In]
} else if (type === "array") {
- return [Op.Contains, Op.NotContains, Op.Empty, Op.NotEmpty]
+ return [Op.Contains, Op.NotContains, Op.Empty, Op.NotEmpty, Op.ContainsAny]
} else if (type === "boolean") {
return [Op.Equals, Op.NotEquals, Op.Empty, Op.NotEmpty]
} else if (type === "longform") {
@@ -96,6 +96,7 @@ export const buildLuceneQuery = filter => {
contains: {},
notContains: {},
oneOf: {},
+ containsAny: {},
}
if (Array.isArray(filter)) {
filter.forEach(expression => {
@@ -128,6 +129,13 @@ export const buildLuceneQuery = filter => {
if (type === "boolean") {
value = `${value}`?.toLowerCase() === "true"
}
+ if (
+ ["contains", "notContains", "containsAny"].includes(operator) &&
+ type === "array" &&
+ typeof value === "string"
+ ) {
+ value = value.split(",")
+ }
if (operator.startsWith("range")) {
const minint =
SqlNumberTypeRangeMap[externalType]?.min || Number.MIN_SAFE_INTEGER
@@ -244,6 +252,18 @@ export const runLuceneQuery = (docs, query) => {
return !testValue?.includes(docValue)
})
+ const containsAny = match("containsAny", (docValue, testValue) => {
+ return !docValue?.includes(...testValue)
+ })
+
+ const contains = match("contains", (docValue, testValue) => {
+ return !testValue?.every(item => docValue?.includes(item))
+ })
+
+ const notContains = match("notContains", (docValue, testValue) => {
+ return testValue?.every(item => docValue?.includes(item))
+ })
+
// Match a document against all criteria
const docMatch = doc => {
return (
@@ -254,7 +274,10 @@ export const runLuceneQuery = (docs, query) => {
notEqualMatch(doc) &&
emptyMatch(doc) &&
notEmptyMatch(doc) &&
- oneOf(doc)
+ oneOf(doc) &&
+ contains(doc) &&
+ containsAny(doc) &&
+ notContains(doc)
)
}
diff --git a/packages/server/package.json b/packages/server/package.json
index 894e67030f..6165cf495e 100644
--- a/packages/server/package.json
+++ b/packages/server/package.json
@@ -1,7 +1,7 @@
{
"name": "@budibase/server",
"email": "hi@budibase.com",
- "version": "1.2.39-alpha.5",
+ "version": "1.2.39-alpha.7",
"description": "Budibase Web Server",
"main": "src/index.ts",
"repository": {
@@ -77,11 +77,11 @@
"license": "GPL-3.0",
"dependencies": {
"@apidevtools/swagger-parser": "10.0.3",
- "@budibase/backend-core": "1.2.39-alpha.5",
- "@budibase/client": "1.2.39-alpha.5",
- "@budibase/pro": "1.2.39-alpha.5",
- "@budibase/string-templates": "1.2.39-alpha.5",
- "@budibase/types": "1.2.39-alpha.5",
+ "@budibase/backend-core": "1.2.39-alpha.7",
+ "@budibase/client": "1.2.39-alpha.7",
+ "@budibase/pro": "1.2.39-alpha.7",
+ "@budibase/string-templates": "1.2.39-alpha.7",
+ "@budibase/types": "1.2.39-alpha.7",
"@bull-board/api": "3.7.0",
"@bull-board/koa": "3.9.4",
"@elastic/elasticsearch": "7.10.0",
diff --git a/packages/server/src/api/controllers/row/internalSearch.js b/packages/server/src/api/controllers/row/internalSearch.js
index 5708cd85f3..3cf60fbcc0 100644
--- a/packages/server/src/api/controllers/row/internalSearch.js
+++ b/packages/server/src/api/controllers/row/internalSearch.js
@@ -21,6 +21,8 @@ class QueryBuilder {
notEmpty: {},
oneOf: {},
contains: {},
+ notContains: {},
+ containsAny: {},
...base,
}
this.limit = 50
@@ -126,6 +128,16 @@ class QueryBuilder {
return this
}
+ addNotContains(key, value) {
+ this.query.notContains[key] = value
+ return this
+ }
+
+ addContainsAny(key, value) {
+ this.query.containsAny[key] = value
+ return this
+ }
+
/**
* Preprocesses a value before going into a lucene search.
* Transforms strings to lowercase and wraps strings and bools in quotes.
@@ -171,11 +183,29 @@ class QueryBuilder {
return `${key}:${builder.preprocess(value, allPreProcessingOpts)}`
}
- const contains = (key, value) => {
- if (!value && value !== 0) {
+ const contains = (key, value, mode = "AND") => {
+ if (Array.isArray(value) && value.length === 0) {
return null
}
- return `${key}:${builder.preprocess(value, { escape: true })}`
+ if (!Array.isArray(value)) {
+ return `${key}:${value}`
+ }
+ let statement = `${builder.preprocess(value[0], { escape: true })}`
+ for (let i = 1; i < value.length; i++) {
+ statement += ` ${mode} ${builder.preprocess(value[i], {
+ escape: true,
+ })}`
+ }
+ return `${key}:(${statement})`
+ }
+
+ const notContains = (key, value) => {
+ const allPrefix = allOr === "" ? "*:* AND" : ""
+ return allPrefix + "NOT " + contains(key, value)
+ }
+
+ const containsAny = (key, value) => {
+ return contains(key, value, "OR")
}
const oneOf = (key, value) => {
@@ -278,6 +308,12 @@ class QueryBuilder {
if (this.query.contains) {
build(this.query.contains, contains)
}
+ if (this.query.notContains) {
+ build(this.query.notContains, notContains)
+ }
+ if (this.query.containsAny) {
+ build(this.query.containsAny, containsAny)
+ }
// make sure table ID is always added as an AND
if (tableId) {
query = `(${query})`
diff --git a/packages/server/src/api/routes/tests/internalSearch.spec.js b/packages/server/src/api/routes/tests/internalSearch.spec.js
index 50ee2b26ac..3b478eda17 100644
--- a/packages/server/src/api/routes/tests/internalSearch.spec.js
+++ b/packages/server/src/api/routes/tests/internalSearch.spec.js
@@ -129,9 +129,10 @@ describe("internal search", () => {
const response = await search.paginatedSearch({
contains: {
"column": "a",
+ "colArr": [1, 2, 3],
},
}, PARAMS)
- checkLucene(response, `*:* AND column:a`, PARAMS)
+ checkLucene(response, `*:* AND column:a AND colArr:(1 AND 2 AND 3)`, PARAMS)
})
it("test multiple of same column", async () => {
@@ -154,4 +155,22 @@ describe("internal search", () => {
}, PARAMS)
checkLucene(response, `*:* AND 1\\:column:"a"`, PARAMS)
})
+
+ it("test containsAny query", async () => {
+ const response = await search.paginatedSearch({
+ containsAny: {
+ "column": ["a", "b", "c"]
+ },
+ }, PARAMS)
+ checkLucene(response, `*:* AND column:(a OR b OR c)`, PARAMS)
+ })
+
+ it("test notContains query", async () => {
+ const response = await search.paginatedSearch({
+ notContains: {
+ "column": ["a", "b", "c"]
+ },
+ }, PARAMS)
+ checkLucene(response, `*:* AND NOT column:(a AND b AND c)`, PARAMS)
+ })
})
\ No newline at end of file
diff --git a/packages/server/src/integrations/base/sql.ts b/packages/server/src/integrations/base/sql.ts
index 1eb0dc5292..f18e9d1d98 100644
--- a/packages/server/src/integrations/base/sql.ts
+++ b/packages/server/src/integrations/base/sql.ts
@@ -159,6 +159,61 @@ class InternalBuilder {
}
}
+ const contains = (mode: object, any: boolean = false) => {
+ const fnc = allOr ? "orWhere" : "where"
+ const rawFnc = `${fnc}Raw`
+ const not = mode === filters?.notContains ? "NOT " : ""
+ function stringifyArray(value: Array, quoteStyle = '"'): string {
+ for (let i in value) {
+ if (typeof value[i] === "string") {
+ value[i] = `${quoteStyle}${value[i]}${quoteStyle}`
+ }
+ }
+ return `[${value.join(",")}]`
+ }
+ if (this.client === SqlClient.POSTGRES) {
+ iterate(mode, (key: string, value: Array) => {
+ const wrap = any ? "" : "'"
+ const containsOp = any ? "\\?| array" : "@>"
+ const fieldNames = key.split(/\./g)
+ const tableName = fieldNames[0]
+ const columnName = fieldNames[1]
+ // @ts-ignore
+ query = query[rawFnc](
+ `${not}"${tableName}"."${columnName}"::jsonb ${containsOp} ${wrap}${stringifyArray(
+ value,
+ any ? "'" : '"'
+ )}${wrap}`
+ )
+ })
+ } else if (this.client === SqlClient.MY_SQL) {
+ const jsonFnc = any ? "JSON_OVERLAPS" : "JSON_CONTAINS"
+ iterate(mode, (key: string, value: Array) => {
+ // @ts-ignore
+ query = query[rawFnc](
+ `${not}${jsonFnc}(${key}, '${stringifyArray(value)}')`
+ )
+ })
+ } else {
+ const andOr = mode === filters?.containsAny ? " OR " : " AND "
+ iterate(mode, (key: string, value: Array) => {
+ let statement = ""
+ for (let i in value) {
+ if (typeof value[i] === "string") {
+ value[i] = `%"${value[i]}"%`
+ } else {
+ value[i] = `%${value[i]}%`
+ }
+ statement +=
+ (statement ? andOr : "") +
+ `LOWER(${likeKey(this.client, key)}) LIKE ?`
+ }
+ // @ts-ignore
+ query = query[rawFnc](`${not}(${statement})`, value)
+ })
+ }
+ }
+
if (!filters) {
return query
}
@@ -229,32 +284,13 @@ class InternalBuilder {
})
}
if (filters.contains) {
- const fnc = allOr ? "orWhere" : "where"
- const rawFnc = `${fnc}Raw`
- if (this.client === SqlClient.POSTGRES) {
- iterate(filters.contains, (key: string, value: any) => {
- 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}]'`
- )
- })
- } else if (this.client === SqlClient.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)
- }
+ contains(filters.contains)
+ }
+ if (filters.notContains) {
+ contains(filters.notContains)
+ }
+ if (filters.containsAny) {
+ contains(filters.containsAny, true)
}
return query
}
diff --git a/packages/server/src/integrations/tests/sql.spec.js b/packages/server/src/integrations/tests/sql.spec.js
index 3d748a03c0..3cc9f0fb3e 100644
--- a/packages/server/src/integrations/tests/sql.spec.js
+++ b/packages/server/src/integrations/tests/sql.spec.js
@@ -240,18 +240,18 @@ describe("SQL query builder", () => {
})
})
- it("should use like expression for MS-SQL when filter is contains", () => {
+ it("should use AND like expression for MS-SQL when filter is contains", () => {
const query = new Sql(SqlClient.MS_SQL, 10)._query(generateReadJson({
filters: {
contains: {
- age: 20,
- name: "John"
+ age: [20, 25],
+ name: ["John", "Mary"]
}
}
}))
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}]`
+ bindings: [10, "%20%", "%25%", `%"John"%`, `%"Mary"%`],
+ sql: `select * from (select top (@p0) * from [${TABLE_NAME}] where (LOWER(${TABLE_NAME}.age) LIKE @p1 AND LOWER(${TABLE_NAME}.age) LIKE @p2) and (LOWER(${TABLE_NAME}.name) LIKE @p3 AND LOWER(${TABLE_NAME}.name) LIKE @p4)) as [${TABLE_NAME}]`
})
})
@@ -259,14 +259,14 @@ describe("SQL query builder", () => {
const query = new Sql(SqlClient.MY_SQL, 10)._query(generateReadJson({
filters: {
contains: {
- age: 20,
- name: "John"
+ 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}\``
+ 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}\``
})
})
@@ -274,8 +274,8 @@ describe("SQL query builder", () => {
const query = new Sql(SqlClient.POSTGRES, 10)._query(generateReadJson({
filters: {
contains: {
- age: 20,
- name: "John"
+ age: [20],
+ name: ["John"]
}
}
}))
@@ -284,4 +284,94 @@ 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 NOT like expression for MS-SQL when filter is notContains", () => {
+ const query = new Sql(SqlClient.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 NOT (LOWER(${TABLE_NAME}.age) LIKE @p1) and NOT (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(SqlClient.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 NOT expression for PostgreSQL when filter is notContains", () => {
+ const query = new Sql(SqlClient.POSTGRES, 10)._query(generateReadJson({
+ filters: {
+ notContains: {
+ age: [20],
+ name: ["John"]
+ }
+ }
+ }))
+ expect(query).toEqual({
+ bindings: [10],
+ sql: `select * from (select * from \"${TABLE_NAME}\" where NOT \"${TABLE_NAME}\".\"age\"::jsonb @> '[20]' and NOT \"${TABLE_NAME}\".\"name\"::jsonb @> '["John"]' limit $1) as \"${TABLE_NAME}\"`
+ })
+ })
+
+ it("should use OR like expression for MS-SQL when filter is containsAny", () => {
+ const query = new Sql(SqlClient.MS_SQL, 10)._query(generateReadJson({
+ filters: {
+ containsAny: {
+ age: [20, 25],
+ name: ["John", "Mary"]
+ }
+ }
+ }))
+ expect(query).toEqual({
+ bindings: [10, "%20%", "%25%", `%"John"%`, `%"Mary"%`],
+ sql: `select * from (select top (@p0) * from [${TABLE_NAME}] where (LOWER(${TABLE_NAME}.age) LIKE @p1 OR LOWER(${TABLE_NAME}.age) LIKE @p2) and (LOWER(${TABLE_NAME}.name) LIKE @p3 OR LOWER(${TABLE_NAME}.name) LIKE @p4)) as [${TABLE_NAME}]`
+ })
+ })
+
+ it("should use JSON_OVERLAPS expression for MySQL when filter is containsAny", () => {
+ const query = new Sql(SqlClient.MY_SQL, 10)._query(generateReadJson({
+ filters: {
+ containsAny: {
+ age: [20, 25],
+ name: ["John", "Mary"]
+ }
+ }
+ }))
+ expect(query).toEqual({
+ bindings: [10],
+ sql: `select * from (select * from \`${TABLE_NAME}\` where JSON_OVERLAPS(${TABLE_NAME}.age, '[20,25]') and JSON_OVERLAPS(${TABLE_NAME}.name, '["John","Mary"]') limit ?) as \`${TABLE_NAME}\``
+ })
+ })
+
+ it("should use ?| operator expression for PostgreSQL when filter is containsAny", () => {
+ const query = new Sql(SqlClient.POSTGRES, 10)._query(generateReadJson({
+ filters: {
+ containsAny: {
+ age: [20, 25],
+ name: ["John", "Mary"]
+ }
+ }
+ }))
+ expect(query).toEqual({
+ bindings: [10],
+ sql: `select * from (select * from \"${TABLE_NAME}\" where \"${TABLE_NAME}\".\"age\"::jsonb ?| array [20,25] and \"${TABLE_NAME}\".\"name\"::jsonb ?| array ['John','Mary'] limit $1) as \"${TABLE_NAME}\"`
+ })
+ })
})
diff --git a/packages/server/yarn.lock b/packages/server/yarn.lock
index b3bfe2e529..7fd0fde8b3 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.2.39-alpha.5":
- version "1.2.39-alpha.5"
- resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-1.2.39-alpha.5.tgz#fbadac10657e44fb05641a43aa8d9481b86418e9"
- integrity sha512-bZPmKqWUnQ+OVwf2z35WXPfPuxwuL0vlZJNyTgLX7hdhjYfPB4LA0vMftPbvtRFO9k6SdB0XpBOJCm2u5k5KsQ==
+"@budibase/backend-core@1.2.39-alpha.7":
+ version "1.2.39-alpha.7"
+ resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-1.2.39-alpha.7.tgz#79c2b3c1b02b6e5c9a42f5bf7e76ea6b2498a971"
+ integrity sha512-1UVfEetO3sbfa7tC7rOXTWrYjpaTZtYpijZMz1CtdOYJKzCimHVbIWWCmEO8BgSOejtWTvIARjoqCfy6oWFM6w==
dependencies:
- "@budibase/types" "1.2.39-alpha.5"
+ "@budibase/types" "1.2.39-alpha.7"
"@techpass/passport-openidconnect" "0.3.2"
aws-sdk "2.1030.0"
bcrypt "5.0.1"
@@ -1178,13 +1178,13 @@
svelte-flatpickr "^3.2.3"
svelte-portal "^1.0.0"
-"@budibase/pro@1.2.39-alpha.5":
- version "1.2.39-alpha.5"
- resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-1.2.39-alpha.5.tgz#cd652a06bf691ae00c2bf8cd9351e46b684939a5"
- integrity sha512-6M0Mj+AbJi+HpFRJJD1bZDwuWlCq1I3m8vrG4A3Rm/VnJigb1Zi7XTMlwzgCiUaWvowstiJVGHG6Af0Mix4zdQ==
+"@budibase/pro@1.2.39-alpha.7":
+ version "1.2.39-alpha.7"
+ resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-1.2.39-alpha.7.tgz#e913b91168f73859ffc78824dc7bd2c4cb7514ef"
+ integrity sha512-HHlAoH4A35p/5nepHVtQ79IrGAG4U3fPzaX6/gz0Ws/tutYRS8vfvGG0OCXom9mBy8X1xcO/X8+KPrHi0oVpQg==
dependencies:
- "@budibase/backend-core" "1.2.39-alpha.5"
- "@budibase/types" "1.2.39-alpha.5"
+ "@budibase/backend-core" "1.2.39-alpha.7"
+ "@budibase/types" "1.2.39-alpha.7"
"@koa/router" "8.0.8"
joi "17.6.0"
node-fetch "^2.6.1"
@@ -1207,10 +1207,10 @@
svelte-apexcharts "^1.0.2"
svelte-flatpickr "^3.1.0"
-"@budibase/types@1.2.39-alpha.5":
- version "1.2.39-alpha.5"
- resolved "https://registry.yarnpkg.com/@budibase/types/-/types-1.2.39-alpha.5.tgz#632ef72b248d105cb69f4a133d78d3a293e5404e"
- integrity sha512-MgJayPh6t7VGB6ErUwM0sJmJoxG91h3z24fWSE+q3bbH2MKbnkpuNCbOh1wlKyFWqzdRofuFpJGK/O9zWXvRwg==
+"@budibase/types@1.2.39-alpha.7":
+ version "1.2.39-alpha.7"
+ resolved "https://registry.yarnpkg.com/@budibase/types/-/types-1.2.39-alpha.7.tgz#d68d5c83d7368a7a5596c0fa70dea3cc1f8629f0"
+ integrity sha512-R5zOze0jD68M6zbEMRCCy46jBjbktcD9cb/U0YPbaHSQNILPgmrsEdXEhHK7JGByfLtPsmPVd9ccDGWV9kaqNw==
"@bull-board/api@3.7.0":
version "3.7.0"
diff --git a/packages/string-templates/package.json b/packages/string-templates/package.json
index 36854f3642..f4a6995318 100644
--- a/packages/string-templates/package.json
+++ b/packages/string-templates/package.json
@@ -1,6 +1,6 @@
{
"name": "@budibase/string-templates",
- "version": "1.2.39-alpha.5",
+ "version": "1.2.39-alpha.7",
"description": "Handlebars wrapper for Budibase templating.",
"main": "src/index.cjs",
"module": "dist/bundle.mjs",
diff --git a/packages/types/package.json b/packages/types/package.json
index 7455926201..472e727ff1 100644
--- a/packages/types/package.json
+++ b/packages/types/package.json
@@ -1,6 +1,6 @@
{
"name": "@budibase/types",
- "version": "1.2.39-alpha.5",
+ "version": "1.2.39-alpha.7",
"description": "Budibase types",
"main": "dist/index.js",
"types": "dist/index.d.ts",
diff --git a/packages/types/src/sdk/search.ts b/packages/types/src/sdk/search.ts
index 6721fe6b55..954ad42ac9 100644
--- a/packages/types/src/sdk/search.ts
+++ b/packages/types/src/sdk/search.ts
@@ -31,7 +31,13 @@ export interface SearchFilters {
[key: string]: any[]
}
contains?: {
- [key: string]: any
+ [key: string]: any[]
+ }
+ notContains?: {
+ [key: string]: any[]
+ }
+ containsAny?: {
+ [key: string]: any[]
}
}
diff --git a/packages/worker/package.json b/packages/worker/package.json
index aaeff3c56d..bfb28ccaaa 100644
--- a/packages/worker/package.json
+++ b/packages/worker/package.json
@@ -1,7 +1,7 @@
{
"name": "@budibase/worker",
"email": "hi@budibase.com",
- "version": "1.2.39-alpha.5",
+ "version": "1.2.39-alpha.7",
"description": "Budibase background service",
"main": "src/index.ts",
"repository": {
@@ -35,10 +35,10 @@
"author": "Budibase",
"license": "GPL-3.0",
"dependencies": {
- "@budibase/backend-core": "1.2.39-alpha.5",
- "@budibase/pro": "1.2.39-alpha.5",
- "@budibase/string-templates": "1.2.39-alpha.5",
- "@budibase/types": "1.2.39-alpha.5",
+ "@budibase/backend-core": "1.2.39-alpha.7",
+ "@budibase/pro": "1.2.39-alpha.7",
+ "@budibase/string-templates": "1.2.39-alpha.7",
+ "@budibase/types": "1.2.39-alpha.7",
"@koa/router": "8.0.8",
"@sentry/node": "6.17.7",
"@techpass/passport-openidconnect": "0.3.2",
diff --git a/packages/worker/yarn.lock b/packages/worker/yarn.lock
index 380d80a741..c6d779b37a 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.2.39-alpha.5":
- version "1.2.39-alpha.5"
- resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-1.2.39-alpha.5.tgz#fbadac10657e44fb05641a43aa8d9481b86418e9"
- integrity sha512-bZPmKqWUnQ+OVwf2z35WXPfPuxwuL0vlZJNyTgLX7hdhjYfPB4LA0vMftPbvtRFO9k6SdB0XpBOJCm2u5k5KsQ==
+"@budibase/backend-core@1.2.39-alpha.7":
+ version "1.2.39-alpha.7"
+ resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-1.2.39-alpha.7.tgz#79c2b3c1b02b6e5c9a42f5bf7e76ea6b2498a971"
+ integrity sha512-1UVfEetO3sbfa7tC7rOXTWrYjpaTZtYpijZMz1CtdOYJKzCimHVbIWWCmEO8BgSOejtWTvIARjoqCfy6oWFM6w==
dependencies:
- "@budibase/types" "1.2.39-alpha.5"
+ "@budibase/types" "1.2.39-alpha.7"
"@techpass/passport-openidconnect" "0.3.2"
aws-sdk "2.1030.0"
bcrypt "5.0.1"
@@ -325,21 +325,21 @@
uuid "8.3.2"
zlib "1.0.5"
-"@budibase/pro@1.2.39-alpha.5":
- version "1.2.39-alpha.5"
- resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-1.2.39-alpha.5.tgz#cd652a06bf691ae00c2bf8cd9351e46b684939a5"
- integrity sha512-6M0Mj+AbJi+HpFRJJD1bZDwuWlCq1I3m8vrG4A3Rm/VnJigb1Zi7XTMlwzgCiUaWvowstiJVGHG6Af0Mix4zdQ==
+"@budibase/pro@1.2.39-alpha.7":
+ version "1.2.39-alpha.7"
+ resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-1.2.39-alpha.7.tgz#e913b91168f73859ffc78824dc7bd2c4cb7514ef"
+ integrity sha512-HHlAoH4A35p/5nepHVtQ79IrGAG4U3fPzaX6/gz0Ws/tutYRS8vfvGG0OCXom9mBy8X1xcO/X8+KPrHi0oVpQg==
dependencies:
- "@budibase/backend-core" "1.2.39-alpha.5"
- "@budibase/types" "1.2.39-alpha.5"
+ "@budibase/backend-core" "1.2.39-alpha.7"
+ "@budibase/types" "1.2.39-alpha.7"
"@koa/router" "8.0.8"
joi "17.6.0"
node-fetch "^2.6.1"
-"@budibase/types@1.2.39-alpha.5":
- version "1.2.39-alpha.5"
- resolved "https://registry.yarnpkg.com/@budibase/types/-/types-1.2.39-alpha.5.tgz#632ef72b248d105cb69f4a133d78d3a293e5404e"
- integrity sha512-MgJayPh6t7VGB6ErUwM0sJmJoxG91h3z24fWSE+q3bbH2MKbnkpuNCbOh1wlKyFWqzdRofuFpJGK/O9zWXvRwg==
+"@budibase/types@1.2.39-alpha.7":
+ version "1.2.39-alpha.7"
+ resolved "https://registry.yarnpkg.com/@budibase/types/-/types-1.2.39-alpha.7.tgz#d68d5c83d7368a7a5596c0fa70dea3cc1f8629f0"
+ integrity sha512-R5zOze0jD68M6zbEMRCCy46jBjbktcD9cb/U0YPbaHSQNILPgmrsEdXEhHK7JGByfLtPsmPVd9ccDGWV9kaqNw==
"@cspotcode/source-map-consumer@0.8.0":
version "0.8.0"