Add contains option to lucene query builder
This commit is contained in:
parent
dbd0d76613
commit
d55218e813
|
@ -168,6 +168,13 @@ export function makeDatasourceFormComponents(datasource) {
|
|||
optionsSource: "schema",
|
||||
})
|
||||
}
|
||||
if (fieldType === "array") {
|
||||
component.customProps({
|
||||
placeholder: "Choose an option",
|
||||
optionsSource: "schema",
|
||||
})
|
||||
}
|
||||
|
||||
if (fieldType === "link") {
|
||||
let placeholder =
|
||||
fieldSchema.relationshipType === "one-to-many"
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
DrawerContent,
|
||||
Layout,
|
||||
Body,
|
||||
Multiselect,
|
||||
} from "@budibase/bbui"
|
||||
import DrawerBindableInput from "components/common/bindings/DrawerBindableInput.svelte"
|
||||
import { generate } from "shortid"
|
||||
|
@ -59,6 +60,14 @@
|
|||
expression.operator = validOperators[0] ?? OperatorOptions.Equals.value
|
||||
onOperatorChange(expression, expression.operator)
|
||||
}
|
||||
|
||||
// if changed to an array, change default value to empty array
|
||||
const idx = filters.findIndex(x => (x.field = field))
|
||||
if (expression.type === "array") {
|
||||
filters[idx].value = []
|
||||
} else {
|
||||
filters[idx].value = null
|
||||
}
|
||||
}
|
||||
|
||||
const onOperatorChange = (expression, operator) => {
|
||||
|
@ -74,7 +83,12 @@
|
|||
|
||||
const getFieldOptions = field => {
|
||||
const schema = schemaFields.find(x => x.name === field)
|
||||
return schema?.constraints?.inclusion || []
|
||||
const opt =
|
||||
schema.type == "array"
|
||||
? schema?.constraints?.inclusion[0]
|
||||
: schema?.constraints?.inclusion || []
|
||||
|
||||
return opt
|
||||
}
|
||||
</script>
|
||||
|
||||
|
@ -128,6 +142,14 @@
|
|||
options={getFieldOptions(filter.field)}
|
||||
bind:value={filter.value}
|
||||
/>
|
||||
{:else if filter.type === "array"}
|
||||
<Multiselect
|
||||
disabled={filter.noValue}
|
||||
options={getFieldOptions(filter.field)}
|
||||
bind:value={filter.value}
|
||||
getOptionLabel={x => x}
|
||||
getOptionValue={x => x}
|
||||
/>
|
||||
{:else if filter.type === "boolean"}
|
||||
<Combobox
|
||||
disabled={filter.noValue}
|
||||
|
|
|
@ -31,6 +31,11 @@ export const OperatorOptions = {
|
|||
value: "rangeHigh",
|
||||
label: "Less than",
|
||||
},
|
||||
Contains: {
|
||||
value: "contains",
|
||||
label: "Contains",
|
||||
},
|
||||
|
||||
}
|
||||
|
||||
export const getValidOperatorsForType = type => {
|
||||
|
@ -55,6 +60,8 @@ export const getValidOperatorsForType = type => {
|
|||
]
|
||||
} else if (type === "options") {
|
||||
return [Op.Equals, Op.NotEquals, Op.Empty, Op.NotEmpty]
|
||||
} else if (type === "array") {
|
||||
return [Op.Equals, Op.NotEquals, Op.Empty, Op.NotEmpty, Op.Contains]
|
||||
} else if (type === "boolean") {
|
||||
return [Op.Equals, Op.NotEquals, Op.Empty, Op.NotEmpty]
|
||||
} else if (type === "longform") {
|
||||
|
|
|
@ -17,6 +17,7 @@ class QueryBuilder {
|
|||
notEqual: {},
|
||||
empty: {},
|
||||
notEmpty: {},
|
||||
contains: {},
|
||||
...base,
|
||||
}
|
||||
this.limit = 50
|
||||
|
@ -104,6 +105,12 @@ 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.
|
||||
|
@ -121,7 +128,7 @@ class QueryBuilder {
|
|||
}
|
||||
// Escape characters
|
||||
if (escape && originalType === "string") {
|
||||
value = `${value}`.replace(/[ #+\-&|!(){}\]^"~*?:\\]/g, "\\$&")
|
||||
value = `${value}`.replace(/[ #+\-&|!{}\]^"~*?:\\]/g, "\\$&")
|
||||
}
|
||||
// Wrap in quotes
|
||||
if (hasVersion && wrap) {
|
||||
|
@ -212,6 +219,19 @@ class QueryBuilder {
|
|||
build(this.query.notEmpty, key => `${key}:["" TO *]`)
|
||||
}
|
||||
|
||||
if (this.query.contains) {
|
||||
build(this.query.contains, (key, value) => {
|
||||
if (!value) {
|
||||
return null
|
||||
}
|
||||
let opts = []
|
||||
value.forEach(val => opts.push(`${key}.${val}:${builder.preprocess(val, allPreProcessingOpts)}`))
|
||||
const joined = opts.join(' AND ')
|
||||
return joined
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
return query
|
||||
}
|
||||
|
||||
|
@ -253,6 +273,7 @@ const runQuery = async (url, body) => {
|
|||
method: "POST",
|
||||
})
|
||||
const json = await response.json()
|
||||
console.log(json)
|
||||
let output = {
|
||||
rows: [],
|
||||
}
|
||||
|
|
|
@ -94,12 +94,18 @@ exports.createAllSearchIndex = async appId => {
|
|||
await searchIndex(
|
||||
appId,
|
||||
SearchIndexes.ROWS,
|
||||
function (doc) {
|
||||
function (doc) {
|
||||
function idx(input, prev) {
|
||||
for (let key of Object.keys(input)) {
|
||||
let idxKey = prev != null ? `${prev}.${key}` : key
|
||||
idxKey = idxKey.replace(/ /, "_")
|
||||
if (key === "_id" || key === "_rev" || input[key] == null) {
|
||||
|
||||
|
||||
if (Array.isArray(input[key])) {
|
||||
for (val in input[key]) {
|
||||
index(`${idxKey}.${input[key][v]}`, input[key][v], { store: true });
|
||||
}
|
||||
} else if (key === "_id" || key === "_rev" || input[key] == null) {
|
||||
continue
|
||||
}
|
||||
if (typeof input[key] === "string") {
|
||||
|
|
|
@ -2036,11 +2036,7 @@
|
|||
"type": "boolean",
|
||||
"label": "Autocomplete",
|
||||
"key": "autocomplete",
|
||||
"defaultValue": false,
|
||||
"dependsOn": {
|
||||
"setting": "optionsType",
|
||||
"value": "select"
|
||||
}
|
||||
"defaultValue": false
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<script>
|
||||
import { Multiselect } from "@budibase/bbui"
|
||||
import Field from "./Field.svelte"
|
||||
|
||||
import { getOptions } from "./optionsParser"
|
||||
export let field
|
||||
export let label
|
||||
export let placeholder
|
||||
|
@ -13,49 +13,20 @@
|
|||
export let labelColumn
|
||||
export let valueColumn
|
||||
export let customOptions
|
||||
console.log(defaultValue)
|
||||
|
||||
let fieldState
|
||||
let fieldApi
|
||||
let fieldSchema
|
||||
|
||||
$: flatOptions = optionsSource == null || optionsSource === "schema"
|
||||
$: options = getOptions(
|
||||
optionsSource,
|
||||
fieldSchema,
|
||||
dataProvider,
|
||||
labelColumn,
|
||||
valueColumn
|
||||
valueColumn,
|
||||
customOptions
|
||||
)
|
||||
const getOptions = (
|
||||
optionsSource,
|
||||
fieldSchema,
|
||||
dataProvider,
|
||||
labelColumn,
|
||||
valueColumn
|
||||
) => {
|
||||
// Take options from schema
|
||||
if (optionsSource == null || optionsSource === "schema") {
|
||||
return fieldSchema?.constraints?.inclusion ?? []
|
||||
}
|
||||
|
||||
// Extract options from data provider
|
||||
if (optionsSource === "provider" && valueColumn) {
|
||||
let optionsSet = {}
|
||||
dataProvider?.rows?.forEach(row => {
|
||||
const value = row?.[valueColumn]
|
||||
if (value) {
|
||||
const label = row[labelColumn] || value
|
||||
optionsSet[value] = { value, label }
|
||||
}
|
||||
})
|
||||
return Object.values(optionsSet)
|
||||
}
|
||||
|
||||
// Extract custom options
|
||||
if (optionsSource === "custom" && customOptions) {
|
||||
return customOptions
|
||||
}
|
||||
|
||||
return []
|
||||
}
|
||||
</script>
|
||||
|
||||
<Field
|
||||
|
@ -69,5 +40,10 @@
|
|||
bind:fieldApi
|
||||
bind:fieldSchema
|
||||
>
|
||||
<Multiselect {placeholder} options={options[0]} />
|
||||
<Multiselect
|
||||
getOptionLabel={flatOptions ? x => x : x => x.label}
|
||||
getOptionValue={flatOptions ? x => x : x => x.value}
|
||||
{placeholder}
|
||||
{options}
|
||||
/>
|
||||
</Field>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<script>
|
||||
import { CoreSelect, CoreRadioGroup } from "@budibase/bbui"
|
||||
import Field from "./Field.svelte"
|
||||
|
||||
import { getOptions } from "./optionsParser"
|
||||
export let field
|
||||
export let label
|
||||
export let placeholder
|
||||
|
@ -26,41 +26,9 @@
|
|||
fieldSchema,
|
||||
dataProvider,
|
||||
labelColumn,
|
||||
valueColumn
|
||||
valueColumn,
|
||||
customOptions
|
||||
)
|
||||
|
||||
const getOptions = (
|
||||
optionsSource,
|
||||
fieldSchema,
|
||||
dataProvider,
|
||||
labelColumn,
|
||||
valueColumn
|
||||
) => {
|
||||
// Take options from schema
|
||||
if (optionsSource == null || optionsSource === "schema") {
|
||||
return fieldSchema?.constraints?.inclusion ?? []
|
||||
}
|
||||
|
||||
// Extract options from data provider
|
||||
if (optionsSource === "provider" && valueColumn) {
|
||||
let optionsSet = {}
|
||||
dataProvider?.rows?.forEach(row => {
|
||||
const value = row?.[valueColumn]
|
||||
if (value) {
|
||||
const label = row[labelColumn] || value
|
||||
optionsSet[value] = { value, label }
|
||||
}
|
||||
})
|
||||
return Object.values(optionsSet)
|
||||
}
|
||||
|
||||
// Extract custom options
|
||||
if (optionsSource === "custom" && customOptions) {
|
||||
return customOptions
|
||||
}
|
||||
|
||||
return []
|
||||
}
|
||||
</script>
|
||||
|
||||
<Field
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
export const getOptions = (
|
||||
optionsSource,
|
||||
fieldSchema,
|
||||
dataProvider,
|
||||
labelColumn,
|
||||
valueColumn,
|
||||
customOptions
|
||||
) => {
|
||||
const isArray = fieldSchema?.type === "array"
|
||||
// Take options from schema
|
||||
if (optionsSource == null || optionsSource === "schema") {
|
||||
if (isArray) {
|
||||
return fieldSchema?.constraints?.inclusion[0] ?? []
|
||||
}
|
||||
return fieldSchema?.constraints?.inclusion ?? []
|
||||
}
|
||||
|
||||
if (optionsSource === "provider" && isArray) {
|
||||
let optionsSet = {}
|
||||
|
||||
dataProvider?.rows?.forEach(row => {
|
||||
const value = row?.[valueColumn]
|
||||
if (value) {
|
||||
const label = row[labelColumn] || value
|
||||
optionsSet[value] = { value, label }
|
||||
}
|
||||
})
|
||||
return Object.values(optionsSet)
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Extract options from data provider
|
||||
if (optionsSource === "provider" && valueColumn) {
|
||||
let optionsSet = {}
|
||||
dataProvider?.rows?.forEach(row => {
|
||||
const value = row?.[valueColumn]
|
||||
if (value) {
|
||||
const label = row[labelColumn] || value
|
||||
optionsSet[value] = { value, label }
|
||||
}
|
||||
})
|
||||
return Object.values(optionsSet)
|
||||
}
|
||||
|
||||
// Extract custom options
|
||||
if (optionsSource === "custom" && customOptions) {
|
||||
return customOptions
|
||||
}
|
||||
|
||||
return []
|
||||
}
|
|
@ -11,6 +11,7 @@ export const buildLuceneQuery = filter => {
|
|||
notEqual: {},
|
||||
empty: {},
|
||||
notEmpty: {},
|
||||
contains: {}
|
||||
}
|
||||
if (Array.isArray(filter)) {
|
||||
filter.forEach(expression => {
|
||||
|
|
Loading…
Reference in New Issue