dbeaver/pro#7562 [CB] keep data editor filter state after reconnect#4238
dbeaver/pro#7562 [CB] keep data editor filter state after reconnect#4238SychevAndrey wants to merge 25 commits intodevelfrom
Conversation
allow using attrName without attrPosition
replaced private restorePendingState() with public restoreViewState(pinnedColumnNames, columnOrderNames?) that takes data directly as arguments
bug with skipping constraints if any without a position.
if (!prevColumn) {
return;
}
… in GridViewAction
| if (!usedIndices.has(key)) { | ||
| newOrder.push(key); | ||
| } | ||
| } |
There was a problem hiding this comment.
What does this check stand for?
What columns didn't we see in the previous cycle above?
There was a problem hiding this comment.
The answer is the same as for indecies. Someone could edit our table and add some columns. So we will not have them in our orderColumns array. Here we put them in the end (I am open to other solutions)
| export interface IDataViewerPersistedState { | ||
| constraints: IPersistedConstraint[]; | ||
| whereFilter: string; | ||
| pinnedColumns: string[]; |
There was a problem hiding this comment.
I'm a bit confused that we use column names as unique identifiers. This feels tricky and has a bug potential
In most of DBs it is forbidden to name 2 columns with the same name it is true. But is it in all DBs? I have a little doubt here
Also we can have this kind of case:
SELECT a AS col, b AS col FROM table;
We execute this in sql editor. See the table. What would happen? Is there a chance one of the columns disappears? Or any other related bug happened?
My suggestion is to add unique index for each column and it will resolve a lot of potential issues
There was a problem hiding this comment.
what will be the index then? How would you resolve it if someone added a column or two while you was working on something else and then reopen the table?
There was a problem hiding this comment.
what will be the index then?
The backend sends the id for each column
How would you resolve it if someone added a column or two while you were working on something else and then reopened the table?
Maybe append it to the end or the beginning of the current view?
| console.warn('[DataViewerTableState] Auto-save failed', exception); | ||
| } | ||
| }, | ||
| { delay: 1000, fireImmediately: false, equals: comparer.structural }, |
There was a problem hiding this comment.
maybe we can not throttle it after any changes, but debounce? like save the state once in 5 seconds after any change?
There was a problem hiding this comment.
Does it spam the table with requests every second if you're actively working with it? if so debounce would reduce amount of requests to save the whole state
There was a problem hiding this comment.
ok I see. I don't think it's a big problem with comparing on each action, but I guess the deep equal is overhead here, so I just simplify it a bit and let MobX compare by pointer. You should make 1000 operation/sec to make it lag :) effect is already delayed, I guess 5 sec is too much, localstorage is fast enough and the size of saved object is relatively small until someone has thousands of columns and pinned half of them
| private validateState(data: IDataViewerPersistedState): boolean { | ||
| if (!data || typeof data !== 'object') { | ||
| return false; | ||
| } | ||
|
|
||
| if (!Array.isArray(data.constraints)) { | ||
| return false; | ||
| } | ||
|
|
||
| for (const c of data.constraints) { | ||
| if (!c || typeof c.attributeName !== 'string' || c.attributeName.length === 0) { | ||
| return false; | ||
| } | ||
| } | ||
|
|
||
| if (typeof data.whereFilter !== 'string') { | ||
| return false; | ||
| } | ||
|
|
||
| if (!Array.isArray(data.pinnedColumns) || !data.pinnedColumns.every(c => typeof c === 'string')) { | ||
| return false; | ||
| } | ||
|
|
||
| if (data.columnOrder !== undefined && (!Array.isArray(data.columnOrder) || !data.columnOrder.every(c => typeof c === 'string'))) { | ||
| return false; | ||
| } | ||
|
|
||
| return true; | ||
| } |
There was a problem hiding this comment.
seems like zod is gonna be quite handy here
webapp/packages/plugin-data-viewer/src/DataViewerTableState/DataViewerTableStateService.ts
Outdated
Show resolved
Hide resolved
…of github.com:dbeaver/cloudbeaver into 7562-cb-keep-data-editor-filter-state-after-reconnect
| return this.columnKeys | ||
| .filter(key => this.isColumnPinned(key)) | ||
| .map(key => this.getColumnName(key)) | ||
| .filter((name): name is string => name !== undefined); |
There was a problem hiding this comment.
we can use isDefined for that
| const name = this.getColumnName(key); | ||
|
|
||
| if (name) { | ||
| nameToIndex.set(name, key.index); |
There was a problem hiding this comment.
seems like we need to add new function, something like getColumnIndexByName
| if (pinnedColumnNames.length > 0) { | ||
| const keys = pinnedColumnNames | ||
| .map(name => this.columnKeys.find(k => this.getColumnName(k) === name)) | ||
| .filter((k): k is IGridColumnKey => k !== undefined); |
| updateConstraintsForResult(this.source, result); | ||
| } | ||
|
|
||
| private getColumnNameByPosition(attributePosition: number): string | undefined { |
There was a problem hiding this comment.
probably it should be implemented on the GridViewAction level as we might need it in different places, we have similar logic there, in general, logic that find data column by certain criteria should be gathered in one place and be accessible from different places. Also we have similar logic inside useTableData
There was a problem hiding this comment.
i think it's better to implement on the GridDataResultAction side, but there is problem GridDataResultAction is abstraction over grid structures, but this function is requires specific ResultSet data type, so it should be implemented in the ResultSetDataAction and can be abstract in the GridDataResultAction
| } | ||
| } | ||
|
|
||
| restoreViewState(pinnedColumnNames: string[], columnOrderNames?: string[]): void { |
There was a problem hiding this comment.
i think it's better to pass an interface as a single argument to this function, so it can be easily extended if needed
| updateConstraintsForResult(this.source, result); | ||
| } | ||
|
|
||
| private getColumnNameByPosition(attributePosition: number): string | undefined { |
There was a problem hiding this comment.
i think it's better to implement on the GridDataResultAction side, but there is problem GridDataResultAction is abstraction over grid structures, but this function is requires specific ResultSet data type, so it should be implemented in the ResultSetDataAction and can be abstract in the GridDataResultAction
| let viewStateRestored = false; | ||
|
|
||
| const cancelViewRestore = hasViewState | ||
| ? reaction( |
There was a problem hiding this comment.
since you using reaction here, it will be much easier to have some state interface, and read-mutate it directly in the database data actions, so it will be automatically synced and restored
i think you can introduce the new database data action for persisted state and re-use it in the actions
Up to standards ✅🟢 Issues
|
| Metric | Results |
|---|---|
| Complexity | 0 |
TIP This summary will be updated as you push new changes. Give us feedback
… centralized state persistence
…LDataFilter" This reverts commit 3e0e86f. Last test showed that we don't need any additional logic on BE. It handles all constraints properly.
No description provided.