You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@druid.apache.org by vo...@apache.org on 2024/06/22 01:33:19 UTC
(druid) 03/04: moves
This is an automated email from the ASF dual-hosted git repository.
vogievetsky pushed a commit to branch power-tiles
in repository https://gitbox.apache.org/repos/asf/druid.git
commit 70099ea4a8920d53be1ac95a9d185812e104c0c9
Author: Vadim Ogievetsky <va...@ogievetsky.com>
AuthorDate: Fri Jun 21 14:20:15 2024 -0700
moves
---
.../generic-output-table/generic-output-table.tsx | 11 +--
web-console/src/modules/models/query-source.ts | 20 +++-
web-console/src/utils/cast-breakdown.ts | 92 +++++++++++++++++++
web-console/src/utils/index.tsx | 1 +
.../edit-column-dialog/edit-column-dialog.scss} | 46 +++++-----
.../edit-column-dialog/edit-column-dialog.tsx | 102 +++++++++++++++++++++
.../explore-view/resource-pane/resource-pane.tsx | 18 +++-
.../column-editor/column-editor.tsx | 85 ++---------------
8 files changed, 266 insertions(+), 109 deletions(-)
diff --git a/web-console/src/modules/components/generic-output-table/generic-output-table.tsx b/web-console/src/modules/components/generic-output-table/generic-output-table.tsx
index b41e3842248..22f9c21eb2f 100644
--- a/web-console/src/modules/components/generic-output-table/generic-output-table.tsx
+++ b/web-console/src/modules/components/generic-output-table/generic-output-table.tsx
@@ -227,16 +227,7 @@ export const GenericOutputTable = React.memo(function GenericOutputTable(
if (column.isTimeColumn()) {
// ToDo: clean
} else if (column.sqlType === 'TIMESTAMP') {
- menuItems.push(
- <MenuItem
- key="declare_time"
- icon={IconNames.TIME}
- text="Use as the primary time column"
- onClick={() => {
- onQueryAction(q => q.changeSelect(headerIndex, selectExpression.as(TIME_COLUMN)));
- }}
- />,
- );
+ // ToDo: clean
} else {
// Not a time column -------------------------------------------
const values = queryResult.rows.map(row => row[headerIndex]);
diff --git a/web-console/src/modules/models/query-source.ts b/web-console/src/modules/models/query-source.ts
index 6baac7cd02c..ef5cc65b7ee 100644
--- a/web-console/src/modules/models/query-source.ts
+++ b/web-console/src/modules/models/query-source.ts
@@ -16,7 +16,7 @@
* limitations under the License.
*/
-import type { SqlQuery } from '@druid-toolkit/query';
+import type { SqlExpression, SqlQuery } from '@druid-toolkit/query';
import { C, SqlStar } from '@druid-toolkit/query';
import type { ExpressionMeta } from '../models';
@@ -60,10 +60,28 @@ export class QuerySource {
this.columns = columns;
}
+ public getSourceExpressionForColumn(outputName: string): SqlExpression {
+ const sourceExpression = this.query
+ .getSelectExpressionsArray()
+ .find(ex => ex.getOutputName() === outputName);
+ if (sourceExpression) return sourceExpression;
+ return C(outputName);
+ }
+
public deleteColumn(outputName: string): SqlQuery {
const noStarQuery = QuerySource.materializeStarIfNeeded(this.query, this.columns);
return noStarQuery.changeSelectExpressions(
noStarQuery.getSelectExpressionsArray().filter(ex => ex.getOutputName() !== outputName),
);
}
+
+ public changeExpression(newExpression: SqlExpression): SqlQuery {
+ const noStarQuery = QuerySource.materializeStarIfNeeded(this.query, this.columns);
+ const outputName = newExpression.getOutputName();
+ return noStarQuery.changeSelectExpressions(
+ noStarQuery
+ .getSelectExpressionsArray()
+ .map(ex => (ex.getOutputName() === outputName ? newExpression : ex)),
+ );
+ }
}
diff --git a/web-console/src/utils/cast-breakdown.ts b/web-console/src/utils/cast-breakdown.ts
new file mode 100644
index 00000000000..5aee18054df
--- /dev/null
+++ b/web-console/src/utils/cast-breakdown.ts
@@ -0,0 +1,92 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import type { SqlType } from '@druid-toolkit/query';
+import { F, SqlExpression, SqlFunction } from '@druid-toolkit/query';
+
+export interface CastBreakdown {
+ formula: string;
+ castType?: SqlType;
+ forceMultiValue: boolean;
+ outputName: string;
+}
+
+export function expressionToCastBreakdown(expression: SqlExpression): CastBreakdown {
+ const outputName = expression.getOutputName() || '';
+ expression = expression.getUnderlyingExpression();
+
+ if (expression instanceof SqlFunction) {
+ const asType = expression.getCastType();
+ const formula = String(expression.getArg(0));
+ if (asType) {
+ return {
+ formula,
+ castType: asType,
+ forceMultiValue: false,
+ outputName,
+ };
+ } else if (expression.getEffectiveFunctionName() === 'ARRAY_TO_MV') {
+ return {
+ formula,
+ forceMultiValue: true,
+ outputName,
+ };
+ }
+ }
+
+ return {
+ formula: String(expression),
+ forceMultiValue: false,
+ outputName,
+ };
+}
+
+export function castBreakdownToExpression({
+ formula,
+ castType,
+ forceMultiValue,
+ outputName,
+}: CastBreakdown): SqlExpression {
+ let newExpression = SqlExpression.parse(formula);
+ const defaultOutputName = newExpression.getOutputName();
+
+ if (castType) {
+ newExpression = newExpression.cast(castType);
+ } else if (forceMultiValue) {
+ newExpression = F('ARRAY_TO_MV', newExpression);
+ }
+
+ if (!defaultOutputName && !outputName) {
+ throw new Error('Must explicitly define an output name');
+ }
+
+ if (newExpression.getOutputName() !== outputName) {
+ newExpression = newExpression.as(outputName);
+ }
+
+ return newExpression;
+}
+
+export function castBreakdownsEqual(a: CastBreakdown, b: CastBreakdown): boolean {
+ return (
+ a.formula === b.formula &&
+ String(a.castType) === String(b.castType) &&
+ a.forceMultiValue === b.forceMultiValue &&
+ a.outputName === b.outputName
+ );
+}
diff --git a/web-console/src/utils/index.tsx b/web-console/src/utils/index.tsx
index 4daeefe61c1..21da7b7faeb 100644
--- a/web-console/src/utils/index.tsx
+++ b/web-console/src/utils/index.tsx
@@ -16,6 +16,7 @@
* limitations under the License.
*/
+export * from './cast-breakdown';
export * from './column-metadata';
export * from './date';
export * from './download';
diff --git a/web-console/src/utils/index.tsx b/web-console/src/views/explore-view/edit-column-dialog/edit-column-dialog.scss
similarity index 53%
copy from web-console/src/utils/index.tsx
copy to web-console/src/views/explore-view/edit-column-dialog/edit-column-dialog.scss
index 4daeefe61c1..744ccf6bf8e 100644
--- a/web-console/src/utils/index.tsx
+++ b/web-console/src/views/explore-view/edit-column-dialog/edit-column-dialog.scss
@@ -16,24 +16,28 @@
* limitations under the License.
*/
-export * from './column-metadata';
-export * from './date';
-export * from './download';
-export * from './download-query-detail-archive';
-export * from './druid-lookup';
-export * from './druid-query';
-export * from './formatter';
-export * from './general';
-export * from './intermediate-query-state';
-export * from './local-storage-backed-visibility';
-export * from './local-storage-keys';
-export * from './null-mode-detection';
-export * from './object-change';
-export * from './query-action';
-export * from './query-manager';
-export * from './query-state';
-export * from './sanitizers';
-export * from './sql';
-export * from './table-helpers';
-export * from './types';
-export * from './values-query';
+@import '../../../variables';
+
+.edit-column-dialog {
+ &.#{$bp-ns}-dialog {
+ width: 80vw;
+ height: 80vh;
+ }
+
+ .#{$bp-ns}-dialog-body {
+ display: flex;
+ gap: 8px;
+
+ .controls {
+ flex: 1;
+ }
+
+ .preview {
+ width: 200px;
+ }
+ }
+
+ .#{$bp-ns}-dialog-footer {
+ margin-top: 0;
+ }
+}
diff --git a/web-console/src/views/explore-view/edit-column-dialog/edit-column-dialog.tsx b/web-console/src/views/explore-view/edit-column-dialog/edit-column-dialog.tsx
new file mode 100644
index 00000000000..b56cdbc1b6b
--- /dev/null
+++ b/web-console/src/views/explore-view/edit-column-dialog/edit-column-dialog.tsx
@@ -0,0 +1,102 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { Button, Classes, Dialog, FormGroup, InputGroup, Intent } from '@blueprintjs/core';
+import type { SqlExpression } from '@druid-toolkit/query';
+import React, { useState } from 'react';
+
+import { AppToaster } from '../../../singletons';
+import { castBreakdownToExpression, expressionToCastBreakdown } from '../../../utils';
+import { FlexibleQueryInput } from '../../workbench-view/flexible-query-input/flexible-query-input';
+
+import './edit-column-dialog.scss';
+
+export interface EditColumnDialogProps {
+ initExpression?: SqlExpression;
+ onApply(expression: SqlExpression): void;
+ onClose(): void;
+}
+
+export const EditColumnDialog = React.memo(function EditColumnDialog(props: EditColumnDialogProps) {
+ const { initExpression, onApply, onClose } = props;
+
+ const [initBreakdown] = useState(
+ initExpression ? expressionToCastBreakdown(initExpression) : undefined,
+ );
+ const [currentBreakdown, setCurrentBreakdown] = useState(
+ initBreakdown || { formula: '', forceMultiValue: false, outputName: '' },
+ );
+
+ return (
+ <Dialog className="edit-column-dialog" isOpen onClose={onClose} title="Edit column">
+ <div className={Classes.DIALOG_BODY}>
+ <div className="controls">
+ <FormGroup label="Name">
+ <InputGroup
+ value={currentBreakdown.outputName}
+ onChange={e => {
+ setCurrentBreakdown({ ...currentBreakdown, outputName: e.target.value });
+ }}
+ />
+ </FormGroup>
+ <FormGroup label="SQL expression">
+ <FlexibleQueryInput
+ showGutter={false}
+ placeholder="expression"
+ queryString={currentBreakdown.formula}
+ onQueryStringChange={formula => {
+ setCurrentBreakdown({ ...currentBreakdown, formula });
+ }}
+ columnMetadata={undefined}
+ />
+ </FormGroup>
+ </div>
+ <div className="preview">
+ <div className="label">Preview</div>
+ <div className="preview-values">Xyz</div>
+ </div>
+ </div>
+ <div className={Classes.DIALOG_FOOTER}>
+ <div className={Classes.DIALOG_FOOTER_ACTIONS}>
+ <div className="edit-column-dialog-buttons">
+ <Button text="Close" onClick={onClose} />
+ <Button
+ text="Save"
+ intent={Intent.PRIMARY}
+ onClick={() => {
+ let newExpression: SqlExpression;
+ try {
+ newExpression = castBreakdownToExpression(currentBreakdown);
+ } catch (e) {
+ AppToaster.show({
+ message: e.message,
+ intent: Intent.DANGER,
+ });
+ return;
+ }
+
+ onApply(newExpression);
+ onClose();
+ }}
+ />
+ </div>
+ </div>
+ </div>
+ </Dialog>
+ );
+});
diff --git a/web-console/src/views/explore-view/resource-pane/resource-pane.tsx b/web-console/src/views/explore-view/resource-pane/resource-pane.tsx
index 5694af50b1d..877b96cb3c5 100644
--- a/web-console/src/views/explore-view/resource-pane/resource-pane.tsx
+++ b/web-console/src/views/explore-view/resource-pane/resource-pane.tsx
@@ -19,13 +19,14 @@
import { Icon, Intent, Menu, MenuDivider, MenuItem } from '@blueprintjs/core';
import { IconNames } from '@blueprintjs/icons';
import { Popover2 } from '@blueprintjs/popover2';
-import type { SqlQuery } from '@druid-toolkit/query';
+import type { SqlExpression, SqlQuery } from '@druid-toolkit/query';
import React, { useState } from 'react';
import { ClearableInput } from '../../../components';
import type { ExpressionMeta, QuerySource } from '../../../modules';
import { caseInsensitiveContains, dataTypeToIcon, filterMap } from '../../../utils';
import { DragHelper } from '../drag-helper';
+import { EditColumnDialog } from '../edit-column-dialog/edit-column-dialog';
import './resource-pane.scss';
@@ -40,7 +41,7 @@ export const ResourcePane = function ResourcePane(props: ResourcePaneProps) {
const { querySource, onQueryChange, onFilter, onShow } = props;
const [columnSearch, setColumnSearch] = useState('');
- // const { query, columns } = querySource;
+ const [editedExpression, setEditedExpression] = useState<SqlExpression | undefined>();
return (
<div className="resource-pane">
@@ -68,6 +69,13 @@ export const ResourcePane = function ResourcePane(props: ResourcePaneProps) {
<MenuItem icon={IconNames.EYE_OPEN} text="Show" onClick={() => onShow(c)} />
)}
<MenuDivider />
+ <MenuItem
+ icon={IconNames.EDIT}
+ text="Edit"
+ onClick={() =>
+ setEditedExpression(querySource.getSourceExpressionForColumn(columnName))
+ }
+ />
<MenuItem
icon={IconNames.TRASH}
text="Delete"
@@ -96,6 +104,12 @@ export const ResourcePane = function ResourcePane(props: ResourcePaneProps) {
);
})}
</div>
+ {editedExpression && (
+ <EditColumnDialog
+ onApply={newExpression => onQueryChange(querySource.changeExpression(newExpression))}
+ onClose={() => setEditedExpression(undefined)}
+ />
+ )}
</div>
);
};
diff --git a/web-console/src/views/sql-data-loader-view/column-editor/column-editor.tsx b/web-console/src/views/sql-data-loader-view/column-editor/column-editor.tsx
index a981ce4140b..074b07dc671 100644
--- a/web-console/src/views/sql-data-loader-view/column-editor/column-editor.tsx
+++ b/web-console/src/views/sql-data-loader-view/column-editor/column-editor.tsx
@@ -28,13 +28,20 @@ import {
} from '@blueprintjs/core';
import { IconNames } from '@blueprintjs/icons';
import { Popover2 } from '@blueprintjs/popover2';
-import type { QueryResult } from '@druid-toolkit/query';
-import { F, SqlExpression, SqlFunction, SqlType } from '@druid-toolkit/query';
+import type { QueryResult, SqlExpression } from '@druid-toolkit/query';
+import { SqlType } from '@druid-toolkit/query';
import type { JSX } from 'react';
import React, { useState } from 'react';
import { AppToaster } from '../../../singletons';
-import { deepDelete, deleteKeys, tickIcon } from '../../../utils';
+import {
+ castBreakdownsEqual,
+ castBreakdownToExpression,
+ deepDelete,
+ deleteKeys,
+ expressionToCastBreakdown,
+ tickIcon,
+} from '../../../utils';
import { FlexibleQueryInput } from '../../workbench-view/flexible-query-input/flexible-query-input';
import './column-editor.scss';
@@ -45,78 +52,6 @@ function getTargetTypes(isArray: boolean): SqlType[] {
: [SqlType.VARCHAR, SqlType.BIGINT, SqlType.DOUBLE];
}
-interface CastBreakdown {
- formula: string;
- castType?: SqlType;
- forceMultiValue: boolean;
- outputName: string;
-}
-
-function expressionToCastBreakdown(expression: SqlExpression): CastBreakdown {
- const outputName = expression.getOutputName() || '';
- expression = expression.getUnderlyingExpression();
-
- if (expression instanceof SqlFunction) {
- const asType = expression.getCastType();
- const formula = String(expression.getArg(0));
- if (asType) {
- return {
- formula,
- castType: asType,
- forceMultiValue: false,
- outputName,
- };
- } else if (expression.getEffectiveFunctionName() === 'ARRAY_TO_MV') {
- return {
- formula,
- forceMultiValue: true,
- outputName,
- };
- }
- }
-
- return {
- formula: String(expression),
- forceMultiValue: false,
- outputName,
- };
-}
-
-function castBreakdownToExpression({
- formula,
- castType,
- forceMultiValue,
- outputName,
-}: CastBreakdown): SqlExpression {
- let newExpression = SqlExpression.parse(formula);
- const defaultOutputName = newExpression.getOutputName();
-
- if (castType) {
- newExpression = newExpression.cast(castType);
- } else if (forceMultiValue) {
- newExpression = F('ARRAY_TO_MV', newExpression);
- }
-
- if (!defaultOutputName && !outputName) {
- throw new Error('Must explicitly define an output name');
- }
-
- if (newExpression.getOutputName() !== outputName) {
- newExpression = newExpression.as(outputName);
- }
-
- return newExpression;
-}
-
-function castBreakdownsEqual(a: CastBreakdown, b: CastBreakdown): boolean {
- return (
- a.formula === b.formula &&
- String(a.castType) === String(b.castType) &&
- a.forceMultiValue === b.forceMultiValue &&
- a.outputName === b.outputName
- );
-}
-
interface ColumnEditorProps {
initExpression?: SqlExpression;
onApply(expression: SqlExpression | undefined): void;
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@druid.apache.org
For additional commands, e-mail: commits-help@druid.apache.org