You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@druid.apache.org by ab...@apache.org on 2023/08/02 05:35:41 UTC
[druid] branch 27.0.0 updated: Web console: fix grouped filtering and add complex menu (#14668) (#14731)
This is an automated email from the ASF dual-hosted git repository.
abhishek pushed a commit to branch 27.0.0
in repository https://gitbox.apache.org/repos/asf/druid.git
The following commit(s) were added to refs/heads/27.0.0 by this push:
new 0f7fa75e57 Web console: fix grouped filtering and add complex menu (#14668) (#14731)
0f7fa75e57 is described below
commit 0f7fa75e578aaa2aa0e37bea9313d7728a576d34
Author: AmatyaAvadhanula <am...@imply.io>
AuthorDate: Wed Aug 2 11:05:34 2023 +0530
Web console: fix grouped filtering and add complex menu (#14668) (#14731)
Backport #14668 to 27.0.0
---
web-console/src/bootstrap/react-table-defaults.tsx | 7 +-
.../segment-timeline/segment-timeline.tsx | 3 +-
web-console/src/react-table/react-table-utils.ts | 2 +-
.../__snapshots__/column-tree.spec.tsx.snap | 24 ++++++-
.../__snapshots__/complex-menu-items.spec.tsx.snap | 74 +++++++++++++++++++
.../complex-menu-items.spec.tsx} | 26 ++++++-
.../complex-menu-items/complex-menu-items.tsx | 83 ++++++++++++++++++++++
.../column-tree/column-tree-menu/index.ts | 1 +
.../number-menu-items/number-menu-items.tsx | 12 +---
.../string-menu-items/string-menu-items.tsx | 14 +---
.../time-menu-items/time-menu-items.tsx | 11 +--
.../workbench-view/column-tree/column-tree.tsx | 29 +++++++-
.../flexible-query-input/flexible-query-input.tsx | 3 +-
13 files changed, 246 insertions(+), 43 deletions(-)
diff --git a/web-console/src/bootstrap/react-table-defaults.tsx b/web-console/src/bootstrap/react-table-defaults.tsx
index 42039b36da..531bc8127b 100644
--- a/web-console/src/bootstrap/react-table-defaults.tsx
+++ b/web-console/src/bootstrap/react-table-defaults.tsx
@@ -40,7 +40,12 @@ export function bootstrapReactTable() {
className: DEFAULT_TABLE_CLASS_NAME,
defaultFilterMethod: (filter: Filter, row: any) => {
const id = filter.pivotId || filter.id;
- return booleanCustomTableFilter(filter, row[id]);
+ const subRows = row._subRows;
+ if (Array.isArray(subRows)) {
+ return subRows.some(r => booleanCustomTableFilter(filter, r[id]));
+ } else {
+ return booleanCustomTableFilter(filter, row[id]);
+ }
},
LoadingComponent: Loader,
loadingText: '',
diff --git a/web-console/src/components/segment-timeline/segment-timeline.tsx b/web-console/src/components/segment-timeline/segment-timeline.tsx
index 150e94f2ab..3fc9e1ad87 100644
--- a/web-console/src/components/segment-timeline/segment-timeline.tsx
+++ b/web-console/src/components/segment-timeline/segment-timeline.tsx
@@ -16,7 +16,6 @@
* limitations under the License.
*/
-import type { IResizeEntry } from '@blueprintjs/core';
import { FormGroup, HTMLSelect, Radio, RadioGroup, ResizeSensor } from '@blueprintjs/core';
import type { AxisScale } from 'd3-axis';
import { scaleLinear, scaleUtc } from 'd3-scale';
@@ -428,7 +427,7 @@ ORDER BY "start" DESC`;
}
};
- private readonly handleResize = (entries: IResizeEntry[]) => {
+ private readonly handleResize = (entries: ResizeObserverEntry[]) => {
const chartRect = entries[0].contentRect;
this.setState({
chartWidth: chartRect.width,
diff --git a/web-console/src/react-table/react-table-utils.ts b/web-console/src/react-table/react-table-utils.ts
index e1ae5b9b15..91248c5520 100644
--- a/web-console/src/react-table/react-table-utils.ts
+++ b/web-console/src/react-table/react-table-utils.ts
@@ -107,7 +107,7 @@ export function addOrUpdateFilter(filters: readonly Filter[], filter: Filter): F
return addOrUpdate(filters, filter, f => f.id);
}
-export function booleanCustomTableFilter(filter: Filter, value: any): boolean {
+export function booleanCustomTableFilter(filter: Filter, value: unknown): boolean {
if (value == null) return false;
const modeAndNeedle = parseFilterModeAndNeedle(filter);
if (!modeAndNeedle) return true;
diff --git a/web-console/src/views/workbench-view/column-tree/__snapshots__/column-tree.spec.tsx.snap b/web-console/src/views/workbench-view/column-tree/__snapshots__/column-tree.spec.tsx.snap
index f1ed721698..93e3c83d04 100644
--- a/web-console/src/views/workbench-view/column-tree/__snapshots__/column-tree.spec.tsx.snap
+++ b/web-console/src/views/workbench-view/column-tree/__snapshots__/column-tree.spec.tsx.snap
@@ -32,7 +32,13 @@ exports[`ColumnTree matches snapshot 1`] = `
Object {
"childNodes": Array [
Object {
- "icon": "time",
+ "icon": <Blueprint4.Icon
+ aria-hidden={true}
+ className="bp4-tree-node-icon"
+ icon="time"
+ tabIndex={-1}
+ title="TIMESTAMP"
+ />,
"id": "__time",
"label": <Blueprint4.Popover2
autoFocus={false}
@@ -65,7 +71,13 @@ exports[`ColumnTree matches snapshot 1`] = `
</Blueprint4.Popover2>,
},
Object {
- "icon": "numerical",
+ "icon": <Blueprint4.Icon
+ aria-hidden={true}
+ className="bp4-tree-node-icon"
+ icon="numerical"
+ tabIndex={-1}
+ title="BIGINT"
+ />,
"id": "added",
"label": <Blueprint4.Popover2
autoFocus={false}
@@ -98,7 +110,13 @@ exports[`ColumnTree matches snapshot 1`] = `
</Blueprint4.Popover2>,
},
Object {
- "icon": "floating-point",
+ "icon": <Blueprint4.Icon
+ aria-hidden={true}
+ className="bp4-tree-node-icon"
+ icon="floating-point"
+ tabIndex={-1}
+ title="FLOAT"
+ />,
"id": "addedBy10",
"label": <Blueprint4.Popover2
autoFocus={false}
diff --git a/web-console/src/views/workbench-view/column-tree/column-tree-menu/complex-menu-items/__snapshots__/complex-menu-items.spec.tsx.snap b/web-console/src/views/workbench-view/column-tree/column-tree-menu/complex-menu-items/__snapshots__/complex-menu-items.spec.tsx.snap
new file mode 100644
index 0000000000..8281c3f085
--- /dev/null
+++ b/web-console/src/views/workbench-view/column-tree/column-tree-menu/complex-menu-items/__snapshots__/complex-menu-items.spec.tsx.snap
@@ -0,0 +1,74 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`ComplexMenuItems matches snapshot when menu is opened for column not inside group by 1`] = `
+<div>
+ <li
+ class="bp4-submenu"
+ role="none"
+ >
+ <span
+ class="bp4-popover-wrapper"
+ >
+ <span
+ aria-haspopup="true"
+ class="bp4-popover-target"
+ role="menuitem"
+ tabindex="0"
+ >
+ <a
+ class="bp4-menu-item"
+ role="none"
+ tabindex="-1"
+ >
+ <span
+ class="bp4-menu-item-icon"
+ >
+ <span
+ aria-hidden="true"
+ class="bp4-icon bp4-icon-function"
+ icon="function"
+ tabindex="-1"
+ >
+ <svg
+ data-icon="function"
+ height="16"
+ role="img"
+ viewBox="0 0 16 16"
+ width="16"
+ >
+ <path
+ d="M8.12 4.74H6.98c.33-1.29.75-2.24 1.28-2.84.33-.37.64-.56.95-.56.06 0 .11.02.15.05.04.04.06.09.06.15 0 .05-.04.15-.13.29-.09.14-.13.28-.13.4 0 .18.07.33.2.46.14.13.31.19.52.19.22 0 .41-.08.56-.23.15-.16.23-.37.23-.63 0-.3-.11-.55-.34-.74C10.1 1.09 9.74 1 9.24 1c-.78 0-1.49.22-2.12.67-.64.45-1.24 1.2-1.81 2.23-.2.36-.38.59-.56.69-.18.1-.46.15-.85.15l-.26.9h1.08l-1.59 6.12c-.27 1.01-.44 1.63-.54 1.86-.14.34-.34.63-.62.87-.11.1-.24.15-.4.15a.15.15 0 01-.11-.04l-.04-.05c0 [...]
+ fill-rule="evenodd"
+ />
+ </svg>
+ </span>
+ </span>
+ <div
+ class="bp4-fill bp4-text-overflow-ellipsis"
+ >
+ Aggregate
+ </div>
+ <span
+ aria-hidden="true"
+ class="bp4-icon bp4-icon-caret-right bp4-submenu-icon"
+ icon="caret-right"
+ >
+ <svg
+ data-icon="caret-right"
+ height="16"
+ role="img"
+ viewBox="0 0 16 16"
+ width="16"
+ >
+ <path
+ d="M11 8c0-.15-.07-.28-.17-.37l-4-3.5A.495.495 0 006 4.5v7a.495.495 0 00.83.37l4-3.5c.1-.09.17-.22.17-.37z"
+ fill-rule="evenodd"
+ />
+ </svg>
+ </span>
+ </a>
+ </span>
+ </span>
+ </li>
+</div>
+`;
diff --git a/web-console/src/views/workbench-view/column-tree/column-tree-menu/index.ts b/web-console/src/views/workbench-view/column-tree/column-tree-menu/complex-menu-items/complex-menu-items.spec.tsx
similarity index 52%
copy from web-console/src/views/workbench-view/column-tree/column-tree-menu/index.ts
copy to web-console/src/views/workbench-view/column-tree/column-tree-menu/complex-menu-items/complex-menu-items.spec.tsx
index 0af13c5eb6..a7d7fdcf2c 100644
--- a/web-console/src/views/workbench-view/column-tree/column-tree-menu/index.ts
+++ b/web-console/src/views/workbench-view/column-tree/column-tree-menu/complex-menu-items/complex-menu-items.spec.tsx
@@ -16,6 +16,26 @@
* limitations under the License.
*/
-export * from './number-menu-items/number-menu-items';
-export * from './string-menu-items/string-menu-items';
-export * from './time-menu-items/time-menu-items';
+import { SqlQuery } from '@druid-toolkit/query';
+import { render } from '@testing-library/react';
+import React from 'react';
+
+import { ComplexMenuItems } from './complex-menu-items';
+
+describe('ComplexMenuItems', () => {
+ it('matches snapshot when menu is opened for column not inside group by', () => {
+ const numberMenu = (
+ <ComplexMenuItems
+ schema="schema"
+ table="table"
+ columnName="user_theta"
+ columnType="COMPLEX<thetaSketch>"
+ parsedQuery={SqlQuery.parse(`SELECT channel, count(*) as cnt FROM wikipedia GROUP BY 1`)}
+ onQueryChange={() => {}}
+ />
+ );
+
+ const { container } = render(numberMenu);
+ expect(container).toMatchSnapshot();
+ });
+});
diff --git a/web-console/src/views/workbench-view/column-tree/column-tree-menu/complex-menu-items/complex-menu-items.tsx b/web-console/src/views/workbench-view/column-tree/column-tree-menu/complex-menu-items/complex-menu-items.tsx
new file mode 100644
index 0000000000..fa172262f0
--- /dev/null
+++ b/web-console/src/views/workbench-view/column-tree/column-tree-menu/complex-menu-items/complex-menu-items.tsx
@@ -0,0 +1,83 @@
+/*
+ * 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 { MenuItem } from '@blueprintjs/core';
+import { IconNames } from '@blueprintjs/icons';
+import type { SqlExpression, SqlQuery } from '@druid-toolkit/query';
+import { C, F } from '@druid-toolkit/query';
+import type { JSX } from 'react';
+import React from 'react';
+
+import { prettyPrintSql } from '../../../../../utils';
+
+const UNIQUE_FUNCTIONS: Record<string, string> = {
+ 'COMPLEX<hyperUnique>': 'APPROX_COUNT_DISTINCT_BUILTIN',
+ 'COMPLEX<thetaSketch>': 'APPROX_COUNT_DISTINCT_DS_THETA',
+ 'COMPLEX<HLLSketch>': 'APPROX_COUNT_DISTINCT_DS_HLL',
+};
+
+const QUANTILE_FUNCTIONS: Record<string, string> = {
+ 'COMPLEX<quantilesDoublesSketch>': 'APPROX_QUANTILE_DS',
+};
+
+export interface ComplexMenuItemsProps {
+ table: string;
+ schema: string;
+ columnName: string;
+ columnType: string;
+ parsedQuery: SqlQuery;
+ onQueryChange: (query: SqlQuery, run?: boolean) => void;
+}
+
+export const ComplexMenuItems = React.memo(function ComplexMenuItems(props: ComplexMenuItemsProps) {
+ const { columnName, columnType, parsedQuery, onQueryChange } = props;
+ const column = C(columnName);
+
+ function renderAggregateMenu(): JSX.Element | undefined {
+ if (!parsedQuery.hasGroupBy()) return;
+
+ function aggregateMenuItem(ex: SqlExpression, alias: string) {
+ return (
+ <MenuItem
+ text={prettyPrintSql(ex)}
+ onClick={() => {
+ onQueryChange(parsedQuery.addSelect(ex.as(alias)), true);
+ }}
+ />
+ );
+ }
+
+ const uniqueFn = UNIQUE_FUNCTIONS[columnType];
+ const quantileFn = QUANTILE_FUNCTIONS[columnType];
+ if (!uniqueFn && !quantileFn) return;
+
+ return (
+ <MenuItem icon={IconNames.FUNCTION} text="Aggregate">
+ {uniqueFn && aggregateMenuItem(F(uniqueFn, column), `unique_${columnName}`)}
+ {quantileFn && (
+ <>
+ {aggregateMenuItem(F(quantileFn, column, 0.5), `median_${columnName}`)}
+ {aggregateMenuItem(F(quantileFn, column, 0.98), `p98_${columnName}`)}
+ </>
+ )}
+ </MenuItem>
+ );
+ }
+
+ return <>{renderAggregateMenu()}</>;
+});
diff --git a/web-console/src/views/workbench-view/column-tree/column-tree-menu/index.ts b/web-console/src/views/workbench-view/column-tree/column-tree-menu/index.ts
index 0af13c5eb6..7fd3dced5c 100644
--- a/web-console/src/views/workbench-view/column-tree/column-tree-menu/index.ts
+++ b/web-console/src/views/workbench-view/column-tree/column-tree-menu/index.ts
@@ -16,6 +16,7 @@
* limitations under the License.
*/
+export * from './complex-menu-items/complex-menu-items';
export * from './number-menu-items/number-menu-items';
export * from './string-menu-items/string-menu-items';
export * from './time-menu-items/time-menu-items';
diff --git a/web-console/src/views/workbench-view/column-tree/column-tree-menu/number-menu-items/number-menu-items.tsx b/web-console/src/views/workbench-view/column-tree/column-tree-menu/number-menu-items/number-menu-items.tsx
index 9c1242dda5..79fbedd54d 100644
--- a/web-console/src/views/workbench-view/column-tree/column-tree-menu/number-menu-items/number-menu-items.tsx
+++ b/web-console/src/views/workbench-view/column-tree/column-tree-menu/number-menu-items/number-menu-items.tsx
@@ -36,9 +36,10 @@ export interface NumberMenuItemsProps {
}
export const NumberMenuItems = React.memo(function NumberMenuItems(props: NumberMenuItemsProps) {
- function renderFilterMenu(): JSX.Element {
- const { columnName, parsedQuery, onQueryChange } = props;
+ const { columnName, parsedQuery, onQueryChange } = props;
+ const column = C(columnName);
+ function renderFilterMenu(): JSX.Element {
function filterMenuItem(clause: SqlExpression) {
return (
<MenuItem
@@ -50,7 +51,6 @@ export const NumberMenuItems = React.memo(function NumberMenuItems(props: Number
);
}
- const column = C(columnName);
return (
<MenuItem icon={IconNames.FILTER} text="Filter">
{filterMenuItem(column.greaterThan(NINE_THOUSAND))}
@@ -60,7 +60,6 @@ export const NumberMenuItems = React.memo(function NumberMenuItems(props: Number
}
function renderRemoveFilter(): JSX.Element | undefined {
- const { columnName, parsedQuery, onQueryChange } = props;
if (!parsedQuery.getEffectiveWhereExpression().containsColumnName(columnName)) return;
return (
@@ -75,7 +74,6 @@ export const NumberMenuItems = React.memo(function NumberMenuItems(props: Number
}
function renderGroupByMenu(): JSX.Element | undefined {
- const { columnName, parsedQuery, onQueryChange } = props;
if (!parsedQuery.hasGroupBy()) return;
function groupByMenuItem(ex: SqlExpression, alias?: string) {
@@ -95,7 +93,6 @@ export const NumberMenuItems = React.memo(function NumberMenuItems(props: Number
);
}
- const column = C(columnName);
return (
<MenuItem icon={IconNames.GROUP_OBJECTS} text="Group by">
{groupByMenuItem(column)}
@@ -105,7 +102,6 @@ export const NumberMenuItems = React.memo(function NumberMenuItems(props: Number
}
function renderRemoveGroupBy(): JSX.Element | undefined {
- const { columnName, parsedQuery, onQueryChange } = props;
const groupedSelectIndexes = parsedQuery.getGroupedSelectIndexesForColumn(columnName);
if (!groupedSelectIndexes.length) return;
@@ -121,7 +117,6 @@ export const NumberMenuItems = React.memo(function NumberMenuItems(props: Number
}
function renderAggregateMenu(): JSX.Element | undefined {
- const { columnName, parsedQuery, onQueryChange } = props;
if (!parsedQuery.hasGroupBy()) return;
function aggregateMenuItem(ex: SqlExpression, alias: string) {
@@ -135,7 +130,6 @@ export const NumberMenuItems = React.memo(function NumberMenuItems(props: Number
);
}
- const column = C(columnName);
return (
<MenuItem icon={IconNames.FUNCTION} text="Aggregate">
{aggregateMenuItem(F('SUM', column), `sum_${columnName}`)}
diff --git a/web-console/src/views/workbench-view/column-tree/column-tree-menu/string-menu-items/string-menu-items.tsx b/web-console/src/views/workbench-view/column-tree/column-tree-menu/string-menu-items/string-menu-items.tsx
index 5760010577..db566765d3 100644
--- a/web-console/src/views/workbench-view/column-tree/column-tree-menu/string-menu-items/string-menu-items.tsx
+++ b/web-console/src/views/workbench-view/column-tree/column-tree-menu/string-menu-items/string-menu-items.tsx
@@ -35,9 +35,10 @@ export interface StringMenuItemsProps {
}
export const StringMenuItems = React.memo(function StringMenuItems(props: StringMenuItemsProps) {
- function renderFilterMenu(): JSX.Element | undefined {
- const { columnName, parsedQuery, onQueryChange } = props;
+ const { schema, table, columnName, parsedQuery, onQueryChange } = props;
+ const column = C(columnName);
+ function renderFilterMenu(): JSX.Element | undefined {
function filterMenuItem(clause: SqlExpression, run = true) {
return (
<MenuItem
@@ -49,7 +50,6 @@ export const StringMenuItems = React.memo(function StringMenuItems(props: String
);
}
- const column = C(columnName);
return (
<MenuItem icon={IconNames.FILTER} text="Filter">
{filterMenuItem(column.isNotNull())}
@@ -61,7 +61,6 @@ export const StringMenuItems = React.memo(function StringMenuItems(props: String
}
function renderRemoveFilter(): JSX.Element | undefined {
- const { columnName, parsedQuery, onQueryChange } = props;
if (!parsedQuery.getEffectiveWhereExpression().containsColumnName(columnName)) return;
return (
@@ -76,7 +75,6 @@ export const StringMenuItems = React.memo(function StringMenuItems(props: String
}
function renderRemoveGroupBy(): JSX.Element | undefined {
- const { columnName, parsedQuery, onQueryChange } = props;
const groupedSelectIndexes = parsedQuery.getGroupedSelectIndexesForColumn(columnName);
if (!groupedSelectIndexes.length) return;
@@ -92,7 +90,6 @@ export const StringMenuItems = React.memo(function StringMenuItems(props: String
}
function renderGroupByMenu(): JSX.Element | undefined {
- const { columnName, parsedQuery, onQueryChange } = props;
if (!parsedQuery.hasGroupBy()) return;
function groupByMenuItem(ex: SqlExpression, alias?: string) {
@@ -112,7 +109,6 @@ export const StringMenuItems = React.memo(function StringMenuItems(props: String
);
}
- const column = C(columnName);
return (
<MenuItem icon={IconNames.GROUP_OBJECTS} text="Group by">
{groupByMenuItem(column)}
@@ -123,7 +119,6 @@ export const StringMenuItems = React.memo(function StringMenuItems(props: String
}
function renderAggregateMenu(): JSX.Element | undefined {
- const { columnName, parsedQuery, onQueryChange } = props;
if (!parsedQuery.hasGroupBy()) return;
function aggregateMenuItem(ex: SqlExpression, alias: string, run = true) {
@@ -137,7 +132,6 @@ export const StringMenuItems = React.memo(function StringMenuItems(props: String
);
}
- const column = C(columnName);
return (
<MenuItem icon={IconNames.FUNCTION} text="Aggregate">
{aggregateMenuItem(F.countDistinct(column), `dist_${columnName}`)}
@@ -152,7 +146,6 @@ export const StringMenuItems = React.memo(function StringMenuItems(props: String
}
function renderJoinMenu(): JSX.Element | undefined {
- const { schema, table, columnName, parsedQuery, onQueryChange } = props;
if (schema !== 'lookup' || !parsedQuery) return;
const firstTableName = parsedQuery.getFirstTableName();
if (!firstTableName) return;
@@ -212,7 +205,6 @@ export const StringMenuItems = React.memo(function StringMenuItems(props: String
}
function renderRemoveJoin(): JSX.Element | undefined {
- const { schema, parsedQuery, onQueryChange } = props;
if (schema !== 'lookup' || !parsedQuery) return;
if (!parsedQuery.hasJoin()) return;
diff --git a/web-console/src/views/workbench-view/column-tree/column-tree-menu/time-menu-items/time-menu-items.tsx b/web-console/src/views/workbench-view/column-tree/column-tree-menu/time-menu-items/time-menu-items.tsx
index 706c1cab50..677667ddc8 100644
--- a/web-console/src/views/workbench-view/column-tree/column-tree-menu/time-menu-items/time-menu-items.tsx
+++ b/web-console/src/views/workbench-view/column-tree/column-tree-menu/time-menu-items/time-menu-items.tsx
@@ -113,9 +113,10 @@ export interface TimeMenuItemsProps {
}
export const TimeMenuItems = React.memo(function TimeMenuItems(props: TimeMenuItemsProps) {
- function renderFilterMenu(): JSX.Element | undefined {
- const { columnName, parsedQuery, onQueryChange } = props;
+ const { columnName, parsedQuery, onQueryChange } = props;
+ const column = C(columnName);
+ function renderFilterMenu(): JSX.Element | undefined {
function filterMenuItem(label: string, clause: SqlExpression) {
return (
<MenuItem
@@ -161,7 +162,6 @@ export const TimeMenuItems = React.memo(function TimeMenuItems(props: TimeMenuIt
}
function renderRemoveFilter(): JSX.Element | undefined {
- const { columnName, parsedQuery, onQueryChange } = props;
if (!parsedQuery.getEffectiveWhereExpression().containsColumnName(columnName)) return;
return (
@@ -176,7 +176,6 @@ export const TimeMenuItems = React.memo(function TimeMenuItems(props: TimeMenuIt
}
function renderRemoveGroupBy(): JSX.Element | undefined {
- const { columnName, parsedQuery, onQueryChange } = props;
const groupedSelectIndexes = parsedQuery.getGroupedSelectIndexesForColumn(columnName);
if (!groupedSelectIndexes.length) return;
@@ -192,7 +191,6 @@ export const TimeMenuItems = React.memo(function TimeMenuItems(props: TimeMenuIt
}
function renderGroupByMenu(): JSX.Element | undefined {
- const { columnName, parsedQuery, onQueryChange } = props;
if (!parsedQuery.hasGroupBy()) return;
function groupByMenuItem(ex: SqlExpression, alias: string) {
@@ -212,7 +210,6 @@ export const TimeMenuItems = React.memo(function TimeMenuItems(props: TimeMenuIt
);
}
- const column = C(columnName);
return (
<MenuItem icon={IconNames.GROUP_OBJECTS} text="Group by">
{groupByMenuItem(F.timeFloor(column, 'PT1H'), `${columnName}_by_hour`)}
@@ -229,7 +226,6 @@ export const TimeMenuItems = React.memo(function TimeMenuItems(props: TimeMenuIt
}
function renderAggregateMenu(): JSX.Element | undefined {
- const { columnName, parsedQuery, onQueryChange } = props;
if (!parsedQuery.hasGroupBy()) return;
function aggregateMenuItem(ex: SqlExpression, alias: string) {
@@ -243,7 +239,6 @@ export const TimeMenuItems = React.memo(function TimeMenuItems(props: TimeMenuIt
);
}
- const column = C(columnName);
return (
<MenuItem icon={IconNames.FUNCTION} text="Aggregate">
{aggregateMenuItem(F.max(column), `max_${columnName}`)}
diff --git a/web-console/src/views/workbench-view/column-tree/column-tree.tsx b/web-console/src/views/workbench-view/column-tree/column-tree.tsx
index e19c2417a4..eee22449c9 100644
--- a/web-console/src/views/workbench-view/column-tree/column-tree.tsx
+++ b/web-console/src/views/workbench-view/column-tree/column-tree.tsx
@@ -17,7 +17,7 @@
*/
import type { TreeNodeInfo } from '@blueprintjs/core';
-import { HTMLSelect, Menu, MenuItem, Position, Tree } from '@blueprintjs/core';
+import { Classes, HTMLSelect, Icon, Menu, MenuItem, Position, Tree } from '@blueprintjs/core';
import { IconNames } from '@blueprintjs/icons';
import { Popover2 } from '@blueprintjs/popover2';
import type { SqlExpression } from '@druid-toolkit/query';
@@ -39,7 +39,12 @@ import { Deferred, Loader } from '../../../components';
import type { ColumnMetadata } from '../../../utils';
import { copyAndAlert, dataTypeToIcon, groupBy, oneOf, prettyPrintSql } from '../../../utils';
-import { NumberMenuItems, StringMenuItems, TimeMenuItems } from './column-tree-menu';
+import {
+ ComplexMenuItems,
+ NumberMenuItems,
+ StringMenuItems,
+ TimeMenuItems,
+} from './column-tree-menu';
import './column-tree.scss';
@@ -400,7 +405,15 @@ export class ColumnTree extends React.PureComponent<ColumnTreeProps, ColumnTreeS
childNodes: metadata.map(
(columnData): TreeNodeInfo => ({
id: columnData.COLUMN_NAME,
- icon: dataTypeToIcon(columnData.DATA_TYPE),
+ icon: (
+ <Icon
+ className={Classes.TREE_NODE_ICON}
+ icon={dataTypeToIcon(columnData.DATA_TYPE)}
+ aria-hidden
+ tabIndex={-1}
+ title={columnData.DATA_TYPE}
+ />
+ ),
label: (
<Popover2
position={Position.RIGHT}
@@ -454,6 +467,16 @@ export class ColumnTree extends React.PureComponent<ColumnTreeProps, ColumnTreeS
onQueryChange={onQueryChange}
/>
)}
+ {parsedQuery && columnData.DATA_TYPE.startsWith('COMPLEX<') && (
+ <ComplexMenuItems
+ table={tableName}
+ schema={schemaName}
+ columnName={columnData.COLUMN_NAME}
+ columnType={columnData.DATA_TYPE}
+ parsedQuery={parsedQuery}
+ onQueryChange={onQueryChange}
+ />
+ )}
<MenuItem
icon={IconNames.CLIPBOARD}
text={`Copy: ${columnData.COLUMN_NAME}`}
diff --git a/web-console/src/views/workbench-view/flexible-query-input/flexible-query-input.tsx b/web-console/src/views/workbench-view/flexible-query-input/flexible-query-input.tsx
index 62ca7e60fb..5f1141aaf2 100644
--- a/web-console/src/views/workbench-view/flexible-query-input/flexible-query-input.tsx
+++ b/web-console/src/views/workbench-view/flexible-query-input/flexible-query-input.tsx
@@ -16,7 +16,6 @@
* limitations under the License.
*/
-import type { ResizeEntry } from '@blueprintjs/core';
import { ResizeSensor2 } from '@blueprintjs/popover2';
import { C, T } from '@druid-toolkit/query';
import type { Ace } from 'ace-builds';
@@ -270,7 +269,7 @@ export class FlexibleQueryInput extends React.PureComponent<
}
}
- private readonly handleAceContainerResize = (entries: ResizeEntry[]) => {
+ private readonly handleAceContainerResize = (entries: ResizeObserverEntry[]) => {
if (entries.length !== 1) return;
this.setState({ editorHeight: entries[0].contentRect.height });
};
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@druid.apache.org
For additional commands, e-mail: commits-help@druid.apache.org