Updating the filter system to allow adding multiple filter properties of the same name at once, as well as enabling the use of the allOr property from within the UI - resolves an old issue #2585.
This commit is contained in:
parent
4a1b99f346
commit
129c966226
|
@ -9,19 +9,26 @@
|
|||
Input,
|
||||
Layout,
|
||||
Select,
|
||||
Toggle,
|
||||
Label,
|
||||
} from "@budibase/bbui"
|
||||
import DrawerBindableInput from "components/common/bindings/DrawerBindableInput.svelte"
|
||||
import ClientBindingPanel from "components/common/bindings/ClientBindingPanel.svelte"
|
||||
import { generate } from "shortid"
|
||||
import { LuceneUtils, Constants } from "@budibase/frontend-core"
|
||||
import { getFields } from "helpers/searchFields"
|
||||
import { createEventDispatcher } from "svelte"
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
export let schemaFields
|
||||
export let filters = []
|
||||
export let bindings = []
|
||||
export let panel = ClientBindingPanel
|
||||
export let allowBindings = true
|
||||
export let allOr = false
|
||||
|
||||
$: dispatch("change", filters)
|
||||
$: enrichedSchemaFields = getFields(schemaFields || [])
|
||||
$: fieldOptions = enrichedSchemaFields.map(field => field.name) || []
|
||||
$: valueTypeOptions = allowBindings ? ["Value", "Binding"] : ["Value"]
|
||||
|
@ -69,7 +76,7 @@
|
|||
}
|
||||
|
||||
// if changed to an array, change default value to empty array
|
||||
const idx = filters.findIndex(x => x.field === field)
|
||||
const idx = filters.findIndex(x => x.id === expression.id)
|
||||
if (expression.type === "array") {
|
||||
filters[idx].value = []
|
||||
} else {
|
||||
|
@ -179,10 +186,16 @@
|
|||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
<div>
|
||||
<div class="bottom">
|
||||
<Button icon="AddCircle" size="M" secondary on:click={addFilter}>
|
||||
Add filter
|
||||
</Button>
|
||||
<div class="toggle">
|
||||
<Toggle
|
||||
value={allOr}
|
||||
on:change={event => (allOr = event.detail)}
|
||||
/><Label size="L">OR conditions</Label>
|
||||
</div>
|
||||
</div>
|
||||
</Layout>
|
||||
</div>
|
||||
|
@ -202,4 +215,16 @@
|
|||
align-items: center;
|
||||
grid-template-columns: 1fr 120px 120px 1fr auto auto;
|
||||
}
|
||||
|
||||
.toggle {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding-right: var(--spacing-s);
|
||||
}
|
||||
|
||||
.bottom {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -8,21 +8,73 @@
|
|||
import FilterDrawer from "./FilterDrawer.svelte"
|
||||
import { currentAsset } from "builderStore"
|
||||
|
||||
const QUERY_START_REGEX = /\d[0-9]*:/g
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
export let value = []
|
||||
export let componentInstance
|
||||
export let bindings = []
|
||||
|
||||
let drawer
|
||||
let tempValue = value || []
|
||||
let drawer,
|
||||
toSaveFilters = null,
|
||||
allOr,
|
||||
initialAllOr
|
||||
|
||||
$: initialFilters = correctFilters(value || [])
|
||||
$: dataSource = getDatasourceForProvider($currentAsset, componentInstance)
|
||||
$: schema = getSchemaForDatasource($currentAsset, dataSource)?.schema
|
||||
$: schemaFields = Object.values(schema || {})
|
||||
|
||||
const saveFilter = async () => {
|
||||
dispatch("change", tempValue)
|
||||
function addNumbering(filters) {
|
||||
let count = 1
|
||||
for (let value of filters) {
|
||||
if (value.field && value.field?.match(QUERY_START_REGEX) == null) {
|
||||
value.field = `${count++}:${value.field}`
|
||||
}
|
||||
}
|
||||
return filters
|
||||
}
|
||||
|
||||
function correctFilters(filters) {
|
||||
const corrected = []
|
||||
for (let filter of filters) {
|
||||
let field = filter.field
|
||||
if (filter.operator === "allOr") {
|
||||
initialAllOr = allOr = true
|
||||
continue
|
||||
}
|
||||
if (
|
||||
typeof filter.field === "string" &&
|
||||
filter.field.match(QUERY_START_REGEX) != null
|
||||
) {
|
||||
const parts = field.split(":")
|
||||
const number = parts[0]
|
||||
// it's the new format, remove number
|
||||
if (!isNaN(parseInt(number))) {
|
||||
parts.shift()
|
||||
field = parts.join(":")
|
||||
}
|
||||
}
|
||||
corrected.push({
|
||||
...filter,
|
||||
field,
|
||||
})
|
||||
}
|
||||
return corrected
|
||||
}
|
||||
|
||||
async function saveFilter() {
|
||||
if (!toSaveFilters && allOr !== initialAllOr) {
|
||||
toSaveFilters = initialFilters
|
||||
}
|
||||
const filters = toSaveFilters?.filter(filter => filter.operator !== "allOr")
|
||||
if (allOr && filters) {
|
||||
filters.push({ operator: "allOr" })
|
||||
}
|
||||
// only save if anything was updated
|
||||
if (filters) {
|
||||
dispatch("change", addNumbering(filters))
|
||||
}
|
||||
notifications.success("Filters saved.")
|
||||
drawer.hide()
|
||||
}
|
||||
|
@ -33,8 +85,12 @@
|
|||
<Button cta slot="buttons" on:click={saveFilter}>Save</Button>
|
||||
<FilterDrawer
|
||||
slot="body"
|
||||
bind:filters={tempValue}
|
||||
filters={initialFilters}
|
||||
{bindings}
|
||||
{schemaFields}
|
||||
bind:allOr
|
||||
on:change={event => {
|
||||
toSaveFilters = event.detail
|
||||
}}
|
||||
/>
|
||||
</Drawer>
|
||||
|
|
|
@ -103,6 +103,10 @@ export const buildLuceneQuery = filter => {
|
|||
const isHbs =
|
||||
typeof value === "string" && value.match(HBS_REGEX)?.length > 0
|
||||
// Parse all values into correct types
|
||||
if (operator === "allOr") {
|
||||
query.allOr = true
|
||||
return
|
||||
}
|
||||
if (type === "datetime") {
|
||||
// Ensure date value is a valid date and parse into correct format
|
||||
if (!value) {
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
const { SearchIndexes } = require("../../../db/utils")
|
||||
const { removeKeyNumbering } = require("./utils")
|
||||
const fetch = require("node-fetch")
|
||||
const { getCouchInfo } = require("@budibase/backend-core/db")
|
||||
const { getAppId } = require("@budibase/backend-core/context")
|
||||
|
@ -197,6 +198,8 @@ class QueryBuilder {
|
|||
|
||||
function build(structure, queryFn) {
|
||||
for (let [key, value] of Object.entries(structure)) {
|
||||
// check for new format - remove numbering if needed
|
||||
key = removeKeyNumbering(key)
|
||||
key = builder.preprocess(key.replace(/ /g, "_"), {
|
||||
escape: true,
|
||||
})
|
||||
|
|
|
@ -3,8 +3,11 @@ const { cloneDeep } = require("lodash/fp")
|
|||
const { InternalTables } = require("../../../db/utils")
|
||||
const userController = require("../user")
|
||||
const { FieldTypes } = require("../../../constants")
|
||||
const { makeExternalQuery } = require("../../../integrations/base/utils")
|
||||
const { getAppDB } = require("@budibase/backend-core/context")
|
||||
const {
|
||||
makeExternalQuery,
|
||||
removeKeyNumbering,
|
||||
} = require("../../../integrations/base/utils")
|
||||
|
||||
validateJs.extend(validateJs.validators.datetime, {
|
||||
parse: function (value) {
|
||||
|
@ -16,6 +19,8 @@ validateJs.extend(validateJs.validators.datetime, {
|
|||
},
|
||||
})
|
||||
|
||||
exports.removeKeyNumbering = removeKeyNumbering
|
||||
|
||||
exports.getDatasourceAndQuery = async json => {
|
||||
const datasourceId = json.endpoint.datasourceId
|
||||
const db = getAppDB()
|
||||
|
|
|
@ -10,6 +10,7 @@ import {
|
|||
import { isIsoDateString, SqlClients } from "../utils"
|
||||
import SqlTableQueryBuilder from "./sqlTable"
|
||||
import environment from "../../environment"
|
||||
import { removeKeyNumbering } from "./utils"
|
||||
|
||||
const envLimit = environment.SQL_MAX_ROWS
|
||||
? parseInt(environment.SQL_MAX_ROWS)
|
||||
|
@ -133,12 +134,13 @@ class InternalBuilder {
|
|||
fn: (key: string, value: any) => void
|
||||
) {
|
||||
for (let [key, value] of Object.entries(structure)) {
|
||||
const isRelationshipField = key.includes(".")
|
||||
const updatedKey = removeKeyNumbering(key)
|
||||
const isRelationshipField = updatedKey.includes(".")
|
||||
if (!opts.relationship && !isRelationshipField) {
|
||||
fn(`${opts.tableName}.${key}`, value)
|
||||
fn(`${opts.tableName}.${updatedKey}`, value)
|
||||
}
|
||||
if (opts.relationship && isRelationshipField) {
|
||||
fn(key, value)
|
||||
fn(updatedKey, value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,22 +1,30 @@
|
|||
import { QueryJson } from "../../definitions/datasource"
|
||||
import { Datasource } from "../../definitions/common"
|
||||
const { integrations } = require("../index")
|
||||
|
||||
module DatasourceUtils {
|
||||
const { integrations } = require("../index")
|
||||
const QUERY_START_REGEX = /\d[0-9]*:/g
|
||||
|
||||
export async function makeExternalQuery(
|
||||
datasource: Datasource,
|
||||
json: QueryJson
|
||||
) {
|
||||
const Integration = integrations[datasource.source]
|
||||
// query is the opinionated function
|
||||
if (Integration.prototype.query) {
|
||||
const integration = new Integration(datasource.config)
|
||||
return integration.query(json)
|
||||
} else {
|
||||
throw "Datasource does not support query."
|
||||
}
|
||||
export async function makeExternalQuery(
|
||||
datasource: Datasource,
|
||||
json: QueryJson
|
||||
) {
|
||||
const Integration = integrations[datasource.source]
|
||||
// query is the opinionated function
|
||||
if (Integration.prototype.query) {
|
||||
const integration = new Integration(datasource.config)
|
||||
return integration.query(json)
|
||||
} else {
|
||||
throw "Datasource does not support query."
|
||||
}
|
||||
}
|
||||
|
||||
export function removeKeyNumbering(key: any): string {
|
||||
if (typeof key === "string" && key.match(QUERY_START_REGEX) != null) {
|
||||
const parts = key.split(":")
|
||||
// remove the number
|
||||
parts.shift()
|
||||
return parts.join(":")
|
||||
} else {
|
||||
return key
|
||||
}
|
||||
|
||||
module.exports.makeExternalQuery = makeExternalQuery
|
||||
}
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import { findHBSBlocks, processStringSync } from "@budibase/string-templates"
|
||||
import { Integration } from "../../definitions/datasource"
|
||||
import { DatasourcePlus } from "../base/datasourcePlus"
|
||||
|
||||
const CONST_CHAR_REGEX = new RegExp("'[^']*'", "g")
|
||||
|
|
Loading…
Reference in New Issue