mirror of
https://github.com/DeNNiiInc/dbgate.git
synced 2026-04-20 04:16:00 +00:00
custom expressions in query designer #306
This commit is contained in:
@@ -8,6 +8,7 @@ import {
|
|||||||
mergeConditions,
|
mergeConditions,
|
||||||
Source,
|
Source,
|
||||||
ResultField,
|
ResultField,
|
||||||
|
Expression,
|
||||||
} from 'dbgate-sqltree';
|
} from 'dbgate-sqltree';
|
||||||
import { EngineDriver } from 'dbgate-types';
|
import { EngineDriver } from 'dbgate-types';
|
||||||
import { DesignerInfo, DesignerTableInfo, DesignerReferenceInfo, DesignerJoinType } from './types';
|
import { DesignerInfo, DesignerTableInfo, DesignerReferenceInfo, DesignerJoinType } from './types';
|
||||||
@@ -120,7 +121,7 @@ export class DesignerQueryDumper {
|
|||||||
select.having,
|
select.having,
|
||||||
_.cloneDeepWith(condition, expr => {
|
_.cloneDeepWith(condition, expr => {
|
||||||
if (expr.exprType == 'placeholder') {
|
if (expr.exprType == 'placeholder') {
|
||||||
return this.getColumnOutputExpression(column, selectIsGrouped);
|
return this.getColumnResultField(column, selectIsGrouped);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
@@ -128,10 +129,29 @@ export class DesignerQueryDumper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getColumnOutputExpression(col, selectIsGrouped): ResultField {
|
getColumnExpression(col): Expression {
|
||||||
const source = findQuerySource(this.designer, col.designerId);
|
const source = findQuerySource(this.designer, col.designerId);
|
||||||
|
const { columnName, isCustomExpression, customExpression } = col;
|
||||||
|
|
||||||
|
const res: Expression = isCustomExpression
|
||||||
|
? {
|
||||||
|
exprType: 'raw',
|
||||||
|
sql: customExpression,
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
exprType: 'column',
|
||||||
|
columnName,
|
||||||
|
source,
|
||||||
|
};
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
getColumnResultField(col, selectIsGrouped): ResultField {
|
||||||
const { columnName } = col;
|
const { columnName } = col;
|
||||||
let { alias } = col;
|
let { alias } = col;
|
||||||
|
|
||||||
|
const exprCore = this.getColumnExpression(col);
|
||||||
|
|
||||||
if (selectIsGrouped && !col.isGrouped) {
|
if (selectIsGrouped && !col.isGrouped) {
|
||||||
// use aggregate
|
// use aggregate
|
||||||
const aggregate = col.aggregate == null || col.aggregate == '---' ? 'MAX' : col.aggregate;
|
const aggregate = col.aggregate == null || col.aggregate == '---' ? 'MAX' : col.aggregate;
|
||||||
@@ -142,20 +162,12 @@ export class DesignerQueryDumper {
|
|||||||
func: aggregate == 'COUNT DISTINCT' ? 'COUNT' : aggregate,
|
func: aggregate == 'COUNT DISTINCT' ? 'COUNT' : aggregate,
|
||||||
argsPrefix: aggregate == 'COUNT DISTINCT' ? 'DISTINCT' : null,
|
argsPrefix: aggregate == 'COUNT DISTINCT' ? 'DISTINCT' : null,
|
||||||
alias,
|
alias,
|
||||||
args: [
|
args: [exprCore],
|
||||||
{
|
|
||||||
exprType: 'column',
|
|
||||||
columnName,
|
|
||||||
source,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
return {
|
return {
|
||||||
exprType: 'column',
|
...exprCore,
|
||||||
columnName,
|
|
||||||
alias,
|
alias,
|
||||||
source,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -179,24 +191,21 @@ export class DesignerQueryDumper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const topLevelColumns = (this.designer.columns || []).filter(col =>
|
const topLevelColumns = (this.designer.columns || []).filter(
|
||||||
topLevelTables.find(tbl => tbl.designerId == col.designerId)
|
col =>
|
||||||
|
topLevelTables.find(tbl => tbl.designerId == col.designerId) || (col.isCustomExpression && col.customExpression)
|
||||||
);
|
);
|
||||||
const selectIsGrouped = !!topLevelColumns.find(x => x.isGrouped || (x.aggregate && x.aggregate != '---'));
|
const selectIsGrouped = !!topLevelColumns.find(x => x.isGrouped || (x.aggregate && x.aggregate != '---'));
|
||||||
const outputColumns = topLevelColumns.filter(x => x.isOutput);
|
const outputColumns = topLevelColumns.filter(x => x.isOutput);
|
||||||
if (outputColumns.length == 0) {
|
if (outputColumns.length == 0) {
|
||||||
res.selectAll = true;
|
res.selectAll = true;
|
||||||
} else {
|
} else {
|
||||||
res.columns = outputColumns.map(col => this.getColumnOutputExpression(col, selectIsGrouped));
|
res.columns = outputColumns.map(col => this.getColumnResultField(col, selectIsGrouped));
|
||||||
}
|
}
|
||||||
|
|
||||||
const groupedColumns = topLevelColumns.filter(x => x.isGrouped);
|
const groupedColumns = topLevelColumns.filter(x => x.isGrouped);
|
||||||
if (groupedColumns.length > 0) {
|
if (groupedColumns.length > 0) {
|
||||||
res.groupBy = groupedColumns.map(col => ({
|
res.groupBy = groupedColumns.map(col => this.getColumnExpression(col));
|
||||||
exprType: 'column',
|
|
||||||
columnName: col.columnName,
|
|
||||||
source: findQuerySource(this.designer, col.designerId),
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const orderColumns = _.sortBy(
|
const orderColumns = _.sortBy(
|
||||||
@@ -205,10 +214,8 @@ export class DesignerQueryDumper {
|
|||||||
);
|
);
|
||||||
if (orderColumns.length > 0) {
|
if (orderColumns.length > 0) {
|
||||||
res.orderBy = orderColumns.map(col => ({
|
res.orderBy = orderColumns.map(col => ({
|
||||||
exprType: 'column',
|
...this.getColumnExpression(col),
|
||||||
direction: col.sortOrder < 0 ? 'DESC' : 'ASC',
|
direction: col.sortOrder < 0 ? 'DESC' : 'ASC',
|
||||||
columnName: col.columnName,
|
|
||||||
source: findQuerySource(this.designer, col.designerId),
|
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -31,6 +31,8 @@ export type DesignerColumnInfo = {
|
|||||||
sortOrder?: number;
|
sortOrder?: number;
|
||||||
filter?: string;
|
filter?: string;
|
||||||
groupFilter?: string;
|
groupFilter?: string;
|
||||||
|
isCustomExpression?: boolean;
|
||||||
|
customExpression?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type DesignerSettings = {
|
export type DesignerSettings = {
|
||||||
|
|||||||
@@ -13,8 +13,10 @@
|
|||||||
import SelectField from '../forms/SelectField.svelte';
|
import SelectField from '../forms/SelectField.svelte';
|
||||||
import TextField from '../forms/TextField.svelte';
|
import TextField from '../forms/TextField.svelte';
|
||||||
import InlineButton from '../buttons/InlineButton.svelte';
|
import InlineButton from '../buttons/InlineButton.svelte';
|
||||||
|
import uuidv1 from 'uuid/v1';
|
||||||
|
|
||||||
import TableControl from './TableControl.svelte';
|
import TableControl from './TableControl.svelte';
|
||||||
|
import FormStyledButton from '../buttons/FormStyledButton.svelte';
|
||||||
|
|
||||||
export let value;
|
export let value;
|
||||||
export let onChange;
|
export let onChange;
|
||||||
@@ -35,6 +37,13 @@
|
|||||||
}));
|
}));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const addExpressionColumn = () => {
|
||||||
|
onChange(current => ({
|
||||||
|
...current,
|
||||||
|
columns: [...(current.columns || []), { isCustomExpression: true, isOutput: true, designerId: uuidv1() }],
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
$: columns = value?.columns;
|
$: columns = value?.columns;
|
||||||
$: tables = value?.tables;
|
$: tables = value?.tables;
|
||||||
$: hasGroupedColumn = !!(columns || []).find(x => x.isGrouped);
|
$: hasGroupedColumn = !!(columns || []).find(x => x.isGrouped);
|
||||||
@@ -44,7 +53,7 @@
|
|||||||
<TableControl
|
<TableControl
|
||||||
rows={columns || []}
|
rows={columns || []}
|
||||||
columns={[
|
columns={[
|
||||||
{ fieldName: 'columnName', header: 'Column/Expression' },
|
{ fieldName: 'columnName', slot: 8, header: 'Column/Expression' },
|
||||||
{ fieldName: 'tableDisplayName', header: 'Table', formatter: row => getTableDisplayName(row, tables) },
|
{ fieldName: 'tableDisplayName', header: 'Table', formatter: row => getTableDisplayName(row, tables) },
|
||||||
{ fieldName: 'isOutput', header: 'Output', slot: 0 },
|
{ fieldName: 'isOutput', header: 'Output', slot: 0 },
|
||||||
{ fieldName: 'alias', header: 'Alias', slot: 1 },
|
{ fieldName: 'alias', header: 'Alias', slot: 1 },
|
||||||
@@ -56,6 +65,19 @@
|
|||||||
{ fieldName: 'actions', header: '', slot: 7 },
|
{ fieldName: 'actions', header: '', slot: 7 },
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
|
<svelte:fragment slot="8" let:row>
|
||||||
|
{#if row.isCustomExpression}
|
||||||
|
<TextField
|
||||||
|
value={row.customExpression}
|
||||||
|
on:input={e => {
|
||||||
|
changeColumn({ ...row, customExpression: e.target.value });
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
{:else}
|
||||||
|
{row.columnName}
|
||||||
|
{/if}
|
||||||
|
</svelte:fragment>
|
||||||
|
|
||||||
<svelte:fragment slot="0" let:row>
|
<svelte:fragment slot="0" let:row>
|
||||||
<CheckboxField
|
<CheckboxField
|
||||||
checked={row.isOutput}
|
checked={row.isOutput}
|
||||||
@@ -134,6 +156,7 @@
|
|||||||
<InlineButton on:click={() => removeColumn(row)}>Remove</InlineButton>
|
<InlineButton on:click={() => removeColumn(row)}>Remove</InlineButton>
|
||||||
</svelte:fragment>
|
</svelte:fragment>
|
||||||
</TableControl>
|
</TableControl>
|
||||||
|
<FormStyledButton value="Add custom" on:click={addExpressionColumn} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
|||||||
@@ -92,6 +92,8 @@
|
|||||||
{:else if col.slot == 5}<slot name="5" {row} {index} />
|
{:else if col.slot == 5}<slot name="5" {row} {index} />
|
||||||
{:else if col.slot == 6}<slot name="6" {row} {index} />
|
{:else if col.slot == 6}<slot name="6" {row} {index} />
|
||||||
{:else if col.slot == 7}<slot name="7" {row} {index} />
|
{:else if col.slot == 7}<slot name="7" {row} {index} />
|
||||||
|
{:else if col.slot == 8}<slot name="8" {row} {index} />
|
||||||
|
{:else if col.slot == 9}<slot name="9" {row} {index} />
|
||||||
{/if}
|
{/if}
|
||||||
{:else}
|
{:else}
|
||||||
{row[col.fieldName] || ''}
|
{row[col.fieldName] || ''}
|
||||||
|
|||||||
Reference in New Issue
Block a user