mirror of https://github.com/apache/druid.git
Web console: fix grouped filtering and add complex menu (#14668)
* fix filtering when grouped * add complex menu * complex aggs * use ResizeObserverEntry * add quantile and test * fix style * update snapshots
This commit is contained in:
parent
2e456d25ae
commit
153948198c
|
@ -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: '',
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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-.03.04-.08.12-.16.08-.08.12-.2.12-.36 0-.18-.06-.33-.19-.44-.12-.12-.3-.18-.54-.18-.28 0-.51.08-.68.23-.16.14-.25.32-.25.53 0 .22.1.42.31.59.21.17.53.25.97.25.7 0 1.32-.18 1.87-.54.54-.36 1.02-.92 1.42-1.67.41-.75.82-1.96 1.25-3.63l.91-3.54h1.1l.29-.89zm5.43 1.52c.2-.15.41-.23.62-.23.08 0 .23.03.45.09s.41.09.57.09c.23 0 .42-.08.57-.23.16-.16.24-.36.24-.61 0-.26-.08-.47-.23-.62-.15-.15-.37-.23-.66-.23-.25 0-.5.06-.72.18-.23.12-.51.38-.86.78-.26.3-.64.81-1.15 1.55-.2-.91-.55-1.75-1.05-2.51l-2.72.46-.06.29c.2-.04.37-.06.51-.06.27 0 .49.11.67.34.28.36.67 1.45 1.17 3.26-.39.52-.66.85-.8 1.01-.24.26-.44.42-.59.5-.12.06-.25.09-.41.09-.11 0-.3-.06-.56-.18-.18-.08-.34-.12-.48-.12-.27 0-.48.08-.66.25-.17.17-.26.38-.26.64 0 .25.08.44.24.6.16.15.37.23.64.23.26 0 .5-.05.73-.16.23-.11.52-.34.86-.69.35-.35.82-.9 1.43-1.67.23.73.44 1.25.61 1.58s.37.57.59.71c.22.15.5.22.83.22.32 0 .65-.11.98-.34.44-.3.88-.81 1.34-1.53l-.26-.15c-.31.43-.54.7-.69.8-.1.07-.22.1-.35.1-.16 0-.32-.1-.48-.3-.27-.34-.62-1.27-1.06-2.8.4-.68.73-1.13 1-1.34z"
|
||||
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>
|
||||
`;
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* 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 { 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();
|
||||
});
|
||||
});
|
|
@ -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()}</>;
|
||||
});
|
|
@ -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';
|
||||
|
|
|
@ -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}`)}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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}`)}
|
||||
|
|
|
@ -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}`}
|
||||
|
|
|
@ -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 });
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue