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 <[email protected]>
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: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to