From a70c23e05663d0ef3ab780f2f962fb9231293134 Mon Sep 17 00:00:00 2001 From: Wroud Date: Thu, 11 Jun 2020 18:09:24 +0300 Subject: [PATCH 1/2] refactor(data-viewer-plugin): data filter --- .../src/DataViewerTableService.ts | 12 +++-- .../data-viewer-plugin/src/DataViewerUtils.ts | 19 -------- .../TableViewer/TableFooter/TableFooter.tsx | 2 +- .../src/TableViewer/TableViewerModel.ts | 44 ++++++++++++++----- .../packages/data-viewer-plugin/src/index.ts | 2 - .../SqlResultTabs/SQLQueryExecutionProcess.ts | 34 +++++++------- .../SqlResultPanelController.ts | 17 ++++--- .../src/SqlResultTabs/SqlResultService.ts | 39 ++++++++-------- .../src/SqlResultTabs/SqlResultTabsService.ts | 8 +++- 9 files changed, 94 insertions(+), 83 deletions(-) delete mode 100644 webapp/packages/data-viewer-plugin/src/DataViewerUtils.ts diff --git a/webapp/packages/data-viewer-plugin/src/DataViewerTableService.ts b/webapp/packages/data-viewer-plugin/src/DataViewerTableService.ts index 9b6ed74f764..c3a88c736c1 100644 --- a/webapp/packages/data-viewer-plugin/src/DataViewerTableService.ts +++ b/webapp/packages/data-viewer-plugin/src/DataViewerTableService.ts @@ -9,10 +9,9 @@ import { injectable } from '@dbeaver/core/di'; import { GraphQLService } from '@dbeaver/core/sdk'; -import { RequestDataOptionsToConstrains } from './DataViewerUtils'; import { IExecutionContext } from './IExecutionContext'; import { RowDiff } from './TableViewer/TableDataModel/EditedRow'; -import { IRequestDataResult, IRequestDataResultOptions, TableViewerModel } from './TableViewer/TableViewerModel'; +import { IRequestDataResult, TableViewerModel } from './TableViewer/TableViewerModel'; import { TableViewerStorageService } from './TableViewer/TableViewerStorageService'; @@ -99,9 +98,8 @@ export class DataViewerTableService { private async requestDataAsync( data: TableViewerModel, - rowOffset: number, - count: number, - options: IRequestDataResultOptions, + offset: number, + count: number ): Promise { if (!data.containerNodePath) { throw new Error('containerNodePath must be provided for table'); @@ -118,9 +116,9 @@ export class DataViewerTableService { contextId: data.executionContext.contextId, containerNodePath: data.containerNodePath, filter: { - offset: rowOffset, + offset, limit: count, - constraints: RequestDataOptionsToConstrains(options), + constraints: Array.from(data.getSortedColumns()), }, }); const dataSet = readDataFromContainer!.results[0].resultSet!; // we expect only one dataset for a table diff --git a/webapp/packages/data-viewer-plugin/src/DataViewerUtils.ts b/webapp/packages/data-viewer-plugin/src/DataViewerUtils.ts deleted file mode 100644 index 0bbbd0f26f8..00000000000 --- a/webapp/packages/data-viewer-plugin/src/DataViewerUtils.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { SqlDataFilterConstraint } from '@dbeaver/core/sdk'; - -import { IRequestDataResultOptions } from './TableViewer/TableViewerModel'; - -export function RequestDataOptionsToConstrains( - options?: IRequestDataResultOptions -): SqlDataFilterConstraint[] | undefined { - const constraints: SqlDataFilterConstraint[] = (options?.sorting || []) - .map((columnSorting, index) => { - const constrain: SqlDataFilterConstraint = { - attribute: columnSorting.colId, - orderPosition: index, - orderAsc: columnSorting.sort === 'asc', - }; - return constrain; - }); - - return constraints.length ? constraints : undefined; -} diff --git a/webapp/packages/data-viewer-plugin/src/TableViewer/TableFooter/TableFooter.tsx b/webapp/packages/data-viewer-plugin/src/TableViewer/TableFooter/TableFooter.tsx index f1829ff4cd0..bbf412c6066 100644 --- a/webapp/packages/data-viewer-plugin/src/TableViewer/TableFooter/TableFooter.tsx +++ b/webapp/packages/data-viewer-plugin/src/TableViewer/TableFooter/TableFooter.tsx @@ -81,7 +81,7 @@ export const TableFooter = observer(function TableFooter({ return styled(useStyles(tableFooterStyles))( - + diff --git a/webapp/packages/data-viewer-plugin/src/TableViewer/TableViewerModel.ts b/webapp/packages/data-viewer-plugin/src/TableViewer/TableViewerModel.ts index 29d47e4a318..6e63e22f2c3 100644 --- a/webapp/packages/data-viewer-plugin/src/TableViewer/TableViewerModel.ts +++ b/webapp/packages/data-viewer-plugin/src/TableViewer/TableViewerModel.ts @@ -13,8 +13,8 @@ import { } from '@dbeaver/ag-grid-plugin'; import { ErrorDetailsDialog } from '@dbeaver/core/app'; import { CommonDialogService } from '@dbeaver/core/dialogs'; -import { GQLError } from '@dbeaver/core/sdk'; -import { uuid } from '@dbeaver/core/utils'; +import { GQLError, SqlDataFilterConstraint } from '@dbeaver/core/sdk'; +import { uuid, MetadataMap } from '@dbeaver/core/utils'; import { IExecutionContext } from '../IExecutionContext'; import { ErrorDialog } from './ErrorDialog'; @@ -46,8 +46,8 @@ export interface ITableViewerModelOptions { requestDataAsync( model: TableViewerModel, rowOffset: number, - count: number, - options?: IRequestDataResultOptions): Promise; + count: number + ): Promise; saveChanges(model: TableViewerModel, diffs: RowDiff[]): Promise; } @@ -70,8 +70,8 @@ export class TableViewerModel implements ITableViewerModelOptions { requestDataAsync: ( model: TableViewerModel, rowOffset: number, - count: number, - options?: IRequestDataResultOptions) => Promise; + count: number + ) => Promise; saveChanges: (model: TableViewerModel, diffs: RowDiff[]) => Promise; agGridModel: IAgGridModel = { @@ -97,7 +97,7 @@ export class TableViewerModel implements ITableViewerModelOptions { getChunkSize = () => this._chunkSize; setChunkSize = (count: number) => this.updateChunkSize(count); - handleRefresh = () => this.resetData(); + refresh = () => this.resetData(); @observable queryDuration = 0; @observable requestStatusMessage = ''; @@ -112,6 +112,9 @@ export class TableViewerModel implements ITableViewerModelOptions { private exception: GQLError | null = null; private tableDataModel = new TableDataModel(); private tableEditor = new TableEditor(this.tableDataModel); + private sortedColumns = new MetadataMap( + (colId, metadata) => ({ attribute: colId, orderPosition: metadata.count(), orderAsc: false }) + ); constructor( options: ITableViewerModelOptions, @@ -137,6 +140,23 @@ export class TableViewerModel implements ITableViewerModelOptions { } } + getSortedColumns() { + return this.sortedColumns.values(); + } + + setColumnSorting(colId: string, orderAsc?: boolean, multiple?: boolean) { + if (!multiple) { + this.sortedColumns.clear(); + } + + const sorting = this.sortedColumns.get(colId); + sorting.orderAsc = orderAsc; + } + + removeColumnSorting(colId: string) { + this.sortedColumns.delete(colId); + } + @action insertRows(position: number, rows: TableRow[], hasMore: boolean) { const isRowsAddition = this.tableDataModel.getRows().length < position + rows.length; @@ -159,7 +179,7 @@ export class TableViewerModel implements ITableViewerModelOptions { this.tableEditor.editCellValue(rowNumber, colNumber, value); } - private async onRequestData(rowOffset: number, count: number, options: IRequestDataOptions): Promise { + private async onRequestData(rowOffset: number, count: number): Promise { // try to return data from cache if (this.tableDataModel.isChunkLoaded(rowOffset, count) || this.isFullyLoaded) { const data: IRequestedData = { @@ -173,7 +193,7 @@ export class TableViewerModel implements ITableViewerModelOptions { this._isLoaderVisible = !this.noLoaderWhileRequestingDataAsync; try { - const response = await this.requestDataAsync(this, rowOffset, count, options); + const response = await this.requestDataAsync(this, rowOffset, count); this.insertRows(rowOffset, response.rows, !response.isFullyLoaded); if (!this.tableDataModel.getColumns().length) { @@ -261,7 +281,11 @@ export class TableViewerModel implements ITableViewerModelOptions { } } - private onSortChanged() { + private onSortChanged(sorting: SortModel) { + this.sortedColumns.clear(); + for (const sort of sorting) { + this.setColumnSorting(sort.colId, sort.sort === 'asc', true); + } this.resetData(); } diff --git a/webapp/packages/data-viewer-plugin/src/index.ts b/webapp/packages/data-viewer-plugin/src/index.ts index b7037de5398..cfc5078a2fd 100644 --- a/webapp/packages/data-viewer-plugin/src/index.ts +++ b/webapp/packages/data-viewer-plugin/src/index.ts @@ -13,6 +13,4 @@ export * from './TableViewer/TableDataModel/TableColumn'; export * from './TableViewer/TableDataModel/TableRow'; export * from './TableViewer/TableDataModel/EditedRow'; -export * from './DataViewerUtils'; - export * from './IExecutionContext'; diff --git a/webapp/packages/sql-editor/src/SqlResultTabs/SQLQueryExecutionProcess.ts b/webapp/packages/sql-editor/src/SqlResultTabs/SQLQueryExecutionProcess.ts index cb5760adc9f..8d0ab565c65 100644 --- a/webapp/packages/sql-editor/src/SqlResultTabs/SQLQueryExecutionProcess.ts +++ b/webapp/packages/sql-editor/src/SqlResultTabs/SQLQueryExecutionProcess.ts @@ -8,12 +8,11 @@ import { NotificationService } from '@dbeaver/core/eventsLog'; import { - AsyncTaskInfo, GraphQLService, ServerInternalError, SqlExecuteInfo, + AsyncTaskInfo, GraphQLService, ServerInternalError, SqlExecuteInfo, SqlDataFilter, } from '@dbeaver/core/sdk'; import { CancellablePromise, cancellableTimeout, Deferred, EDeferredState, } from '@dbeaver/core/utils'; -import { IRequestDataResultOptions, RequestDataOptionsToConstrains } from '@dbeaver/data-viewer-plugin'; import { ISqlQueryParams } from '../ISqlEditorTabState'; @@ -25,18 +24,20 @@ export class SQLQueryExecutionProcess extends Deferred { private timeout?: CancellablePromise; private isCancelConfirmed = false; // true when server successfully executed cancelQueryAsync - constructor(private graphQLService: GraphQLService, - private notificationService: NotificationService) { + constructor( + private graphQLService: GraphQLService, + private notificationService: NotificationService + ) { super(); } - async start(sqlQueryParams: ISqlQueryParams, - rowOffset: number, - count: number, - options?: IRequestDataResultOptions): Promise { + async start( + sqlQueryParams: ISqlQueryParams, + filter: SqlDataFilter + ): Promise { // start async task try { - const taskInfo = await this.executeQueryAsync(sqlQueryParams, rowOffset, count, options); + const taskInfo = await this.executeQueryAsync(sqlQueryParams, filter); this.applyResult(taskInfo); this.taskId = taskInfo.id; if (this.getState() === EDeferredState.CANCELLING) { @@ -104,20 +105,15 @@ export class SQLQueryExecutionProcess extends Deferred { } } - private async executeQueryAsync(sqlQueryParams: ISqlQueryParams, - rowOffset: number, - count: number, - options?: IRequestDataResultOptions): Promise { + private async executeQueryAsync( + sqlQueryParams: ISqlQueryParams, + filter: SqlDataFilter, + ): Promise { const { taskInfo } = await this.graphQLService.gql.asyncSqlExecuteQuery({ connectionId: sqlQueryParams.connectionId, contextId: sqlQueryParams.contextId, query: sqlQueryParams.query, - - filter: { - offset: rowOffset, - limit: count, - constraints: RequestDataOptionsToConstrains(options), - }, + filter, }); return taskInfo; } diff --git a/webapp/packages/sql-editor/src/SqlResultTabs/SqlResultPanel/SqlResultPanelController.ts b/webapp/packages/sql-editor/src/SqlResultTabs/SqlResultPanel/SqlResultPanelController.ts index 91e8a5f2e41..43a8dbb630b 100644 --- a/webapp/packages/sql-editor/src/SqlResultTabs/SqlResultPanel/SqlResultPanelController.ts +++ b/webapp/packages/sql-editor/src/SqlResultTabs/SqlResultPanel/SqlResultPanelController.ts @@ -140,16 +140,23 @@ implements IInitializableController, IDestructibleController { private async requestDataAsync( sqlExecutingState: SqlExecutionState, model: TableViewerModel, - rowOffset: number, - count: number, - options: IRequestDataResultOptions, + offset: number, + count: number ): Promise { const queryExecutionProcess = this.sqlResultService - .asyncSqlQuery(this.panelInit.sqlQueryParams, rowOffset, count, options); + .asyncSqlQuery(this.panelInit.sqlQueryParams, { + offset, + limit: count, + constraints: Array.from(model.getSortedColumns()), + }); sqlExecutingState.setCurrentlyExecutingQuery(queryExecutionProcess); const response = await queryExecutionProcess.promise; - const dataResults = this.sqlResultService.sqlExecuteInfoToData(response, this.panelInit.indexInResultSet, count); + const dataResults = this.sqlResultService.sqlExecuteInfoToData( + response, + this.panelInit.indexInResultSet, + count + ); /** * Note that each data fetching overwrites resultId diff --git a/webapp/packages/sql-editor/src/SqlResultTabs/SqlResultService.ts b/webapp/packages/sql-editor/src/SqlResultTabs/SqlResultService.ts index 332941ce62a..b3866ba9f6a 100644 --- a/webapp/packages/sql-editor/src/SqlResultTabs/SqlResultService.ts +++ b/webapp/packages/sql-editor/src/SqlResultTabs/SqlResultService.ts @@ -11,13 +11,10 @@ import { NotificationService } from '@dbeaver/core/eventsLog'; import { GraphQLService, SqlExecuteInfo, + SqlDataFilter, + SqlDataFilterConstraint, } from '@dbeaver/core/sdk'; -import { - IRequestDataResult, - RowDiff, - IRequestDataResultOptions, - RequestDataOptionsToConstrains, -} from '@dbeaver/data-viewer-plugin'; +import { IRequestDataResult, RowDiff } from '@dbeaver/data-viewer-plugin'; import { ISqlQueryParams } from '../ISqlEditorTabState'; import { SQLQueryExecutionProcess } from './SQLQueryExecutionProcess'; @@ -31,10 +28,12 @@ export class SqlResultService { /** * @deprecated use asyncSqlQuery */ - async fetchData(sqlQueryParams: ISqlQueryParams, - rowOffset: number, - count: number, - options: IRequestDataResultOptions): Promise { + async fetchData( + sqlQueryParams: ISqlQueryParams, + rowOffset: number, + count: number, + constraints?: SqlDataFilterConstraint[] + ): Promise { const response = await this.graphQLService.gql.executeSqlQuery({ connectionId: sqlQueryParams.connectionId, contextId: sqlQueryParams.contextId, @@ -43,25 +42,27 @@ export class SqlResultService { filter: { offset: rowOffset, limit: count, - constraints: RequestDataOptionsToConstrains(options), + constraints, }, }); return response.result!; } - asyncSqlQuery(sqlQueryParams: ISqlQueryParams, - rowOffset: number, - count: number, - options?: IRequestDataResultOptions): SQLQueryExecutionProcess { + asyncSqlQuery( + sqlQueryParams: ISqlQueryParams, + filter: SqlDataFilter + ): SQLQueryExecutionProcess { const cancellableSqlQuery = new SQLQueryExecutionProcess(this.graphQLService, this.notificationService); - cancellableSqlQuery.start(sqlQueryParams, rowOffset, count, options); + cancellableSqlQuery.start(sqlQueryParams, filter); return cancellableSqlQuery; } - async saveChanges(sqlQueryParams: ISqlQueryParams, - resultId: string, - diffs: RowDiff[]): Promise { + async saveChanges( + sqlQueryParams: ISqlQueryParams, + resultId: string, + diffs: RowDiff[] + ): Promise { const firstRow = diffs[0]; // todo multiple row to be implemented later diff --git a/webapp/packages/sql-editor/src/SqlResultTabs/SqlResultTabsService.ts b/webapp/packages/sql-editor/src/SqlResultTabs/SqlResultTabsService.ts index 483fed9ec8c..15f56b99ded 100644 --- a/webapp/packages/sql-editor/src/SqlResultTabs/SqlResultTabsService.ts +++ b/webapp/packages/sql-editor/src/SqlResultTabs/SqlResultTabsService.ts @@ -34,7 +34,13 @@ export class SqlResultTabsService { }; // we should first render table, because we expect that table request first data portion - const queryExecutionProcess = this.sqlResultService.asyncSqlQuery(sqlQueryParams, 0, fetchingSettings.fetchDefault); + const queryExecutionProcess = this.sqlResultService.asyncSqlQuery( + sqlQueryParams, + { + offset: 0, + limit: fetchingSettings.fetchDefault, + } + ); editorState.sqlExecutionState.setCurrentlyExecutingQuery(queryExecutionProcess); From 5a56a099c8ef82c348282b005b6b0276877e1560 Mon Sep 17 00:00:00 2001 From: Wroud Date: Thu, 11 Jun 2020 18:21:43 +0300 Subject: [PATCH 2/2] feat(data-viewer-plugin): add where filter support for model CB-85 --- .../src/DataViewerTableService.ts | 21 ++++++++++--------- .../src/TableViewer/TableViewerModel.ts | 10 +++++++++ .../SqlResultPanelController.ts | 3 +-- 3 files changed, 22 insertions(+), 12 deletions(-) diff --git a/webapp/packages/data-viewer-plugin/src/DataViewerTableService.ts b/webapp/packages/data-viewer-plugin/src/DataViewerTableService.ts index c3a88c736c1..8122701bb91 100644 --- a/webapp/packages/data-viewer-plugin/src/DataViewerTableService.ts +++ b/webapp/packages/data-viewer-plugin/src/DataViewerTableService.ts @@ -97,32 +97,33 @@ export class DataViewerTableService { } private async requestDataAsync( - data: TableViewerModel, + model: TableViewerModel, offset: number, count: number ): Promise { - if (!data.containerNodePath) { + if (!model.containerNodePath) { throw new Error('containerNodePath must be provided for table'); } - if (!data.executionContext) { + if (!model.executionContext) { // it is first data request - const executionContext: IExecutionContext = await this.createExecutionContext(data.connectionId); - data.executionContext = executionContext; + const executionContext: IExecutionContext = await this.createExecutionContext(model.connectionId); + model.executionContext = executionContext; } const { readDataFromContainer } = await this.graphQLService.gql.readDataFromContainer({ - connectionId: data.executionContext.connectionId, - contextId: data.executionContext.contextId, - containerNodePath: data.containerNodePath, + connectionId: model.executionContext.connectionId, + contextId: model.executionContext.contextId, + containerNodePath: model.containerNodePath, filter: { offset, limit: count, - constraints: Array.from(data.getSortedColumns()), + constraints: Array.from(model.getSortedColumns()), + where: model.getQueryWhereFilter() || undefined, }, }); const dataSet = readDataFromContainer!.results[0].resultSet!; // we expect only one dataset for a table - data.resultId = dataSet.id; // server generates new resultId on each fetch + model.resultId = dataSet.id; // server generates new resultId on each fetch const result: IRequestDataResult = { rows: dataSet.rows!, diff --git a/webapp/packages/data-viewer-plugin/src/TableViewer/TableViewerModel.ts b/webapp/packages/data-viewer-plugin/src/TableViewer/TableViewerModel.ts index 6e63e22f2c3..e8a7a5c1649 100644 --- a/webapp/packages/data-viewer-plugin/src/TableViewer/TableViewerModel.ts +++ b/webapp/packages/data-viewer-plugin/src/TableViewer/TableViewerModel.ts @@ -108,6 +108,7 @@ export class TableViewerModel implements ITableViewerModelOptions { @observable private _hasMoreRows = true @observable private _isLoaderVisible = false; @observable private _chunkSize: number = this.getDefaultRowsCount(); + @observable private queryWhereFilter: string | null = null; private exception: GQLError | null = null; private tableDataModel = new TableDataModel(); @@ -140,6 +141,15 @@ export class TableViewerModel implements ITableViewerModelOptions { } } + getQueryWhereFilter() { + return this.queryWhereFilter; + } + + setQueryWhereFilter(where: string | null) { + this.queryWhereFilter = where; + this.resetData(); + } + getSortedColumns() { return this.sortedColumns.values(); } diff --git a/webapp/packages/sql-editor/src/SqlResultTabs/SqlResultPanel/SqlResultPanelController.ts b/webapp/packages/sql-editor/src/SqlResultTabs/SqlResultPanel/SqlResultPanelController.ts index 43a8dbb630b..019f3b8b2a6 100644 --- a/webapp/packages/sql-editor/src/SqlResultTabs/SqlResultPanel/SqlResultPanelController.ts +++ b/webapp/packages/sql-editor/src/SqlResultTabs/SqlResultPanel/SqlResultPanelController.ts @@ -17,11 +17,9 @@ import { PromiseCancelledError } from '@dbeaver/core/utils'; import { fetchingSettings, IRequestDataResult, - IRequestDataResultOptions, RowDiff, TableViewerStorageService, TableViewerModel, - IExecutionContext, } from '@dbeaver/data-viewer-plugin'; import { ISqlResultPanelParams } from '../../ISqlEditorTabState'; @@ -149,6 +147,7 @@ implements IInitializableController, IDestructibleController { offset, limit: count, constraints: Array.from(model.getSortedColumns()), + where: model.getQueryWhereFilter() || undefined, }); sqlExecutingState.setCurrentlyExecutingQuery(queryExecutionProcess); const response = await queryExecutionProcess.promise;