Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Backport 2.11] UI-bug fixes, added create query for MV #188

Merged
merged 1 commit into from
Oct 26, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
UI-bug fixes, added create query for MV (#182)
* fixed MV load bug, added mv create button, handled error case when post query async fails

Signed-off-by: sumukhswamy <sumukhhs@amazon.com>

* addressed pr commits

Signed-off-by: sumukhswamy <sumukhhs@amazon.com>

* changed the materialiazed view check

Signed-off-by: sumukhswamy <sumukhhs@amazon.com>

* changed badge to notif badge for mv

Signed-off-by: sumukhswamy <sumukhhs@amazon.com>

* type check for mv

Signed-off-by: sumukhswamy <sumukhhs@amazon.com>

* added label for mv, changed mv condition

Signed-off-by: sumukhswamy <sumukhhs@amazon.com>

---------

Signed-off-by: sumukhswamy <sumukhhs@amazon.com>
(cherry picked from commit be457f6)
Signed-off-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
github-actions[bot] committed Oct 26, 2023
commit 00cc60fd75c66e4d0172a3bf237f178151362714
25 changes: 19 additions & 6 deletions common/constants/index.ts
Original file line number Diff line number Diff line change
@@ -18,18 +18,20 @@ export const TREE_ITEM_TABLE_NAME_DEFAULT_NAME = `table`;
export const TREE_ITEM_LOAD_MATERIALIZED_BADGE_NAME = `Load Materialized View`;
export const TREE_ITEM_BADGE_NAME = `badge`;
export const LOAD_OPENSEARCH_INDICES_QUERY = `SHOW tables LIKE '%';`;
export const SKIPPING_INDEX_QUERY = `CREATE SKIPPING INDEX ON myS3.logs_db.http_logs
export const SKIPPING_INDEX_QUERY = `CREATE SKIPPING INDEX ON datasource.database.table
(status VALUE_SET)
WITH (
auto_refresh = true
auto_refresh = true,
checkpoint_location = 's3://test/'
)`;
export const COVERING_INDEX_QUERY = `CREATE INDEX covering_idx ON myS3.logs_db.http_logs
export const COVERING_INDEX_QUERY = `CREATE INDEX covering_idx ON datasource.database.table
(status)
WITH (
auto_refresh = true
auto_refresh = true,
checkpoint_location = 's3://test/'
)`;
export const CREATE_DATABASE_QUERY = `CREATE DATABASE myS3.logs_db`;
export const CREATE_TABLE_QUERY = `CREATE EXTERNAL TABLE myS3.logs_db.logs (
export const CREATE_DATABASE_QUERY = `CREATE DATABASE datasource.database`;
export const CREATE_TABLE_QUERY = `CREATE EXTERNAL TABLE datasource.database.table (
key BIGINT,
status INTEGER,
size FLOAT,
@@ -42,6 +44,17 @@ OPTIONS (
compression 'gzip'
);`;

export const CREATE_MATERIALIZED_VIEW = `CREATE MATERIALIZED VIEW datasource.database.materialized_view
AS SELECT
count(field)
FROM datasource.database.table
GROUP BY TUMBLE (timestamp, '2 hours')
WITH (
auto_refresh = true,
watermark_delay = '2 minutes',
checkpoint_location = 's3://test/'
)`;

export const ACCELERATION_INDEX_TYPES = [
{ label: 'Skipping Index', value: 'skipping' },
{ label: 'Covering Index', value: 'covering' },
3 changes: 3 additions & 0 deletions common/utils/async_query_helpers.ts
Original file line number Diff line number Diff line change
@@ -30,6 +30,9 @@ export const getJobId = (query: {}, http: CoreStart['http'], callback) => {
.then((res) => {
const id = res.data.resp.queryId;
setAsyncSessionId(_.get(res.data.resp, 'sessionId', null));
if (id === undefined) {
console.error(JSON.parse(res.data.body));
}
callback(id);
})
.catch((err) => {
19 changes: 18 additions & 1 deletion public/components/SQLPage/CreateButton.tsx
Original file line number Diff line number Diff line change
@@ -8,8 +8,9 @@ import React, { useState } from 'react';
import {
COVERING_INDEX_QUERY,
CREATE_DATABASE_QUERY,
CREATE_MATERIALIZED_VIEW,
CREATE_TABLE_QUERY,
SKIPPING_INDEX_QUERY,
SKIPPING_INDEX_QUERY
} from '../../../common/constants';

interface CreateButtonProps {
@@ -57,6 +58,13 @@ export const CreateButton = ({ updateSQLQueries, selectedDatasource }: CreateBut
},
];

const materializedViewItems = [
{
name: 'Materialized View',
onClick: () => handleSubMenuClick(CREATE_MATERIALIZED_VIEW),
},
];

const button = (
<EuiButton iconType="arrowDown" iconSide="right" onClick={() => togglePopover(null)}>
Create
@@ -88,6 +96,10 @@ export const CreateButton = ({ updateSQLQueries, selectedDatasource }: CreateBut
name: 'Acceleration Index',
panel: 2,
},
{
name: 'Materialized View',
panel: 3,
}
],
},
{
@@ -100,6 +112,11 @@ export const CreateButton = ({ updateSQLQueries, selectedDatasource }: CreateBut
title: 'Acceleration Index Options',
items: acceleratedIndexItems,
},
{
id: 3,
title: 'Create Materialized View',
items: materializedViewItems,
},
]}
/>
</EuiPopover>
356 changes: 246 additions & 110 deletions public/components/SQLPage/table_view.tsx
Original file line number Diff line number Diff line change
@@ -11,10 +11,11 @@ import {
EuiFlexItem,
EuiIcon,
EuiLoadingSpinner,
EuiNotificationBadge,
EuiSpacer,
EuiText,
EuiToolTip,
EuiTreeView,
EuiTreeView
} from '@elastic/eui';
import { AccelerationIndexType, DatasourceTreeLoading, TreeItem, TreeItemType } from 'common/types';
import _ from 'lodash';
@@ -122,7 +123,7 @@ export const TableView = ({ http, selectedItems, updateSQLQueries, refreshTree }
flag: false,
status: 'Error fetching data',
});
setToast(`ERROR: fetching data`, 'danger');
setToast(`ERROR fetching data`, 'danger');
}
})
.catch((err) => {
@@ -131,7 +132,7 @@ export const TableView = ({ http, selectedItems, updateSQLQueries, refreshTree }
flag: false,
status: err,
});
setToast(`ERROR: ${err}`,'danger');
setToast(`ERROR ${err}`, 'danger');
});
} else {
setTableNames([]);
@@ -141,6 +142,14 @@ export const TableView = ({ http, selectedItems, updateSQLQueries, refreshTree }
datasource: selectedItems[0]['label'],
};
getJobId(query, http, (id) => {
if (id === undefined) {
const errorMessage = 'ERROR fetching databases';
setIsLoading({
flag: false,
status: errorMessage,
});
setToast(errorMessage, 'danger');
}
pollQueryStatus(id, http, (data) => {
setIsLoading({ flag: true, status: data.status });
if (data.status === 'SUCCESS') {
@@ -152,7 +161,7 @@ export const TableView = ({ http, selectedItems, updateSQLQueries, refreshTree }
flag: false,
status: data.error,
});
setToast(`ERROR: ${data.error}`,'danger');
setToast(`ERROR ${data.error}`, 'danger');
}
});
});
@@ -183,31 +192,48 @@ export const TableView = ({ http, selectedItems, updateSQLQueries, refreshTree }
datasource: selectedItems[0]['label'],
};
getJobId(query, http, (id) => {
pollQueryStatus(id, http, (data) => {
if (data.status === 'SUCCESS') {
const fetchTables = data.results.map((subArray) => subArray[1]);
let values = loadTreeItem(fetchTables, TREE_ITEM_TABLE_NAME_DEFAULT_NAME);
let mvObj = loadTreeItem(
[TREE_ITEM_LOAD_MATERIALIZED_BADGE_NAME],
TREE_ITEM_LOAD_MATERIALIZED_BADGE_NAME
);
values = [...values, ...mvObj];
setTreeData((prevTreeData) => {
return prevTreeData.map((database) => {
if (database.name === databaseName) {
return { ...database, values: values, isExpanded: true, isLoading: false };
}
return database;
});
});
} else if (data.status === 'FAILED') {
setIsLoading({
flag: false,
status: data.error,
if (id === undefined) {
const errorMessage = 'ERROR fetching Tables';
setIsLoading({
flag: false,
status: errorMessage,
});
setTreeData((prevTreeData) => {
return prevTreeData.map((database) => {
if (database.name === databaseName) {
return { ...database, isExpanded: true, isLoading: false };
}
return database;
});
setToast(`ERROR: ${data.error}`,'danger');
}
});
});
setToast(errorMessage, 'danger');
} else {
pollQueryStatus(id, http, (data) => {
if (data.status === 'SUCCESS') {
const fetchTables = data.results.map((subArray) => subArray[1]);
let values = loadTreeItem(fetchTables, TREE_ITEM_TABLE_NAME_DEFAULT_NAME);
let mvObj = loadTreeItem(
[TREE_ITEM_LOAD_MATERIALIZED_BADGE_NAME],
TREE_ITEM_LOAD_MATERIALIZED_BADGE_NAME
);
values = [...values, ...mvObj];
setTreeData((prevTreeData) => {
return prevTreeData.map((database) => {
if (database.name === databaseName) {
return { ...database, values: values, isExpanded: true, isLoading: false };
}
return database;
});
});
} else if (data.status === 'FAILED') {
setIsLoading({
flag: false,
status: data.error,
});
setToast(`ERROR ${data.error}`, 'danger');
}
});
}
});
};

@@ -218,6 +244,34 @@ export const TableView = ({ http, selectedItems, updateSQLQueries, refreshTree }
datasource: selectedItems[0]['label'],
};
getJobId(coverQuery, http, (id) => {
if (id === undefined) {
const errorMessage = 'ERROR fetching Covering Index';
setIsLoading({
flag: false,
status: errorMessage,
});
setTreeData((prevTreeData) => {
return prevTreeData.map((database) => {
if (database.name === selectedDatabase) {
return {
...database,
values: database.values?.map((table) => {
if (table.name === tableName) {
return {
...table,
isLoading: false,
isExpanded: false,
};
}
return table;
}),
};
}
return database;
});
});
setToast(errorMessage, 'danger');
}
pollQueryStatus(id, http, (data) => {
if (data.status === 'SUCCESS') {
const res = [].concat(data.results);
@@ -254,14 +308,15 @@ export const TableView = ({ http, selectedItems, updateSQLQueries, refreshTree }
flag: false,
status: data.error,
});
setToast(`ERROR: ${data.error}`, 'danger');
setToast(`ERROR ${data.error}`, 'danger');
}
});
});
};

const handleButtonClick = (e: MouseEvent, tableName: string) => {
e.stopPropagation();
tableName = TREE_ITEM_LOAD_MATERIALIZED_BADGE_NAME;
setSelectedTable(tableName);
setTreeData((prevTreeData) => {
return prevTreeData.map((database) => {
@@ -288,44 +343,75 @@ export const TableView = ({ http, selectedItems, updateSQLQueries, refreshTree }
datasource: selectedItems[0]['label'],
};
getJobId(materializedViewQuery, http, (id) => {
pollQueryStatus(id, http, (data) => {
if (data.status === 'SUCCESS') {
const fetchMaterialzedView = data.results.map((subArray) => subArray[1]);
let values = loadTreeItem(fetchMaterialzedView, TREE_ITEM_MATERIALIZED_VIEW_DEFAULT_NAME);
if (values.length === 0) {
values = [
{
name: 'No Materialized View',
type: TREE_ITEM_BADGE_NAME,
isExpanded: false,
isLoading: false,
},
];
}
setTreeData((prevTreeData) => {
return prevTreeData.map((database) => {
if (database.name === selectedDatabase) {
const updatedValues = database.values?.filter(
(item) => item.type !== TREE_ITEM_LOAD_MATERIALIZED_BADGE_NAME
);
return {
...database,
values: updatedValues?.concat(...values),
if (id === undefined) {
const errorMessage = 'ERROR fetching Materialized View';
setIsLoading({
flag: false,
status: errorMessage,
});
setTreeData((prevTreeData) => {
return prevTreeData.map((database) => {
if (database.name === selectedDatabase) {
return {
...database,
values: database.values?.map((table) => {
if (table.name === tableName) {
return {
...table,
isLoading: false,
};
}
return table;
}),
};
}
return database;
});
});
setToast(errorMessage, 'danger');
} else {
pollQueryStatus(id, http, (data) => {
if (data.status === 'SUCCESS') {
const fetchMaterialzedView = data.results;
let values = loadTreeItem(
fetchMaterialzedView,
TREE_ITEM_MATERIALIZED_VIEW_DEFAULT_NAME
);
if (values.length === 0) {
values = [
{
name: 'No Materialized View',
type: TREE_ITEM_BADGE_NAME,
isExpanded: false,
isLoading: false,
isExpanded: true,
};
}
return database;
},
];
}
setTreeData((prevTreeData) => {
return prevTreeData.map((database) => {
if (database.name === selectedDatabase) {
const updatedValues = database.values?.filter(
(item) => item.type !== TREE_ITEM_LOAD_MATERIALIZED_BADGE_NAME
);
return {
...database,
values: updatedValues?.concat(...values),
isLoading: false,
isExpanded: true,
};
}
return database;
});
});
});
} else if (data.status === 'FAILED') {
setIsLoading({
flag: false,
status: data.error,
});
setToast(`ERROR: ${data.error}`,'danger');
}
});
} else if (data.status === 'FAILED') {
setIsLoading({
flag: false,
status: data.error,
});
setToast(`ERROR ${data.error}`, 'danger');
}
});
}
});
};

@@ -356,44 +442,92 @@ export const TableView = ({ http, selectedItems, updateSQLQueries, refreshTree }
datasource: selectedItems[0]['label'],
};
getJobId(skipQuery, http, (id) => {
pollQueryStatus(id, http, (data) => {
if (data.status === 'SUCCESS') {
if (data.resp.values.length > 0) {
setTreeData((prevTreeData) => {
return prevTreeData.map((database) => {
if (database.name === selectedDatabase) {
return {
...database,
values: database.values?.map((table) => {
if (table.name === tableName) {
return {
...table,
values: loadTreeItem(
[TREE_ITEM_SKIPPING_INDEX_DEFAULT_NAME],
TREE_ITEM_SKIPPING_INDEX_DEFAULT_NAME
),
};
}
return table;
}),
};
}
return database;
if (id === undefined) {
const errorMessage = 'ERROR fetching Skipping index';
setIsLoading({
flag: false,
status: 'error',
});
setTreeData((prevTreeData) => {
return prevTreeData.map((database) => {
if (database.name === selectedDatabase) {
return {
...database,
values: database.values?.map((table) => {
if (table.name === tableName) {
return {
...table,
isLoading: false,
};
}
return table;
}),
};
}
return database;
});
});
setToast(errorMessage, 'danger');
} else {
pollQueryStatus(id, http, (data) => {
if (data.status === 'SUCCESS') {
if (data.results.length > 0) {
setTreeData((prevTreeData) => {
return prevTreeData.map((database) => {
if (database.name === selectedDatabase) {
return {
...database,
values: database.values?.map((table) => {
if (table.name === tableName) {
return {
...table,
values: loadTreeItem(
[TREE_ITEM_SKIPPING_INDEX_DEFAULT_NAME],
TREE_ITEM_SKIPPING_INDEX_DEFAULT_NAME
),
};
}
return table;
}),
};
}
return database;
});
});
}
loadCoveringIndex(tableName);
} else if (data.status === 'FAILED') {
setIsLoading({
flag: false,
status: data.error,
});
setToast(`ERROR ${data.error}`, 'danger');
}
loadCoveringIndex(tableName);
} else if (data.status === 'FAILED') {
setIsLoading({
flag: false,
status: data.error,
});
setToast(`ERROR: ${data.error}`,'danger');
}
});
});
}
});
};

const iconCreation = (node: TreeItem) => {
if (node.type === TREE_ITEM_MATERIALIZED_VIEW_DEFAULT_NAME) {
return <EuiNotificationBadge aria-label='Materialized view' color = 'subdued'>MV</EuiNotificationBadge>;
} else if (
node.type === TREE_ITEM_BADGE_NAME ||
node.type === TREE_ITEM_LOAD_MATERIALIZED_BADGE_NAME
) {
return null;
} else if (node.type === TREE_ITEM_TABLE_NAME_DEFAULT_NAME) {
return <EuiIcon type="tableDensityCompact" size="s" />;
} else if (node.type === TREE_ITEM_DATABASE_NAME_DEFAULT_NAME) {
return <EuiIcon type="database" size="m" />;
} else if (
node.type === TREE_ITEM_COVERING_INDEX_DEFAULT_NAME ||
TREE_ITEM_SKIPPING_INDEX_DEFAULT_NAME
) {
return <EuiIcon type="bolt" size="m" />;
}
};

const createLabel = (node: TreeItem, parentName: string, index: number) => {
switch (node.type) {
case TREE_ITEM_BADGE_NAME:
@@ -408,10 +542,16 @@ export const TableView = ({ http, selectedItems, updateSQLQueries, refreshTree }
case TREE_ITEM_LOAD_MATERIALIZED_BADGE_NAME:
return (
<div key={node.name}>
<EuiBadge color="hollow" onClick={handleButtonClick}>
Load Materialized View
</EuiBadge>
<EuiText>{node.isLoading && <EuiLoadingSpinner size="m" />}</EuiText>
<EuiFlexGroup direction="row">
<EuiFlexItem>
<EuiBadge color="hollow" onClick={handleButtonClick}>
Load Materialized View
</EuiBadge>
</EuiFlexItem>
<EuiFlexItem>
<EuiText>{node.isLoading && <EuiLoadingSpinner size="m" />}</EuiText>
</EuiFlexItem>
</EuiFlexGroup>
</div>
);

@@ -448,7 +588,7 @@ export const TableView = ({ http, selectedItems, updateSQLQueries, refreshTree }

const treeDataDatabases = treeData.map((database, index) => ({
label: createLabel(database, selectedItems[0].label, index),
icon: <EuiIcon type="database" size="m" />,
icon: iconCreation(database),
id: 'element_' + index,
callback: () => {
if (database.values?.length === 0 && selectedItems[0].label !== 'OpenSearch') {
@@ -460,16 +600,12 @@ export const TableView = ({ http, selectedItems, updateSQLQueries, refreshTree }
children: database.values?.map((table, index) => ({
label: createLabel(table, database.name, index),
id: `${database.name}_${table.name}`,
icon:
table.type === TREE_ITEM_LOAD_MATERIALIZED_BADGE_NAME ? null : table.type ===
TREE_ITEM_BADGE_NAME ? null : (
<EuiIcon type="tableDensityCompact" size="s" />
),
icon: iconCreation(table),
callback: () => {
if (table.type !== TREE_ITEM_LOAD_MATERIALIZED_BADGE_NAME && table.values?.length === 0) {
if (table.type !== TREE_ITEM_LOAD_MATERIALIZED_BADGE_NAME && table.type !== TREE_ITEM_MATERIALIZED_VIEW_DEFAULT_NAME && table.values?.length === 0) {
handleTableClick(table.name);
}
if (table.type === TREE_ITEM_LOAD_MATERIALIZED_BADGE_NAME) {
if (table.type === TREE_ITEM_MATERIALIZED_VIEW_DEFAULT_NAME) {
handleAccelerationIndexClick(
'materialized',
selectedItems[0].label,
@@ -483,7 +619,7 @@ export const TableView = ({ http, selectedItems, updateSQLQueries, refreshTree }
children: table.values?.map((indexChild, index) => ({
label: createLabel(indexChild, table.name, index),
id: `${database.name}_${table.name}_${indexChild.name}`,
icon: indexChild.type === TREE_ITEM_BADGE_NAME ? null : <EuiIcon type="bolt" size="s" />,
icon: iconCreation(indexChild),
callback: () => {
if (indexChild.type !== TREE_ITEM_BADGE_NAME) {
handleAccelerationIndexClick(