custom expressions in query designer #306

This commit is contained in:
Jan Prochazka
2022-07-16 20:53:11 +02:00
parent c368ad8d54
commit a4e5630f89
4 changed files with 59 additions and 25 deletions

View File

@@ -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),
})); }));
} }

View File

@@ -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 = {

View File

@@ -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>

View File

@@ -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] || ''}