From 129c9662266bc6b1b29cf61a9aa6a14fcf074b3f Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Tue, 2 Aug 2022 18:34:58 +0100 Subject: [PATCH 1/6] 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. --- .../controls/FilterEditor/FilterDrawer.svelte | 29 +++++++- .../controls/FilterEditor/FilterEditor.svelte | 66 +++++++++++++++++-- packages/frontend-core/src/utils/lucene.js | 4 ++ .../src/api/controllers/row/internalSearch.js | 3 + .../server/src/api/controllers/row/utils.js | 7 +- packages/server/src/integrations/base/sql.ts | 8 ++- .../server/src/integrations/base/utils.ts | 40 ++++++----- .../server/src/integrations/queries/sql.ts | 1 - 8 files changed, 130 insertions(+), 28 deletions(-) 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 aa191ce0ea..1d9c3dea09 100644 --- a/packages/builder/src/components/design/settings/controls/FilterEditor/FilterDrawer.svelte +++ b/packages/builder/src/components/design/settings/controls/FilterEditor/FilterDrawer.svelte @@ -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} {/if} -
+
+
+ (allOr = event.detail)} + /> +
@@ -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; + } diff --git a/packages/builder/src/components/design/settings/controls/FilterEditor/FilterEditor.svelte b/packages/builder/src/components/design/settings/controls/FilterEditor/FilterEditor.svelte index 2cb35a9cf5..ea54afc0ee 100644 --- a/packages/builder/src/components/design/settings/controls/FilterEditor/FilterEditor.svelte +++ b/packages/builder/src/components/design/settings/controls/FilterEditor/FilterEditor.svelte @@ -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 @@ { + toSaveFilters = event.detail + }} /> diff --git a/packages/frontend-core/src/utils/lucene.js b/packages/frontend-core/src/utils/lucene.js index b6699628d1..780cb2da39 100644 --- a/packages/frontend-core/src/utils/lucene.js +++ b/packages/frontend-core/src/utils/lucene.js @@ -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) { diff --git a/packages/server/src/api/controllers/row/internalSearch.js b/packages/server/src/api/controllers/row/internalSearch.js index 8a04fc2bd0..ab084d9e0b 100644 --- a/packages/server/src/api/controllers/row/internalSearch.js +++ b/packages/server/src/api/controllers/row/internalSearch.js @@ -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, }) diff --git a/packages/server/src/api/controllers/row/utils.js b/packages/server/src/api/controllers/row/utils.js index 5da7ca331e..da14020757 100644 --- a/packages/server/src/api/controllers/row/utils.js +++ b/packages/server/src/api/controllers/row/utils.js @@ -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() diff --git a/packages/server/src/integrations/base/sql.ts b/packages/server/src/integrations/base/sql.ts index 750564c6ff..a46ce7aea2 100644 --- a/packages/server/src/integrations/base/sql.ts +++ b/packages/server/src/integrations/base/sql.ts @@ -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) } } } diff --git a/packages/server/src/integrations/base/utils.ts b/packages/server/src/integrations/base/utils.ts index 086912b920..0913d59b2a 100644 --- a/packages/server/src/integrations/base/utils.ts +++ b/packages/server/src/integrations/base/utils.ts @@ -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 } diff --git a/packages/server/src/integrations/queries/sql.ts b/packages/server/src/integrations/queries/sql.ts index 271a414d44..7fbcfea0a3 100644 --- a/packages/server/src/integrations/queries/sql.ts +++ b/packages/server/src/integrations/queries/sql.ts @@ -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") From dee15b6415e319e7e50e69d2ae72a55d9579b659 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Wed, 3 Aug 2022 17:44:44 +0100 Subject: [PATCH 2/6] Switching to an explicit select for the OR/AND options in the filter. --- .../controls/FilterEditor/FilterDrawer.svelte | 172 ++++++++++-------- 1 file changed, 94 insertions(+), 78 deletions(-) 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 1d9c3dea09..fcec0e35ac 100644 --- a/packages/builder/src/components/design/settings/controls/FilterEditor/FilterDrawer.svelte +++ b/packages/builder/src/components/design/settings/controls/FilterEditor/FilterDrawer.svelte @@ -9,7 +9,6 @@ Input, Layout, Select, - Toggle, Label, } from "@budibase/bbui" import DrawerBindableInput from "components/common/bindings/DrawerBindableInput.svelte" @@ -17,7 +16,7 @@ import { generate } from "shortid" import { LuceneUtils, Constants } from "@budibase/frontend-core" import { getFields } from "helpers/searchFields" - import { createEventDispatcher } from "svelte" + import { createEventDispatcher, onMount } from "svelte" const dispatch = createEventDispatcher() @@ -33,6 +32,11 @@ $: fieldOptions = enrichedSchemaFields.map(field => field.name) || [] $: valueTypeOptions = allowBindings ? ["Value", "Binding"] : ["Value"] + let behaviourValue + const behaviourOptions = [ + { value: "and", label: "Match all of the following filters" }, + { value: "or", label: "Match any of the following filters" }, + ] const addFilter = () => { filters = [ ...filters, @@ -99,6 +103,10 @@ const schema = enrichedSchemaFields.find(x => x.name === field) return schema?.constraints?.inclusion || [] } + + onMount(() => { + behaviourValue = allOr ? "or" : "and" + }) @@ -114,88 +122,98 @@ {#if filters?.length}
- {#each filters as filter, idx} - onOperatorChange(filter, e.detail)} - placeholder={null} - /> - opt.label} + getOptionValue={opt => opt.value} + on:change={e => (allOr = e.detail === "or")} + placeholder={null} + /> +
+
+
+ +
+
+ {#each filters as filter, idx} + - {:else if ["options", "array"].includes(filter.type)} - onOperatorChange(filter, e.detail)} + placeholder={null} /> - {:else if filter.type === "boolean"} - - {:else if filter.type === "datetime"} - (filter.value = event.detail)} + /> + {:else if ["string", "longform", "number", "formula"].includes(filter.type)} + + {:else if ["options", "array"].includes(filter.type)} + + {:else if filter.type === "boolean"} + + {:else if filter.type === "datetime"} + + {:else} + + {/if} + duplicateFilter(filter.id)} /> - {:else} - - {/if} - duplicateFilter(filter.id)} - /> - removeFilter(filter.id)} - /> - {/each} + removeFilter(filter.id)} + /> + {/each} +
{/if}
-
- (allOr = event.detail)} - /> -
@@ -216,10 +234,8 @@ grid-template-columns: 1fr 120px 120px 1fr auto auto; } - .toggle { - display: flex; - align-items: center; - padding-right: var(--spacing-s); + .filter-label { + margin-bottom: var(--spacing-s); } .bottom { From d14051bd62feca7a0264d95127451e2386e935c8 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Wed, 3 Aug 2022 18:20:07 +0100 Subject: [PATCH 3/6] Fixing test cases. --- .../server/src/api/controllers/row/utils.js | 6 ++---- .../src/api/controllers/table/external.js | 2 +- packages/server/src/integrations/base/query.ts | 17 +++++++++++++++++ packages/server/src/integrations/base/sql.ts | 1 - packages/server/src/integrations/base/utils.ts | 18 ------------------ .../src/integrations/microsoftSqlServer.ts | 5 +++-- packages/server/src/integrations/mysql.ts | 2 +- packages/server/src/integrations/postgres.ts | 3 ++- .../server/src/integrations/tests/sql.spec.js | 2 +- 9 files changed, 27 insertions(+), 29 deletions(-) create mode 100644 packages/server/src/integrations/base/query.ts diff --git a/packages/server/src/api/controllers/row/utils.js b/packages/server/src/api/controllers/row/utils.js index da14020757..4c837e7630 100644 --- a/packages/server/src/api/controllers/row/utils.js +++ b/packages/server/src/api/controllers/row/utils.js @@ -4,10 +4,8 @@ const { InternalTables } = require("../../../db/utils") const userController = require("../user") const { FieldTypes } = require("../../../constants") const { getAppDB } = require("@budibase/backend-core/context") -const { - makeExternalQuery, - removeKeyNumbering, -} = require("../../../integrations/base/utils") +const { makeExternalQuery } = require("../../../integrations/base/query") +const { removeKeyNumbering } = require("../../../integrations/base/utils") validateJs.extend(validateJs.validators.datetime, { parse: function (value) { diff --git a/packages/server/src/api/controllers/table/external.js b/packages/server/src/api/controllers/table/external.js index 34319c5bff..d919e9dad7 100644 --- a/packages/server/src/api/controllers/table/external.js +++ b/packages/server/src/api/controllers/table/external.js @@ -14,7 +14,7 @@ const { FieldTypes, RelationshipTypes, } = require("../../../constants") -const { makeExternalQuery } = require("../../../integrations/base/utils") +const { makeExternalQuery } = require("../../../integrations/base/query") const { cloneDeep } = require("lodash/fp") const csvParser = require("../../../utilities/csvParser") const { handleRequest } = require("../row/external") diff --git a/packages/server/src/integrations/base/query.ts b/packages/server/src/integrations/base/query.ts new file mode 100644 index 0000000000..59d67b2ef1 --- /dev/null +++ b/packages/server/src/integrations/base/query.ts @@ -0,0 +1,17 @@ +import { QueryJson } from "../../definitions/datasource" +import { Datasource } from "../../definitions/common" +const { integrations } = require("../index") + +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." + } +} diff --git a/packages/server/src/integrations/base/sql.ts b/packages/server/src/integrations/base/sql.ts index a46ce7aea2..4d33669a03 100644 --- a/packages/server/src/integrations/base/sql.ts +++ b/packages/server/src/integrations/base/sql.ts @@ -584,4 +584,3 @@ class SqlQueryBuilder extends SqlTableQueryBuilder { } export default SqlQueryBuilder -module.exports = SqlQueryBuilder diff --git a/packages/server/src/integrations/base/utils.ts b/packages/server/src/integrations/base/utils.ts index 0913d59b2a..54efdb91a0 100644 --- a/packages/server/src/integrations/base/utils.ts +++ b/packages/server/src/integrations/base/utils.ts @@ -1,23 +1,5 @@ -import { QueryJson } from "../../definitions/datasource" -import { Datasource } from "../../definitions/common" -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 function removeKeyNumbering(key: any): string { if (typeof key === "string" && key.match(QUERY_START_REGEX) != null) { const parts = key.split(":") diff --git a/packages/server/src/integrations/microsoftSqlServer.ts b/packages/server/src/integrations/microsoftSqlServer.ts index 1e5664748d..2fe445f8fe 100644 --- a/packages/server/src/integrations/microsoftSqlServer.ts +++ b/packages/server/src/integrations/microsoftSqlServer.ts @@ -15,10 +15,10 @@ import { } from "./utils" import { DatasourcePlus } from "./base/datasourcePlus" import { Table, TableSchema } from "../definitions/common" +import Sql from "./base/sql" module MSSQLModule { const sqlServer = require("mssql") - const Sql = require("./base/sql") const DEFAULT_SCHEMA = "dbo" interface MSSQLConfig { @@ -96,7 +96,8 @@ module MSSQLModule { class SqlServerIntegration extends Sql implements DatasourcePlus { private readonly config: MSSQLConfig private index: number = 0 - static pool: any + private readonly pool: any + private client: any public tables: Record = {} public schemaErrors: Record = {} diff --git a/packages/server/src/integrations/mysql.ts b/packages/server/src/integrations/mysql.ts index 1fdab66701..61780ffcd7 100644 --- a/packages/server/src/integrations/mysql.ts +++ b/packages/server/src/integrations/mysql.ts @@ -16,10 +16,10 @@ import { import { DatasourcePlus } from "./base/datasourcePlus" import dayjs from "dayjs" const { NUMBER_REGEX } = require("../utilities") +import Sql from "./base/sql" module MySQLModule { const mysql = require("mysql2/promise") - const Sql = require("./base/sql") interface MySQLConfig { host: string diff --git a/packages/server/src/integrations/postgres.ts b/packages/server/src/integrations/postgres.ts index 7cc01bdc79..48b9544e50 100644 --- a/packages/server/src/integrations/postgres.ts +++ b/packages/server/src/integrations/postgres.ts @@ -14,10 +14,10 @@ import { SqlClients, } from "./utils" import { DatasourcePlus } from "./base/datasourcePlus" +import Sql from "./base/sql" module PostgresModule { const { Client, types } = require("pg") - const Sql = require("./base/sql") const { escapeDangerousCharacters } = require("../utilities") // Return "date" and "timestamp" types as plain strings. @@ -117,6 +117,7 @@ module PostgresModule { private readonly client: any private readonly config: PostgresConfig private index: number = 1 + private open: boolean public tables: Record = {} public schemaErrors: Record = {} diff --git a/packages/server/src/integrations/tests/sql.spec.js b/packages/server/src/integrations/tests/sql.spec.js index 55c762573a..42fe9e3b30 100644 --- a/packages/server/src/integrations/tests/sql.spec.js +++ b/packages/server/src/integrations/tests/sql.spec.js @@ -1,4 +1,4 @@ -const Sql = require("../base/sql") +const Sql = require("../base/sql").default const { SqlClients } = require("../utils") const TABLE_NAME = "test" From 31e83b3f4c57d7271a3fde44589bbdc177e04e8f Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Mon, 8 Aug 2022 19:16:25 +0100 Subject: [PATCH 4/6] Adding unit tests for internal DB lucene syntax building. --- packages/server/__mocks__/node-fetch.ts | 9 +- .../src/api/controllers/row/internalSearch.js | 3 + .../api/routes/tests/internalSearch.spec.js | 136 ++++++++++++++++++ 3 files changed, 147 insertions(+), 1 deletion(-) create mode 100644 packages/server/src/api/routes/tests/internalSearch.spec.js diff --git a/packages/server/__mocks__/node-fetch.ts b/packages/server/__mocks__/node-fetch.ts index 1a7015fa52..dfffa7eb58 100644 --- a/packages/server/__mocks__/node-fetch.ts +++ b/packages/server/__mocks__/node-fetch.ts @@ -57,12 +57,19 @@ module FetchMock { 404 ) } else if (url.includes("_search")) { + const body = opts.body + const parts = body.split("tableId:") + let tableId + if (parts && parts[1]) { + tableId = parts[1].split('"')[0] + } return json({ rows: [ { doc: { _id: "test", - tableId: opts.body.split("tableId:")[1].split('"')[0], + tableId: tableId, + query: opts.body, }, }, ], diff --git a/packages/server/src/api/controllers/row/internalSearch.js b/packages/server/src/api/controllers/row/internalSearch.js index ab084d9e0b..a403b76aed 100644 --- a/packages/server/src/api/controllers/row/internalSearch.js +++ b/packages/server/src/api/controllers/row/internalSearch.js @@ -313,6 +313,9 @@ class QueryBuilder { } } +// exported for unit testing +exports.QueryBuilder = QueryBuilder + /** * Executes a lucene search query. * @param url The query URL diff --git a/packages/server/src/api/routes/tests/internalSearch.spec.js b/packages/server/src/api/routes/tests/internalSearch.spec.js new file mode 100644 index 0000000000..238b915cf2 --- /dev/null +++ b/packages/server/src/api/routes/tests/internalSearch.spec.js @@ -0,0 +1,136 @@ +const search = require("../../controllers/row/internalSearch") +// this will be mocked out for _search endpoint +const fetch = require("node-fetch") +const PARAMS = { + tableId: "ta_12345679abcdef", + version: "1", + bookmark: null, + sort: null, + sortOrder: "ascending", + sortType: "string", +} + +function checkLucene(resp, expected, params = PARAMS) { + const query = resp.rows[0].query + const json = JSON.parse(query) + if (PARAMS.sort) { + expect(json.sort).toBe(`${PARAMS.sort}<${PARAMS.sortType}>`) + } + if (PARAMS.bookmark) { + expect(json.bookmark).toBe(PARAMS.bookmark) + } + expect(json.include_docs).toBe(true) + expect(json.q).toBe(`(${expected}) AND tableId:"${params.tableId}"`) + expect(json.limit).toBe(params.limit || 50) +} + +describe("internal search", () => { + it("default query", async () => { + const response = await search.paginatedSearch({ + }, PARAMS) + checkLucene(response, `*:*`) + }) + + it("test equal query", async () => { + const response = await search.paginatedSearch({ + equal: { + "column": "1", + } + }, PARAMS) + checkLucene(response, `*:* AND column:"1"`) + }) + + it("test notEqual query", async () => { + const response = await search.paginatedSearch({ + notEqual: { + "column": "1", + } + }, PARAMS) + checkLucene(response, `*:* AND !column:"1"`) + }) + + it("test OR query", async () => { + const response = await search.paginatedSearch({ + allOr: true, + equal: { + "column": "2", + }, + notEqual: { + "column": "1", + } + }, PARAMS) + checkLucene(response, `column:"2" OR !column:"1"`) + }) + + it("test AND query", async () => { + const response = await search.paginatedSearch({ + equal: { + "column": "2", + }, + notEqual: { + "column": "1", + } + }, PARAMS) + checkLucene(response, `*:* AND column:"2" AND !column:"1"`) + }) + + it("test pagination query", async () => { + const updatedParams = { + ...PARAMS, + limit: 100, + bookmark: "awd", + sort: "column", + } + const response = await search.paginatedSearch({ + string: { + "column": "2", + }, + }, updatedParams) + checkLucene(response, `*:* AND column:2*`, updatedParams) + }) + + it("test range query", async () => { + const response = await search.paginatedSearch({ + range: { + "column": { low: 1, high: 2 }, + }, + }, PARAMS) + checkLucene(response, `*:* AND column:[1 TO 2]`, PARAMS) + }) + + it("test empty query", async () => { + const response = await search.paginatedSearch({ + empty: { + "column": "", + }, + }, PARAMS) + checkLucene(response, `*:* AND !column:["" TO *]`, PARAMS) + }) + + it("test notEmpty query", async () => { + const response = await search.paginatedSearch({ + notEmpty: { + "column": "", + }, + }, PARAMS) + checkLucene(response, `*:* AND column:["" TO *]`, PARAMS) + }) + + it("test oneOf query", async () => { + const response = await search.paginatedSearch({ + oneOf: { + "column": ["a", "b"], + }, + }, PARAMS) + checkLucene(response, `*:* AND column:("a" OR "b")`, PARAMS) + }) + + it("test contains query", async () => { + const response = await search.paginatedSearch({ + contains: { + "column": "a", + }, + }, PARAMS) + checkLucene(response, `*:* AND column:a`, PARAMS) + }) +}) \ No newline at end of file From 76f0c4a289143336ab53ebdbb530d6bd28516a60 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Mon, 8 Aug 2022 19:19:33 +0100 Subject: [PATCH 5/6] Adding multiple of same property test case for lucene building. --- .../src/api/routes/tests/internalSearch.spec.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/packages/server/src/api/routes/tests/internalSearch.spec.js b/packages/server/src/api/routes/tests/internalSearch.spec.js index 238b915cf2..810226aae1 100644 --- a/packages/server/src/api/routes/tests/internalSearch.spec.js +++ b/packages/server/src/api/routes/tests/internalSearch.spec.js @@ -133,4 +133,16 @@ describe("internal search", () => { }, PARAMS) checkLucene(response, `*:* AND column:a`, PARAMS) }) + + it("test multiple of same column", async () => { + const response = await search.paginatedSearch({ + allOr: true, + equal: { + "1:column": "a", + "2:column": "b", + "3:column": "c", + }, + }, PARAMS) + checkLucene(response, `column:"a" OR column:"b" OR column:"c"`, PARAMS) + }) }) \ No newline at end of file From fda15d3bd81e7a7b94e667e969c609ee853d83b2 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Mon, 8 Aug 2022 19:21:06 +0100 Subject: [PATCH 6/6] Adding a test for the case where a user has added a number: at the start of the column - could have broken things. --- .../server/src/api/routes/tests/internalSearch.spec.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/packages/server/src/api/routes/tests/internalSearch.spec.js b/packages/server/src/api/routes/tests/internalSearch.spec.js index 810226aae1..50ee2b26ac 100644 --- a/packages/server/src/api/routes/tests/internalSearch.spec.js +++ b/packages/server/src/api/routes/tests/internalSearch.spec.js @@ -145,4 +145,13 @@ describe("internal search", () => { }, PARAMS) checkLucene(response, `column:"a" OR column:"b" OR column:"c"`, PARAMS) }) + + it("check a weird case for lucene building", async () => { + const response = await search.paginatedSearch({ + equal: { + "1:1:column": "a", + }, + }, PARAMS) + checkLucene(response, `*:* AND 1\\:column:"a"`, PARAMS) + }) }) \ No newline at end of file