mirror of https://github.com/Budibase/budibase.git
9 changed files with 197 additions and 185 deletions
@ -1,179 +0,0 @@ |
|||||
/** |
|
||||
* Builds a lucene JSON query from the filter structure generated in the builder |
|
||||
* @param filter the builder filter structure |
|
||||
*/ |
|
||||
export const buildLuceneQuery = filter => { |
|
||||
let query = { |
|
||||
string: {}, |
|
||||
fuzzy: {}, |
|
||||
range: {}, |
|
||||
equal: {}, |
|
||||
notEqual: {}, |
|
||||
empty: {}, |
|
||||
notEmpty: {}, |
|
||||
contains: {}, |
|
||||
notContains: {}, |
|
||||
} |
|
||||
if (Array.isArray(filter)) { |
|
||||
filter.forEach(expression => { |
|
||||
let { operator, field, type, value } = expression |
|
||||
// Parse all values into correct types
|
|
||||
if (type === "datetime" && value) { |
|
||||
value = new Date(value).toISOString() |
|
||||
} |
|
||||
if (type === "number") { |
|
||||
value = parseFloat(value) |
|
||||
} |
|
||||
if (type === "boolean") { |
|
||||
value = `${value}`?.toLowerCase() === "true" |
|
||||
} |
|
||||
if (operator.startsWith("range")) { |
|
||||
if (!query.range[field]) { |
|
||||
query.range[field] = { |
|
||||
low: |
|
||||
type === "number" |
|
||||
? Number.MIN_SAFE_INTEGER |
|
||||
: "0000-00-00T00:00:00.000Z", |
|
||||
high: |
|
||||
type === "number" |
|
||||
? Number.MAX_SAFE_INTEGER |
|
||||
: "9999-00-00T00:00:00.000Z", |
|
||||
} |
|
||||
} |
|
||||
if (operator === "rangeLow" && value != null && value !== "") { |
|
||||
query.range[field].low = value |
|
||||
} else if (operator === "rangeHigh" && value != null && value !== "") { |
|
||||
query.range[field].high = value |
|
||||
} |
|
||||
} else if (query[operator]) { |
|
||||
if (type === "boolean") { |
|
||||
// Transform boolean filters to cope with null.
|
|
||||
// "equals false" needs to be "not equals true"
|
|
||||
// "not equals false" needs to be "equals true"
|
|
||||
if (operator === "equal" && value === false) { |
|
||||
query.notEqual[field] = true |
|
||||
} else if (operator === "notEqual" && value === false) { |
|
||||
query.equal[field] = true |
|
||||
} else { |
|
||||
query[operator][field] = value |
|
||||
} |
|
||||
} else { |
|
||||
query[operator][field] = value |
|
||||
} |
|
||||
} |
|
||||
}) |
|
||||
} |
|
||||
|
|
||||
return query |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* Performs a client-side lucene search on an array of data |
|
||||
* @param docs the data |
|
||||
* @param query the JSON lucene query |
|
||||
*/ |
|
||||
export const luceneQuery = (docs, query) => { |
|
||||
if (!query) { |
|
||||
return docs |
|
||||
} |
|
||||
|
|
||||
// Iterates over a set of filters and evaluates a fail function against a doc
|
|
||||
const match = (type, failFn) => doc => { |
|
||||
const filters = Object.entries(query[type] || {}) |
|
||||
for (let i = 0; i < filters.length; i++) { |
|
||||
if (failFn(filters[i][0], filters[i][1], doc)) { |
|
||||
return false |
|
||||
} |
|
||||
} |
|
||||
return true |
|
||||
} |
|
||||
|
|
||||
// Process a string match (fails if the value does not start with the string)
|
|
||||
const stringMatch = match("string", (key, value, doc) => { |
|
||||
return !doc[key] || !doc[key].startsWith(value) |
|
||||
}) |
|
||||
|
|
||||
// Process a fuzzy match (treat the same as starts with when running locally)
|
|
||||
const fuzzyMatch = match("fuzzy", (key, value, doc) => { |
|
||||
return !doc[key] || !doc[key].startsWith(value) |
|
||||
}) |
|
||||
|
|
||||
// Process a range match
|
|
||||
const rangeMatch = match("range", (key, value, doc) => { |
|
||||
return !doc[key] || doc[key] < value.low || doc[key] > value.high |
|
||||
}) |
|
||||
|
|
||||
// Process an equal match (fails if the value is different)
|
|
||||
const equalMatch = match("equal", (key, value, doc) => { |
|
||||
return value != null && value !== "" && doc[key] !== value |
|
||||
}) |
|
||||
|
|
||||
// Process a not-equal match (fails if the value is the same)
|
|
||||
const notEqualMatch = match("notEqual", (key, value, doc) => { |
|
||||
return value != null && value !== "" && doc[key] === value |
|
||||
}) |
|
||||
|
|
||||
// Process an empty match (fails if the value is not empty)
|
|
||||
const emptyMatch = match("empty", (key, value, doc) => { |
|
||||
return doc[key] != null && doc[key] !== "" |
|
||||
}) |
|
||||
|
|
||||
// Process a not-empty match (fails is the value is empty)
|
|
||||
const notEmptyMatch = match("notEmpty", (key, value, doc) => { |
|
||||
return doc[key] == null || doc[key] === "" |
|
||||
}) |
|
||||
|
|
||||
// Match a document against all criteria
|
|
||||
const docMatch = doc => { |
|
||||
return ( |
|
||||
stringMatch(doc) && |
|
||||
fuzzyMatch(doc) && |
|
||||
rangeMatch(doc) && |
|
||||
equalMatch(doc) && |
|
||||
notEqualMatch(doc) && |
|
||||
emptyMatch(doc) && |
|
||||
notEmptyMatch(doc) |
|
||||
) |
|
||||
} |
|
||||
|
|
||||
// Process all docs
|
|
||||
return docs.filter(docMatch) |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* Performs a client-side sort from the equivalent server-side lucene sort |
|
||||
* parameters. |
|
||||
* @param docs the data |
|
||||
* @param sort the sort column |
|
||||
* @param sortOrder the sort order ("ascending" or "descending") |
|
||||
* @param sortType the type of sort ("string" or "number") |
|
||||
*/ |
|
||||
export const luceneSort = (docs, sort, sortOrder, sortType = "string") => { |
|
||||
if (!sort || !sortOrder || !sortType) { |
|
||||
return docs |
|
||||
} |
|
||||
const parse = sortType === "string" ? x => `${x}` : x => parseFloat(x) |
|
||||
return docs.slice().sort((a, b) => { |
|
||||
const colA = parse(a[sort]) |
|
||||
const colB = parse(b[sort]) |
|
||||
if (sortOrder === "Descending") { |
|
||||
return colA > colB ? -1 : 1 |
|
||||
} else { |
|
||||
return colA > colB ? 1 : -1 |
|
||||
} |
|
||||
}) |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* Limits the specified docs to the specified number of rows from the equivalent |
|
||||
* server-side lucene limit parameters. |
|
||||
* @param docs the data |
|
||||
* @param limit the number of docs to limit to |
|
||||
*/ |
|
||||
export const luceneLimit = (docs, limit) => { |
|
||||
const numLimit = parseFloat(limit) |
|
||||
if (isNaN(numLimit)) { |
|
||||
return docs |
|
||||
} |
|
||||
return docs.slice(0, numLimit) |
|
||||
} |
|
||||
Loading…
Reference in new issue