mirror of
https://github.com/DeNNiiInc/dbgate.git
synced 2026-04-30 23:13:57 +00:00
perspective filters
This commit is contained in:
@@ -88,7 +88,7 @@ export class PerspectiveCache {
|
|||||||
|
|
||||||
getTableCache(props: PerspectiveDataLoadProps) {
|
getTableCache(props: PerspectiveDataLoadProps) {
|
||||||
const tableKey = stableStringify(
|
const tableKey = stableStringify(
|
||||||
_pick(props, ['schemaName', 'pureName', 'bindingColumns', 'databaseConfig', 'orderBy'])
|
_pick(props, ['schemaName', 'pureName', 'bindingColumns', 'databaseConfig', 'orderBy', 'condition'])
|
||||||
);
|
);
|
||||||
let res = this.tables[tableKey];
|
let res = this.tables[tableKey];
|
||||||
|
|
||||||
|
|||||||
@@ -1,15 +1,23 @@
|
|||||||
export interface PerspectiveConfig {
|
export interface PerspectiveConfigColumns {
|
||||||
expandedColumns: string[];
|
expandedColumns: string[];
|
||||||
checkedColumns: string[];
|
checkedColumns: string[];
|
||||||
uncheckedColumns: string[];
|
uncheckedColumns: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface PerspectiveConfig extends PerspectiveConfigColumns {
|
||||||
|
filters: { [uniqueName: string]: string };
|
||||||
|
}
|
||||||
|
|
||||||
export function createPerspectiveConfig(): PerspectiveConfig {
|
export function createPerspectiveConfig(): PerspectiveConfig {
|
||||||
return {
|
return {
|
||||||
expandedColumns: [],
|
expandedColumns: [],
|
||||||
checkedColumns: [],
|
checkedColumns: [],
|
||||||
uncheckedColumns: [],
|
uncheckedColumns: [],
|
||||||
|
filters: {},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ChangePerspectiveConfigFunc = (changeFunc: (config: PerspectiveConfig) => PerspectiveConfig) => void;
|
export type ChangePerspectiveConfigFunc = (
|
||||||
|
changeFunc: (config: PerspectiveConfig) => PerspectiveConfig,
|
||||||
|
reload?: boolean
|
||||||
|
) => void;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Expression, Select } from 'dbgate-sqltree';
|
import { Condition, Expression, Select } from 'dbgate-sqltree';
|
||||||
import { PerspectiveDataLoadProps } from './PerspectiveDataProvider';
|
import { PerspectiveDataLoadProps } from './PerspectiveDataProvider';
|
||||||
import debug from 'debug';
|
import debug from 'debug';
|
||||||
|
|
||||||
@@ -7,6 +7,37 @@ const dbg = debug('dbgate:PerspectiveDataLoader');
|
|||||||
export class PerspectiveDataLoader {
|
export class PerspectiveDataLoader {
|
||||||
constructor(public apiCall) {}
|
constructor(public apiCall) {}
|
||||||
|
|
||||||
|
buildCondition(props: PerspectiveDataLoadProps): Condition {
|
||||||
|
const { schemaName, pureName, bindingColumns, bindingValues, dataColumns, orderBy, condition } = props;
|
||||||
|
|
||||||
|
const conditions = [];
|
||||||
|
|
||||||
|
if (condition) {
|
||||||
|
conditions.push(condition);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bindingColumns?.length == 1) {
|
||||||
|
conditions.push({
|
||||||
|
conditionType: 'in',
|
||||||
|
expr: {
|
||||||
|
exprType: 'column',
|
||||||
|
columnName: bindingColumns[0],
|
||||||
|
source: {
|
||||||
|
name: { schemaName, pureName },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
values: bindingValues.map(x => x[0]),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return conditions.length > 0
|
||||||
|
? {
|
||||||
|
conditionType: 'and',
|
||||||
|
conditions,
|
||||||
|
}
|
||||||
|
: null;
|
||||||
|
}
|
||||||
|
|
||||||
async loadGrouping(props: PerspectiveDataLoadProps) {
|
async loadGrouping(props: PerspectiveDataLoadProps) {
|
||||||
const { schemaName, pureName, bindingColumns, bindingValues, dataColumns } = props;
|
const { schemaName, pureName, bindingColumns, bindingValues, dataColumns } = props;
|
||||||
|
|
||||||
@@ -40,20 +71,8 @@ export class PerspectiveDataLoader {
|
|||||||
},
|
},
|
||||||
...bindingColumnExpressions,
|
...bindingColumnExpressions,
|
||||||
],
|
],
|
||||||
|
where: this.buildCondition(props),
|
||||||
};
|
};
|
||||||
if (bindingColumns?.length == 1) {
|
|
||||||
select.where = {
|
|
||||||
conditionType: 'in',
|
|
||||||
expr: {
|
|
||||||
exprType: 'column',
|
|
||||||
columnName: bindingColumns[0],
|
|
||||||
source: {
|
|
||||||
name: { schemaName, pureName },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
values: bindingValues.map(x => x[0]),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
select.groupBy = bindingColumnExpressions;
|
select.groupBy = bindingColumnExpressions;
|
||||||
|
|
||||||
@@ -75,7 +94,8 @@ export class PerspectiveDataLoader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async loadData(props: PerspectiveDataLoadProps) {
|
async loadData(props: PerspectiveDataLoadProps) {
|
||||||
const { schemaName, pureName, bindingColumns, bindingValues, dataColumns, orderBy } = props;
|
const { schemaName, pureName, bindingColumns, bindingValues, dataColumns, orderBy, condition } = props;
|
||||||
|
|
||||||
const select: Select = {
|
const select: Select = {
|
||||||
commandType: 'select',
|
commandType: 'select',
|
||||||
from: {
|
from: {
|
||||||
@@ -98,20 +118,8 @@ export class PerspectiveDataLoader {
|
|||||||
},
|
},
|
||||||
})),
|
})),
|
||||||
range: props.range,
|
range: props.range,
|
||||||
|
where: this.buildCondition(props),
|
||||||
};
|
};
|
||||||
if (bindingColumns?.length == 1) {
|
|
||||||
select.where = {
|
|
||||||
conditionType: 'in',
|
|
||||||
expr: {
|
|
||||||
exprType: 'column',
|
|
||||||
columnName: bindingColumns[0],
|
|
||||||
source: {
|
|
||||||
name: { schemaName, pureName },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
values: bindingValues.map(x => x[0]),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dbg?.enabled) {
|
if (dbg?.enabled) {
|
||||||
dbg(
|
dbg(
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { Condition } from 'dbgate-sqltree';
|
||||||
import { RangeDefinition } from 'dbgate-types';
|
import { RangeDefinition } from 'dbgate-types';
|
||||||
import { format } from 'path';
|
import { format } from 'path';
|
||||||
import { PerspectiveBindingGroup, PerspectiveCache } from './PerspectiveCache';
|
import { PerspectiveBindingGroup, PerspectiveCache } from './PerspectiveCache';
|
||||||
@@ -20,6 +21,7 @@ export interface PerspectiveDataLoadProps {
|
|||||||
bindingValues?: any[][];
|
bindingValues?: any[][];
|
||||||
range?: RangeDefinition;
|
range?: RangeDefinition;
|
||||||
topCount?: number;
|
topCount?: number;
|
||||||
|
condition?: Condition;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class PerspectiveDataProvider {
|
export class PerspectiveDataProvider {
|
||||||
|
|||||||
@@ -1,18 +1,22 @@
|
|||||||
import { ColumnInfo, DatabaseInfo, ForeignKeyInfo, RangeDefinition, TableInfo } from 'dbgate-types';
|
import { ColumnInfo, DatabaseInfo, ForeignKeyInfo, RangeDefinition, TableInfo } from 'dbgate-types';
|
||||||
import { clearConfigCache } from 'prettier';
|
import { clearConfigCache } from 'prettier';
|
||||||
import { ChangePerspectiveConfigFunc, PerspectiveConfig } from './PerspectiveConfig';
|
import { ChangePerspectiveConfigFunc, PerspectiveConfig, PerspectiveConfigColumns } from './PerspectiveConfig';
|
||||||
import _isEqual from 'lodash/isEqual';
|
import _isEqual from 'lodash/isEqual';
|
||||||
import _cloneDeep from 'lodash/cloneDeep';
|
import _cloneDeep from 'lodash/cloneDeep';
|
||||||
import _compact from 'lodash/compact';
|
import _compact from 'lodash/compact';
|
||||||
import _uniq from 'lodash/uniq';
|
import _uniq from 'lodash/uniq';
|
||||||
import _flatten from 'lodash/flatten';
|
import _flatten from 'lodash/flatten';
|
||||||
import _uniqBy from 'lodash/uniqBy';
|
import _uniqBy from 'lodash/uniqBy';
|
||||||
|
import _cloneDeepWith from 'lodash/cloneDeepWith';
|
||||||
import {
|
import {
|
||||||
PerspectiveDatabaseConfig,
|
PerspectiveDatabaseConfig,
|
||||||
PerspectiveDataLoadProps,
|
PerspectiveDataLoadProps,
|
||||||
PerspectiveDataProvider,
|
PerspectiveDataProvider,
|
||||||
} from './PerspectiveDataProvider';
|
} from './PerspectiveDataProvider';
|
||||||
import stableStringify from 'json-stable-stringify';
|
import stableStringify from 'json-stable-stringify';
|
||||||
|
import { getFilterType, parseFilter } from 'dbgate-filterparser';
|
||||||
|
import { FilterType } from 'dbgate-filterparser/lib/types';
|
||||||
|
import { Condition, Expression } from 'dbgate-sqltree';
|
||||||
|
|
||||||
export interface PerspectiveDataLoadPropsWithNode {
|
export interface PerspectiveDataLoadPropsWithNode {
|
||||||
props: PerspectiveDataLoadProps;
|
props: PerspectiveDataLoadProps;
|
||||||
@@ -84,6 +88,9 @@ export abstract class PerspectiveTreeNode {
|
|||||||
get columnTitle() {
|
get columnTitle() {
|
||||||
return this.title;
|
return this.title;
|
||||||
}
|
}
|
||||||
|
get filterType(): FilterType {
|
||||||
|
return 'string';
|
||||||
|
}
|
||||||
|
|
||||||
getChildMatchColumns() {
|
getChildMatchColumns() {
|
||||||
return [];
|
return [];
|
||||||
@@ -93,6 +100,10 @@ export abstract class PerspectiveTreeNode {
|
|||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
parseFilterCondition() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
get childDataColumn() {
|
get childDataColumn() {
|
||||||
if (!this.isExpandable && this.isChecked) {
|
if (!this.isExpandable && this.isChecked) {
|
||||||
return this.codeName;
|
return this.codeName;
|
||||||
@@ -108,7 +119,7 @@ export abstract class PerspectiveTreeNode {
|
|||||||
this.includeInColumnSet('checkedColumns', this.uniqueName, value == null ? !this.isChecked : value);
|
this.includeInColumnSet('checkedColumns', this.uniqueName, value == null ? !this.isChecked : value);
|
||||||
}
|
}
|
||||||
|
|
||||||
includeInColumnSet(field: keyof PerspectiveConfig, uniqueName: string, isIncluded: boolean) {
|
includeInColumnSet(field: keyof PerspectiveConfigColumns, uniqueName: string, isIncluded: boolean) {
|
||||||
if (isIncluded) {
|
if (isIncluded) {
|
||||||
this.setConfig(cfg => ({
|
this.setConfig(cfg => ({
|
||||||
...cfg,
|
...cfg,
|
||||||
@@ -122,6 +133,23 @@ export abstract class PerspectiveTreeNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setFilter(value) {
|
||||||
|
this.setConfig(
|
||||||
|
cfg => ({
|
||||||
|
...cfg,
|
||||||
|
filters: {
|
||||||
|
...cfg.filters,
|
||||||
|
[this.uniqueName]: value,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
true
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
getFilter() {
|
||||||
|
return this.config.filters[this.uniqueName];
|
||||||
|
}
|
||||||
|
|
||||||
getDataLoadColumns() {
|
getDataLoadColumns() {
|
||||||
return _compact(
|
return _compact(
|
||||||
_uniq([
|
_uniq([
|
||||||
@@ -131,6 +159,20 @@ export abstract class PerspectiveTreeNode {
|
|||||||
])
|
])
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getChildrenCondition(): Condition {
|
||||||
|
const conditions = _compact(this.childNodes.map(x => x.parseFilterCondition()));
|
||||||
|
if (conditions.length == 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (conditions.length == 1) {
|
||||||
|
return conditions[0];
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
conditionType: 'and',
|
||||||
|
conditions,
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class PerspectiveTableColumnNode extends PerspectiveTreeNode {
|
export class PerspectiveTableColumnNode extends PerspectiveTreeNode {
|
||||||
@@ -205,6 +247,10 @@ export class PerspectiveTableColumnNode extends PerspectiveTreeNode {
|
|||||||
return !!this.foreignKey;
|
return !!this.foreignKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get filterType(): FilterType {
|
||||||
|
return getFilterType(this.column.dataType);
|
||||||
|
}
|
||||||
|
|
||||||
get childNodes(): PerspectiveTreeNode[] {
|
get childNodes(): PerspectiveTreeNode[] {
|
||||||
if (!this.foreignKey) return [];
|
if (!this.foreignKey) return [];
|
||||||
const tbl = this?.db?.tables?.find(
|
const tbl = this?.db?.tables?.find(
|
||||||
@@ -220,6 +266,23 @@ export class PerspectiveTableColumnNode extends PerspectiveTreeNode {
|
|||||||
this
|
this
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
parseFilterCondition() {
|
||||||
|
const filter = this.getFilter();
|
||||||
|
if (!filter) return null;
|
||||||
|
const condition = parseFilter(filter, this.filterType);
|
||||||
|
if (!condition) return null;
|
||||||
|
return _cloneDeepWith(condition, (expr: Expression) => {
|
||||||
|
if (expr.exprType == 'placeholder') {
|
||||||
|
return {
|
||||||
|
exprType: 'column',
|
||||||
|
columnName: this.column.columnName,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return condition;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class PerspectiveTableNode extends PerspectiveTreeNode {
|
export class PerspectiveTableNode extends PerspectiveTreeNode {
|
||||||
@@ -235,13 +298,14 @@ export class PerspectiveTableNode extends PerspectiveTreeNode {
|
|||||||
super(config, setConfig, parentNode, dataProvider, databaseConfig);
|
super(config, setConfig, parentNode, dataProvider, databaseConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
getNodeLoadProps(parentRows: any[]) {
|
getNodeLoadProps(parentRows: any[]): PerspectiveDataLoadProps {
|
||||||
return {
|
return {
|
||||||
schemaName: this.table.schemaName,
|
schemaName: this.table.schemaName,
|
||||||
pureName: this.table.pureName,
|
pureName: this.table.pureName,
|
||||||
dataColumns: this.getDataLoadColumns(),
|
dataColumns: this.getDataLoadColumns(),
|
||||||
databaseConfig: this.databaseConfig,
|
databaseConfig: this.databaseConfig,
|
||||||
orderBy: this.table.primaryKey?.columns.map(x => x.columnName) || [this.table.columns[0].columnName],
|
orderBy: this.table.primaryKey?.columns.map(x => x.columnName) || [this.table.columns[0].columnName],
|
||||||
|
condition: this.getChildrenCondition(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -320,6 +320,7 @@
|
|||||||
input {
|
input {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
min-width: 10px;
|
min-width: 10px;
|
||||||
|
width: 1px;
|
||||||
}
|
}
|
||||||
|
|
||||||
input.isError {
|
input.isError {
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
import debug from 'debug';
|
import debug from 'debug';
|
||||||
import contextMenu from '../utility/contextMenu';
|
import contextMenu from '../utility/contextMenu';
|
||||||
import DataFilterControl from '../datagrid/DataFilterControl.svelte';
|
import DataFilterControl from '../datagrid/DataFilterControl.svelte';
|
||||||
|
import { countVisibleRealColumns } from '../datagrid/gridutil';
|
||||||
|
|
||||||
const dbg = debug('dbgate:PerspectivaTable');
|
const dbg = debug('dbgate:PerspectivaTable');
|
||||||
|
|
||||||
@@ -115,12 +116,7 @@
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div
|
<div class="wrapper" bind:this={domWrapper} use:resizeObserver={true} use:contextMenu={buildMenu}>
|
||||||
class="wrapper"
|
|
||||||
bind:this={domWrapper}
|
|
||||||
use:resizeObserver={true}
|
|
||||||
use:contextMenu={buildMenu}
|
|
||||||
>
|
|
||||||
{#if display}
|
{#if display}
|
||||||
<table>
|
<table>
|
||||||
<thead>
|
<thead>
|
||||||
@@ -136,13 +132,18 @@
|
|||||||
{/each}
|
{/each}
|
||||||
</tr>
|
</tr>
|
||||||
{/each}
|
{/each}
|
||||||
<!-- <tr>
|
<tr>
|
||||||
{#each display.columns as column}
|
{#each display.columns as column}
|
||||||
<th>
|
<th class="filter">
|
||||||
<DataFilterControl filter="" setFilter={null} columnName={column.dataNode.codeName} filterType="string" />
|
<DataFilterControl
|
||||||
|
filter={column.dataNode.getFilter()}
|
||||||
|
setFilter={value => column.dataNode.setFilter(value)}
|
||||||
|
columnName={column.dataNode.uniqueName}
|
||||||
|
filterType={column.dataNode.filterType}
|
||||||
|
/>
|
||||||
</th>
|
</th>
|
||||||
{/each}
|
{/each}
|
||||||
</tr> -->
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{#each display.rows as row}
|
{#each display.rows as row}
|
||||||
@@ -218,6 +219,10 @@
|
|||||||
border-right: 1px solid var(--theme-border);
|
border-right: 1px solid var(--theme-border);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
th.filter {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
thead tr:first-child th {
|
thead tr:first-child th {
|
||||||
border-top: 1px solid var(--theme-border);
|
border-top: 1px solid var(--theme-border);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,7 +50,15 @@
|
|||||||
{schemaName}
|
{schemaName}
|
||||||
{pureName}
|
{pureName}
|
||||||
config={$config}
|
config={$config}
|
||||||
setConfig={config.update}
|
setConfig={(value, reload) => {
|
||||||
|
if (reload) {
|
||||||
|
cache.clear();
|
||||||
|
}
|
||||||
|
config.update(value);
|
||||||
|
if (reload) {
|
||||||
|
loadedCounts.set({});
|
||||||
|
}
|
||||||
|
}}
|
||||||
{cache}
|
{cache}
|
||||||
{loadedCounts}
|
{loadedCounts}
|
||||||
/>
|
/>
|
||||||
|
|||||||
Reference in New Issue
Block a user