diff --git a/packages/api/src/utility/JsonLinesDatastore.js b/packages/api/src/utility/JsonLinesDatastore.js index b5809bf9d..0f22a12f7 100644 --- a/packages/api/src/utility/JsonLinesDatastore.js +++ b/packages/api/src/utility/JsonLinesDatastore.js @@ -163,6 +163,7 @@ class JsonLinesDatastore { const res = []; await lock.acquire('reader', async () => { await this._ensureReader(offset, filter); + // console.log(JSON.stringify(this.currentFilter, undefined, 2)); for (let i = 0; i < limit; i += 1) { const line = await this._readLine(true); if (line == null) break; diff --git a/packages/datalib/src/GridDisplay.ts b/packages/datalib/src/GridDisplay.ts index ecec9051b..a622cb9cf 100644 --- a/packages/datalib/src/GridDisplay.ts +++ b/packages/datalib/src/GridDisplay.ts @@ -652,7 +652,8 @@ export abstract class GridDisplay { for (const name in filters) { const column = this.isDynamicStructure ? null : this.columns.find(x => x.columnName == name); if (!this.isDynamicStructure && !column) continue; - const filterType = this.isDynamicStructure ? this.filterTypeOverride ?? 'mongo' : getFilterType(column.dataType); + const filterType = + this.filterTypeOverride ?? (this.isDynamicStructure ? 'mongo' : getFilterType(column.dataType)); try { const condition = parseFilter(filters[name], filterType); const replaced = _.cloneDeepWith(condition, (expr: Expression) => { diff --git a/packages/datalib/src/JslGridDisplay.ts b/packages/datalib/src/JslGridDisplay.ts index 081d98b7f..323f01751 100644 --- a/packages/datalib/src/JslGridDisplay.ts +++ b/packages/datalib/src/JslGridDisplay.ts @@ -21,7 +21,7 @@ export class JslGridDisplay extends GridDisplay { this.filterable = true; this.supportsReload = supportsReload; this.isDynamicStructure = isDynamicStructure; - if (isDynamicStructure) this.filterTypeOverride = 'string'; + this.filterTypeOverride = 'eval'; if (structure?.columns) { this.columns = _.uniqBy( diff --git a/packages/filterparser/src/parseFilter.ts b/packages/filterparser/src/parseFilter.ts index 8ebadeb49..7afa36ebd 100644 --- a/packages/filterparser/src/parseFilter.ts +++ b/packages/filterparser/src/parseFilter.ts @@ -254,10 +254,10 @@ const createParser = (filterType: FilterType) => { eq: r => word('=').then(r.value).map(binaryCondition('=')), ne: r => word('!=').then(r.value).map(binaryCondition('<>')), ne2: r => word('<>').then(r.value).map(binaryCondition('<>')), - lt: r => word('<').then(r.value).map(binaryCondition('<')), - gt: r => word('>').then(r.value).map(binaryCondition('>')), le: r => word('<=').then(r.value).map(binaryCondition('<=')), ge: r => word('>=').then(r.value).map(binaryCondition('>=')), + lt: r => word('<').then(r.value).map(binaryCondition('<')), + gt: r => word('>').then(r.value).map(binaryCondition('>')), startsWith: r => word('^').then(r.value).map(likeCondition('like', '#VALUE#%')), endsWith: r => word('$').then(r.value).map(likeCondition('like', '%#VALUE#')), contains: r => word('+').then(r.value).map(likeCondition('like', '%#VALUE#%')), @@ -271,24 +271,30 @@ const createParser = (filterType: FilterType) => { }; const allowedValues = []; // 'string1', 'string2', 'number', 'noQuotedString']; - if (filterType == 'string') allowedValues.push('string1', 'string2', 'noQuotedString'); - if (filterType == 'number') allowedValues.push('string1Num', 'string2Num', 'number'); + if (filterType == 'string' || filterType == 'eval') { + allowedValues.push('string1', 'string2', 'noQuotedString'); + } + if (filterType == 'number') { + allowedValues.push('string1Num', 'string2Num', 'number'); + } const allowedElements = ['null', 'notNull', 'eq', 'ne', 'ne2']; - if (filterType == 'number' || filterType == 'datetime') allowedElements.push('lt', 'gt', 'le', 'ge'); - if (filterType == 'string') - allowedElements.push( - 'empty', - 'notEmpty', - 'startsWith', - 'endsWith', - 'contains', - 'startsWithNot', - 'endsWithNot', - 'containsNot' - ); - if (filterType == 'logical') allowedElements.push('true', 'false', 'trueNum', 'falseNum'); - if (filterType == 'datetime') + if (filterType == 'number' || filterType == 'datetime' || filterType == 'eval') { + allowedElements.push('le', 'ge', 'lt', 'gt'); + } + if (filterType == 'string') { + allowedElements.push('empty', 'notEmpty'); + } + if (filterType == 'eval' || filterType == 'string') { + allowedElements.push('startsWith', 'endsWith', 'contains', 'startsWithNot', 'endsWithNot', 'containsNot'); + } + if (filterType == 'logical') { + allowedElements.push('true', 'false', 'trueNum', 'falseNum'); + } + if (filterType == 'eval') { + allowedElements.push('true', 'false'); + } + if (filterType == 'datetime') { allowedElements.push( 'yearMonthDaySecond', 'yearMonthDayMinute', @@ -308,10 +314,13 @@ const createParser = (filterType: FilterType) => { 'thisYear', 'nextYear' ); - + } // must be last - if (filterType == 'string') allowedElements.push('valueTestStr'); - else allowedElements.push('valueTestEq'); + if (filterType == 'string' || filterType == 'eval') { + allowedElements.push('valueTestStr'); + } else { + allowedElements.push('valueTestEq'); + } return P.createLanguage(langDef); }; @@ -321,6 +330,7 @@ const parsers = { string: createParser('string'), datetime: createParser('datetime'), logical: createParser('logical'), + eval: createParser('eval'), mongo: mongoParser, }; diff --git a/packages/filterparser/src/types.ts b/packages/filterparser/src/types.ts index 447b49a9d..367e07d90 100644 --- a/packages/filterparser/src/types.ts +++ b/packages/filterparser/src/types.ts @@ -1,3 +1,3 @@ // import types from 'dbgate-types'; -export type FilterType = 'number' | 'string' | 'datetime' | 'logical' | 'mongo'; +export type FilterType = 'number' | 'string' | 'datetime' | 'logical' | 'eval' | 'mongo'; diff --git a/packages/web/src/datagrid/DataFilterControl.svelte b/packages/web/src/datagrid/DataFilterControl.svelte index 5468dfc64..881227cff 100644 --- a/packages/web/src/datagrid/DataFilterControl.svelte +++ b/packages/web/src/datagrid/DataFilterControl.svelte @@ -161,6 +161,33 @@ { onClick: () => setFilter('TRUE'), text: 'Is True' }, { onClick: () => setFilter('FALSE'), text: 'Is False' }, ]; + case 'eval': + return [ + { onClick: () => setFilter(''), text: 'Clear Filter' }, + { onClick: () => filterMultipleValues(), text: 'Filter multiple values' }, + + { onClick: () => openFilterWindow('='), text: 'Equals...' }, + { onClick: () => openFilterWindow('<>'), text: 'Does Not Equal...' }, + { onClick: () => setFilter('NULL'), text: 'Is Null' }, + { onClick: () => setFilter('NOT NULL'), text: 'Is Not Null' }, + + { divider: true }, + + { onClick: () => openFilterWindow('>'), text: 'Greater Than...' }, + { onClick: () => openFilterWindow('>='), text: 'Greater Than Or Equal To...' }, + { onClick: () => openFilterWindow('<'), text: 'Less Than...' }, + { onClick: () => openFilterWindow('<='), text: 'Less Than Or Equal To...' }, + + { divider: true }, + + + { onClick: () => openFilterWindow('+'), text: 'Contains...' }, + { onClick: () => openFilterWindow('~'), text: 'Does Not Contain...' }, + { onClick: () => openFilterWindow('^'), text: 'Begins With...' }, + { onClick: () => openFilterWindow('!^'), text: 'Does Not Begin With...' }, + { onClick: () => openFilterWindow('$'), text: 'Ends With...' }, + { onClick: () => openFilterWindow('!$'), text: 'Does Not End With...' }, + ]; } // return [ diff --git a/packages/web/src/datagrid/DataGrid.svelte b/packages/web/src/datagrid/DataGrid.svelte index 02db99cc9..7fb21253a 100644 --- a/packages/web/src/datagrid/DataGrid.svelte +++ b/packages/web/src/datagrid/DataGrid.svelte @@ -80,6 +80,7 @@ export let display; export let changeSetState; export let dispatchChangeSet; + export let useEvalFilters = false; export let isDetailView = false; export let showReferences = false; @@ -181,7 +182,7 @@ height={'30%'} skip={!isDynamicStructure || !display?.filterable} > - + - + display.setFilter(col.uniqueName, value)} showResizeSplitter diff --git a/packages/web/src/datagrid/JslDataGrid.svelte b/packages/web/src/datagrid/JslDataGrid.svelte index 9cb7db2a9..04d443cbb 100644 --- a/packages/web/src/datagrid/JslDataGrid.svelte +++ b/packages/web/src/datagrid/JslDataGrid.svelte @@ -58,5 +58,6 @@ gridCoreComponent={JslDataGridCore} bind:loadedRows isDynamicStructure={$info?.__isDynamicStructure} + useEvalFilters /> {/key} diff --git a/packages/web/src/jsonview/JsonViewFilterColumn.svelte b/packages/web/src/jsonview/JsonViewFilterColumn.svelte index de07325c4..922cb493b 100644 --- a/packages/web/src/jsonview/JsonViewFilterColumn.svelte +++ b/packages/web/src/jsonview/JsonViewFilterColumn.svelte @@ -10,8 +10,10 @@ export let display; export let filters; export let isDynamicStructure; + export let useEvalFilters; - function computeFilterType(isDynamicStructure, display, uniqueName) { + function computeFilterType(isDynamicStructure, display, uniqueName, useEvalFilters) { + if (useEvalFilters) return 'eval'; if (isDynamicStructure) return 'mongo'; const col = display.findColumn(uniqueName); if (col) { @@ -35,7 +37,7 @@ display.setFilter(uniqueName, value)} /> diff --git a/packages/web/src/jsonview/JsonViewFilters.svelte b/packages/web/src/jsonview/JsonViewFilters.svelte index 819b00dc4..807e81118 100644 --- a/packages/web/src/jsonview/JsonViewFilters.svelte +++ b/packages/web/src/jsonview/JsonViewFilters.svelte @@ -7,6 +7,7 @@ export let managerSize; export let display; export let isDynamicStructure; + export let useEvalFilters; $: filters = display?.config?.filters; @@ -15,6 +16,6 @@ {#each allFilterNames as uniqueName} - + {/each} diff --git a/packages/web/src/modals/SetFilterModal_Select.svelte b/packages/web/src/modals/SetFilterModal_Select.svelte index 219f4b8af..33457b7e1 100644 --- a/packages/web/src/modals/SetFilterModal_Select.svelte +++ b/packages/web/src/modals/SetFilterModal_Select.svelte @@ -54,6 +54,21 @@ { value: '$', label: 'ends with' }, { value: '!$', label: 'does not end with' }, ]; + case 'eval': + return [ + { value: '=', label: 'eqals' }, + { value: '<>', label: 'does not equal' }, + { value: '<', label: 'is smaller' }, + { value: '>', label: 'is greater' }, + { value: '<=', label: 'is smaller or equal' }, + { value: '>=', label: 'is greater or equal' }, + { value: '+', label: 'contains' }, + { value: '~', label: 'does not contain' }, + { value: '^', label: 'begins with' }, + { value: '!^', label: 'does not begin with' }, + { value: '$', label: 'ends with' }, + { value: '!$', label: 'does not end with' }, + ]; } }