diff --git a/common/constants/index.ts b/common/constants/index.ts index 7083779a..3ca8dd41 100644 --- a/common/constants/index.ts +++ b/common/constants/index.ts @@ -6,6 +6,7 @@ export const PLUGIN_ID = 'queryWorkbenchDashboards'; export const PLUGIN_NAME = 'Query Workbench'; export const OPENSEARCH_ACC_DOCUMENTATION_URL = 'https://opensearch.org/docs/latest'; +export const ACC_INDEX_TYPE_DOCUMENTATION_URL = 'https://opensearch.org/docs/latest'; export const ACCELERATION_INDEX_TYPES = [ { label: 'Skipping Index', value: 'skipping' }, @@ -14,9 +15,33 @@ export const ACCELERATION_INDEX_TYPES = [ ]; export const ACCELERATION_AGGREGRATION_FUNCTIONS = [ - { label: 'count', value: 'count' }, - { label: 'sum', value: 'sum' }, - { label: 'avg', value: 'avg' }, - { label: 'max', value: 'max' }, - { label: 'min', value: 'min' }, + { label: 'count' }, + { label: 'sum' }, + { label: 'avg' }, + { label: 'max' }, + { label: 'min' }, ]; + +export const ACCELERATION_TIME_INTERVAL = [ + { text: 'millisecond(s)', value: 'millisecond' }, + { text: 'second(s)', value: 'second' }, + { text: 'hour(s)', value: 'hour' }, + { text: 'day(s)', value: 'day' }, + { text: 'week(s)', value: 'week' }, +]; + +export const ACCELERATION_ADD_FIELDS_TEXT = '(add fields here)'; + +export const ACCELERATION_INDEX_NAME_INFO = `All OpenSearch acceleration indices have a naming format of pattern: \`prefix__suffix\`. They share a common prefix structure, which is \`flint____\`. Additionally, they may have a suffix that varies based on the index type. +##### Skipping Index +- For 'Skipping' indices, a fixed index name 'skipping' is used, and this name cannot be modified by the user. The suffix added to this type is \`_index\`. + - An example of a 'Skipping' index name would be: \`flint_mydatasource_mydb_mytable_skipping_index\`. +##### Covering Index +- 'Covering' indices allow users to specify their index name. The suffix added to this type is \`_index\`. + - For instance, a 'Covering' index name could be: \`flint_mydatasource_mydb_mytable_myindexname_index\`. +##### Materialized View Index +- 'Materialized View' indices also enable users to define their index name, but they do not have a suffix. + - An example of a 'Materialized View' index name might look like: \`flint_mydatasource_mydb_mytable_myindexname\`. +##### Note: +- All user given index names must be in lowercase letters. Cannot begin with underscores or hyphens. Spaces, commas, and characters :, ", *, +, /, \, |, ?, #, >, or < are not allowed. + `; diff --git a/common/types/index.ts b/common/types/index.ts index f07b6cf7..ed1783a9 100644 --- a/common/types/index.ts +++ b/common/types/index.ts @@ -3,9 +3,11 @@ * SPDX-License-Identifier: Apache-2.0 */ +export type AggregationFunctionType = 'count' | 'sum' | 'avg' | 'max' | 'min'; + export interface MaterializedViewColumn { id: string; - functionName: 'count' | 'sum' | 'avg' | 'min' | 'max'; + functionName: AggregationFunctionType; functionParam: string; fieldAlias?: string; } @@ -23,19 +25,37 @@ export interface DataTableFieldsType { dataType: string; } +export interface RefreshIntervalType { + refreshWindow: number; + refreshInterval: string; +} + +export type AccelerationIndexType = 'skipping' | 'covering' | 'materialized'; + +export interface GroupByTumbleType { + timeField: string; + tumbleWindow: number; + tumbleInterval: string; +} + +export interface materializedViewQueryType { + columnsValues: MaterializedViewColumn[]; + groupByTumbleValue: GroupByTumbleType; +} + export interface CreateAccelerationForm { dataSource: string; + database: string; dataTable: string; dataTableFields: DataTableFieldsType[]; - accelerationIndexType: 'skipping' | 'covering' | 'materialized'; - queryBuilderType: 'visual' | 'code'; + accelerationIndexType: AccelerationIndexType; skippingIndexQueryData: SkippingIndexRowType[]; - coveringIndexQueryData: string; - materializedViewQueryData: string; + coveringIndexQueryData: string[]; + materializedViewQueryData: materializedViewQueryType; accelerationIndexName: string; - accelerationIndexAlias: string; primaryShardsCount: number; replicaShardsCount: number; refreshType: 'interval' | 'auto'; - refreshIntervalSeconds: string | undefined; + checkpointLocation: string | undefined; + refreshIntervalOptions: RefreshIntervalType | undefined; } diff --git a/public/components/acceleration/create/create_acceleration.tsx b/public/components/acceleration/create/create_acceleration.tsx index b840f848..e726adf0 100644 --- a/public/components/acceleration/create/create_acceleration.tsx +++ b/public/components/acceleration/create/create_acceleration.tsx @@ -4,24 +4,26 @@ */ import { - EuiSpacer, - EuiFlyout, - EuiFlyoutBody, - EuiFlyoutHeader, - EuiFlyoutFooter, EuiButton, EuiButtonEmpty, EuiFlexGroup, EuiFlexItem, + EuiFlyout, + EuiFlyoutBody, + EuiFlyoutFooter, + EuiFlyoutHeader, + EuiSpacer, } from '@elastic/eui'; import React, { useState } from 'react'; -import { CreateAccelerationHeader } from './create_acceleration_header'; -import { CautionBannerCallout } from './caution_banner_callout'; -import { AccelerationDataSourceSelector } from '../selectors/source_selector'; -import { IndexTypeSelector } from '../selectors/index_type_selector'; +import { ACCELERATION_TIME_INTERVAL } from '../../../../common/constants'; import { CreateAccelerationForm } from '../../../../common/types/'; -import { QueryVisualEditor } from '../visual_editors/query_visual_editor'; +import { DefineIndexOptions } from '../selectors/define_index_options'; +import { IndexSettingOptions } from '../selectors/index_setting_options'; +import { AccelerationDataSourceSelector } from '../selectors/source_selector'; import { accelerationQueryBuilder } from '../visual_editors/query_builder'; +import { QueryVisualEditor } from '../visual_editors/query_visual_editor'; +import { CautionBannerCallout } from './caution_banner_callout'; +import { CreateAccelerationHeader } from './create_acceleration_header'; export interface CreateAccelerationProps { dataSource: string; @@ -37,18 +39,28 @@ export const CreateAcceleration = ({ const [accelerationFormData, setAccelerationFormData] = useState({ dataSource: '', dataTable: '', + database: '', dataTableFields: [], accelerationIndexType: 'skipping', - queryBuilderType: 'visual', skippingIndexQueryData: [], - coveringIndexQueryData: '', - materializedViewQueryData: '', + coveringIndexQueryData: [], + materializedViewQueryData: { + columnsValues: [], + groupByTumbleValue: { + timeField: '', + tumbleWindow: 0, + tumbleInterval: '', + }, + }, accelerationIndexName: '', - accelerationIndexAlias: '', primaryShardsCount: 5, replicaShardsCount: 1, refreshType: 'auto', - refreshIntervalSeconds: undefined, + checkpointLocation: undefined, + refreshIntervalOptions: { + refreshWindow: 1, + refreshInterval: ACCELERATION_TIME_INTERVAL[1].value, + }, }); const copyToEditor = () => { @@ -73,7 +85,13 @@ export const CreateAcceleration = ({ accelerationFormData={accelerationFormData} setAccelerationFormData={setAccelerationFormData} /> - + + + diff --git a/public/components/acceleration/create/create_acceleration_header.tsx b/public/components/acceleration/create/create_acceleration_header.tsx index 8c4fc968..d21f87d6 100644 --- a/public/components/acceleration/create/create_acceleration_header.tsx +++ b/public/components/acceleration/create/create_acceleration_header.tsx @@ -4,12 +4,12 @@ */ import { + EuiLink, EuiPageHeader, EuiPageHeaderSection, - EuiTitle, EuiSpacer, EuiText, - EuiLink, + EuiTitle, } from '@elastic/eui'; import React from 'react'; import { OPENSEARCH_ACC_DOCUMENTATION_URL } from '../../../../common/constants'; @@ -20,7 +20,7 @@ export const CreateAccelerationHeader = () => { -

Create Acceleration Index

+

Accelerate data

diff --git a/public/components/acceleration/create/utils.tsx b/public/components/acceleration/create/utils.tsx new file mode 100644 index 00000000..798a94ae --- /dev/null +++ b/public/components/acceleration/create/utils.tsx @@ -0,0 +1,15 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { ACCELERATION_INDEX_NAME_REGEX } from '../../../../common/constants'; + +export const pluralizeTime = (timeWindow: number) => { + return timeWindow > 1 ? 's' : ''; +}; + +export const validateIndexName = (value: string) => { + // Check if the value does not begin with underscores or hyphens and all characters are lower case + return ACCELERATION_INDEX_NAME_REGEX.test(value); +}; diff --git a/public/components/acceleration/selectors/define_index_options.tsx b/public/components/acceleration/selectors/define_index_options.tsx new file mode 100644 index 00000000..e43f7258 --- /dev/null +++ b/public/components/acceleration/selectors/define_index_options.tsx @@ -0,0 +1,119 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { + EuiButton, + EuiFieldText, + EuiFlexGroup, + EuiFlexItem, + EuiFormRow, + EuiIconTip, + EuiLink, + EuiMarkdownFormat, + EuiModal, + EuiModalBody, + EuiModalFooter, + EuiModalHeader, + EuiModalHeaderTitle, + EuiSpacer, + EuiText, +} from '@elastic/eui'; +import React, { ChangeEvent, useState } from 'react'; +import { ACCELERATION_INDEX_NAME_INFO } from '../../../../common/constants'; +import { CreateAccelerationForm } from '../../../../common/types'; +import { validateIndexName } from '../create/utils'; + +interface DefineIndexOptionsProps { + accelerationFormData: CreateAccelerationForm; + setAccelerationFormData: React.Dispatch>; +} + +export const DefineIndexOptions = ({ + accelerationFormData, + setAccelerationFormData, +}: DefineIndexOptionsProps) => { + const [indexName, setIndexName] = useState(''); + const [modalComponent, setModalComponent] = useState(<>); + + const modalValue = ( + setModalComponent(<>)}> + + +

Acceleration index naming

+
+
+ + + + {ACCELERATION_INDEX_NAME_INFO} + + + + + setModalComponent(<>)} fill> + Close + + +
+ ); + + const onChangeIndexName = (e: ChangeEvent) => { + setAccelerationFormData({ ...accelerationFormData, accelerationIndexName: e.target.value }); + setIndexName(e.target.value); + }; + + const getPreprend = () => { + const dataSource = + accelerationFormData.dataSource !== '' + ? accelerationFormData.dataSource + : '{Datasource Name}'; + const database = + accelerationFormData.database !== '' ? accelerationFormData.database : '{Database Name}'; + const dataTable = + accelerationFormData.dataTable !== '' ? accelerationFormData.dataTable : '{Table Name}'; + const prependValue = `flint_${dataSource}_${database}_${dataTable}_`; + return [ + prependValue, + , + ]; + }; + + const getAppend = () => { + const appendValue = + accelerationFormData.accelerationIndexType === 'materialized' ? '' : '_index'; + return appendValue; + }; + + return ( + <> + +

Index settings

+
+ + + setModalComponent(modalValue)}>Help + + } + > + + + {modalComponent} + + ); +}; diff --git a/public/components/acceleration/selectors/index_setting_options.tsx b/public/components/acceleration/selectors/index_setting_options.tsx new file mode 100644 index 00000000..02ec6a2a --- /dev/null +++ b/public/components/acceleration/selectors/index_setting_options.tsx @@ -0,0 +1,188 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { + EuiFieldNumber, + EuiFieldText, + EuiFormRow, + EuiRadioGroup, + EuiSelect, + EuiSpacer, + EuiText, +} from '@elastic/eui'; +import React, { ChangeEvent, useState } from 'react'; +import { ACCELERATION_TIME_INTERVAL } from '../../../../common/constants'; +import { CreateAccelerationForm } from '../../../../common/types'; +import { IndexTypeSelector } from './index_type_selector'; + +interface IndexSettingOptionsProps { + accelerationFormData: CreateAccelerationForm; + setAccelerationFormData: React.Dispatch>; +} + +export const IndexSettingOptions = ({ + accelerationFormData, + setAccelerationFormData, +}: IndexSettingOptionsProps) => { + const autoRefreshId = 'refresh-option-1'; + const intervalRefreshId = 'refresh-option-2'; + const refreshOptions = [ + { + id: autoRefreshId, + label: 'Auto Refresh', + }, + { + id: intervalRefreshId, + label: 'Refresh by interval', + }, + ]; + + const [primaryShards, setPrimaryShards] = useState(5); + const [replicaCount, setReplicaCount] = useState(1); + const [refreshTypeSelected, setRefreshTypeSelected] = useState(autoRefreshId); + const [refreshWindow, setRefreshWindow] = useState(1); + const [refreshInterval, setRefreshInterval] = useState(ACCELERATION_TIME_INTERVAL[1].value); + const [checkpoint, setCheckpoint] = useState(''); + + const onChangePrimaryShards = (e: ChangeEvent) => { + const countPrimaryShards = +e.target.value; + setAccelerationFormData({ ...accelerationFormData, primaryShardsCount: countPrimaryShards }); + setPrimaryShards(countPrimaryShards); + }; + + const onChangeReplicaCount = (e: ChangeEvent) => { + const replicaCount = +e.target.value; + setAccelerationFormData({ ...accelerationFormData, replicaShardsCount: replicaCount }); + setReplicaCount(replicaCount); + }; + + const onChangeRefreshType = (optionId: React.SetStateAction) => { + setAccelerationFormData({ + ...accelerationFormData, + refreshType: optionId === autoRefreshId ? 'auto' : 'interval', + }); + setRefreshTypeSelected(optionId); + }; + + const onChangeRefreshWindow = (e: ChangeEvent) => { + const windowCount = +e.target.value; + setAccelerationFormData({ + ...accelerationFormData, + refreshIntervalOptions: { + ...accelerationFormData.refreshIntervalOptions, + refreshWindow: windowCount, + }, + }); + setRefreshWindow(windowCount); + }; + + const onChangeRefreshInterval = (e: React.ChangeEvent) => { + const refreshIntervalValue = e.target.value; + setAccelerationFormData({ + ...accelerationFormData, + refreshIntervalOptions: { + ...accelerationFormData.refreshIntervalOptions, + refreshInterval: refreshIntervalValue, + }, + }); + setRefreshInterval(refreshIntervalValue); + }; + + const onChangeCheckpoint = (e: ChangeEvent) => { + const checkpointLocation = e.target.value; + setAccelerationFormData({ ...accelerationFormData, checkpointLocation: checkpointLocation }); + setCheckpoint(checkpointLocation); + }; + + return ( + <> + +

Index settings

+
+ + + + + + + + + + + + {refreshTypeSelected === intervalRefreshId && ( + + + } + /> + + )} + + + + + ); +}; diff --git a/public/components/acceleration/selectors/index_type_selector.tsx b/public/components/acceleration/selectors/index_type_selector.tsx index 33750b52..fe822322 100644 --- a/public/components/acceleration/selectors/index_type_selector.tsx +++ b/public/components/acceleration/selectors/index_type_selector.tsx @@ -3,15 +3,13 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { EuiComboBox, EuiFormRow, EuiSpacer, EuiText } from '@elastic/eui'; +import { EuiComboBox, EuiComboBoxOptionOption, EuiFormRow, EuiLink, EuiText } from '@elastic/eui'; import React, { useState } from 'react'; -import { ACCELERATION_INDEX_TYPES } from '../../../../common/constants'; -import { CreateAccelerationForm } from '../../../../common/types'; - -interface Indextypes { - label: string; - value: string; -} +import { + ACCELERATION_INDEX_TYPES, + ACC_INDEX_TYPE_DOCUMENTATION_URL, +} from '../../../../common/constants'; +import { AccelerationIndexType, CreateAccelerationForm } from '../../../../common/types'; interface IndexTypeSelectorProps { accelerationFormData: CreateAccelerationForm; @@ -22,31 +20,36 @@ export const IndexTypeSelector = ({ accelerationFormData, setAccelerationFormData, }: IndexTypeSelectorProps) => { - const [selectedIndexType, setSelectedIndexType] = useState([ + const [selectedIndexType, setSelectedIndexType] = useState[]>([ ACCELERATION_INDEX_TYPES[0], ]); return ( <> - -

Define index

-
- + + Help + + + } > { setAccelerationFormData({ ...accelerationFormData, - accelerationIndexType: indexType[0].value, + accelerationIndexType: indexType[0].value as AccelerationIndexType, }); setSelectedIndexType(indexType); }} + isInvalid={selectedIndexType.length === 0} + isClearable={false} /> diff --git a/public/components/acceleration/selectors/source_selector.tsx b/public/components/acceleration/selectors/source_selector.tsx index 1a82cff6..02ffc251 100644 --- a/public/components/acceleration/selectors/source_selector.tsx +++ b/public/components/acceleration/selectors/source_selector.tsx @@ -3,15 +3,17 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { EuiComboBox, EuiFormRow, EuiSpacer, EuiText, htmlIdGenerator } from '@elastic/eui'; -import React, { useState } from 'react'; -import { useEffect } from 'react'; +import { + EuiComboBox, + EuiComboBoxOptionOption, + EuiFormRow, + EuiSpacer, + EuiText, + htmlIdGenerator, +} from '@elastic/eui'; +import React, { useEffect, useState } from 'react'; import { CreateAccelerationForm } from '../../../../common/types'; -interface DataSourceTypes { - label: string; -} - interface AccelerationDataSourceSelectorProps { accelerationFormData: CreateAccelerationForm; setAccelerationFormData: React.Dispatch>; @@ -21,14 +23,18 @@ export const AccelerationDataSourceSelector = ({ accelerationFormData, setAccelerationFormData, }: AccelerationDataSourceSelectorProps) => { - const [dataConnections, setDataConnections] = useState([]); - const [selectedDataConnection, setSelectedDataConnection] = useState([]); - const [tables, setTables] = useState([]); - const [selectedTable, setSelectedTable] = useState([]); + const [dataConnections, setDataConnections] = useState[]>([]); + const [selectedDataConnection, setSelectedDataConnection] = useState< + EuiComboBoxOptionOption[] + >([]); + const [databases, setDatabases] = useState[]>([]); + const [selectedDatabase, setSelectedDatabase] = useState[]>([]); + const [tables, setTables] = useState[]>([]); + const [selectedTable, setSelectedTable] = useState[]>([]); useEffect(() => { + // TODO: remove hardcoded responses setDataConnections([ - // TODO: remove hardcoded responses { label: 'spark1', }, @@ -39,9 +45,23 @@ export const AccelerationDataSourceSelector = ({ }, []); useEffect(() => { + // TODO: remove hardcoded responses if (accelerationFormData.dataSource !== '') { + setDatabases([ + { + label: 'Database1', + }, + { + label: 'Database2', + }, + ]); + } + }, [accelerationFormData.dataSource]); + + useEffect(() => { + // TODO: remove hardcoded responses + if (accelerationFormData.database !== '') { setTables([ - // TODO: remove hardcoded responses { label: 'Table1', }, @@ -50,26 +70,27 @@ export const AccelerationDataSourceSelector = ({ }, ]); } - }, [accelerationFormData.dataSource]); + }, [accelerationFormData.database]); useEffect(() => { + // TODO: remove hardcoded responses if (accelerationFormData.dataTable !== '') { const idPrefix = htmlIdGenerator()(); setAccelerationFormData({ ...accelerationFormData, - // TODO: remove hardcoded responses dataTableFields: [ - { id: `${idPrefix}1`, fieldName: 'Field 1', dataType: 'Integer' }, - { id: `${idPrefix}2`, fieldName: 'Field 2', dataType: 'Integer' }, - { id: `${idPrefix}3`, fieldName: 'Field 3', dataType: 'Integer' }, - { id: `${idPrefix}4`, fieldName: 'Field 4', dataType: 'Integer' }, - { id: `${idPrefix}5`, fieldName: 'Field 5', dataType: 'Integer' }, - { id: `${idPrefix}6`, fieldName: 'Field 6', dataType: 'Integer' }, - { id: `${idPrefix}7`, fieldName: 'Field 7', dataType: 'Integer' }, - { id: `${idPrefix}8`, fieldName: 'Field 8', dataType: 'Integer' }, - { id: `${idPrefix}9`, fieldName: 'Field 9', dataType: 'Integer' }, - { id: `${idPrefix}10`, fieldName: 'Field 10', dataType: 'Integer' }, - { id: `${idPrefix}11`, fieldName: 'Field 11', dataType: 'Integer' }, + { id: `${idPrefix}1`, fieldName: 'Field1', dataType: 'Integer' }, + { id: `${idPrefix}2`, fieldName: 'Field2', dataType: 'Integer' }, + { id: `${idPrefix}3`, fieldName: 'Field3', dataType: 'Integer' }, + { id: `${idPrefix}4`, fieldName: 'Field4', dataType: 'Integer' }, + { id: `${idPrefix}5`, fieldName: 'Field5', dataType: 'Integer' }, + { id: `${idPrefix}6`, fieldName: 'Field6', dataType: 'Integer' }, + { id: `${idPrefix}7`, fieldName: 'Field7', dataType: 'Integer' }, + { id: `${idPrefix}8`, fieldName: 'Field8', dataType: 'Integer' }, + { id: `${idPrefix}9`, fieldName: 'Field9', dataType: 'Integer' }, + { id: `${idPrefix}10`, fieldName: 'Field10', dataType: 'Integer' }, + { id: `${idPrefix}11`, fieldName: 'Field11', dataType: 'Integer' }, + { id: `${idPrefix}12`, fieldName: 'Field12', dataType: 'TimestampType' }, ], }); } @@ -78,19 +99,19 @@ export const AccelerationDataSourceSelector = ({ return ( <> -

Select data connection

+

Select data source

- Select data connection where the data you want to accelerate resides.{' '} + Select data connection where the data you want to accelerate resides. + + + { + setAccelerationFormData({ + ...accelerationFormData, + database: tableOptions[0].label, + }); + setSelectedDatabase(tableOptions); + }} + isInvalid={selectedDatabase.length === 0} + isClearable={false} /> - ); }; diff --git a/public/components/acceleration/visual_editors/covering_index/covering_index_builder.tsx b/public/components/acceleration/visual_editors/covering_index/covering_index_builder.tsx index 1e980650..0335f498 100644 --- a/public/components/acceleration/visual_editors/covering_index/covering_index_builder.tsx +++ b/public/components/acceleration/visual_editors/covering_index/covering_index_builder.tsx @@ -5,15 +5,18 @@ import { EuiComboBox, + EuiComboBoxOptionOption, EuiExpression, + EuiFlexGroup, + EuiFlexItem, EuiPopover, EuiPopoverTitle, EuiSpacer, EuiText, } from '@elastic/eui'; import React, { useState } from 'react'; +import { ACCELERATION_ADD_FIELDS_TEXT } from '../../../../../common/constants'; import { CreateAccelerationForm } from '../../../../../common/types'; -import _ from 'lodash'; interface CoveringIndexBuilderProps { accelerationFormData: CreateAccelerationForm; @@ -25,27 +28,20 @@ export const CoveringIndexBuilder = ({ setAccelerationFormData, }: CoveringIndexBuilderProps) => { const [isPopOverOpen, setIsPopOverOpen] = useState(false); - const [columnsValue, setColumnsValue] = useState('(add columns here)'); - const [selectedOptions, setSelected] = useState([]); + const [columnsValue, setColumnsValue] = useState(ACCELERATION_ADD_FIELDS_TEXT); + const [selectedOptions, setSelectedOptions] = useState([]); - const onChange = (selectedOptions: any[]) => { - let expresseionValue = '(add columns here)'; + const onChange = (selectedOptions: EuiComboBoxOptionOption[]) => { + let expressionValue = ACCELERATION_ADD_FIELDS_TEXT; if (selectedOptions.length > 0) { - expresseionValue = - '(' + - _.reduce( - selectedOptions, - function (columns, n, index) { - const columnValue = columns + `${n.label}`; - if (index !== selectedOptions.length - 1) return `${columnValue}, `; - else return columnValue; - }, - '' - ) + - ')'; + expressionValue = `(${selectedOptions.map((option) => option.label).join(', ')})`; } - setColumnsValue(expresseionValue); - setSelected(selectedOptions); + setAccelerationFormData({ + ...accelerationFormData, + coveringIndexQueryData: selectedOptions.map((option) => option.label), + }); + setColumnsValue(expressionValue); + setSelectedOptions(selectedOptions); }; return ( @@ -54,38 +50,50 @@ export const CoveringIndexBuilder = ({

Covering index definition

- - - + setIsPopOverOpen(true)} + description="CREATE INDEX" + value={accelerationFormData.accelerationIndexName} /> - } - isOpen={isPopOverOpen} - closePopover={() => setIsPopOverOpen(false)} - panelPaddingSize="s" - anchorPosition="downLeft" - > - <> - Columns - { - return { label: x.fieldName }; - })} - selectedOptions={selectedOptions} - onChange={onChange} + + + + + + - - + setIsPopOverOpen(true)} + isInvalid={columnsValue === ACCELERATION_ADD_FIELDS_TEXT} + /> + } + isOpen={isPopOverOpen} + closePopover={() => setIsPopOverOpen(false)} + panelPaddingSize="s" + anchorPosition="downLeft" + > + <> + Columns + ({ label: x.fieldName }))} + selectedOptions={selectedOptions} + onChange={onChange} + /> + + + + ); }; diff --git a/public/components/acceleration/visual_editors/index_setting_options.tsx b/public/components/acceleration/visual_editors/index_setting_options.tsx deleted file mode 100644 index e895c862..00000000 --- a/public/components/acceleration/visual_editors/index_setting_options.tsx +++ /dev/null @@ -1,160 +0,0 @@ -/* - * Copyright OpenSearch Contributors - * SPDX-License-Identifier: Apache-2.0 - */ - -import React, { useState, ChangeEvent } from 'react'; -import { CreateAccelerationForm } from '../../../../common/types'; -import { - EuiFieldNumber, - EuiFieldText, - EuiFormRow, - EuiRadioGroup, - htmlIdGenerator, -} from '@elastic/eui'; - -const idPrefix = htmlIdGenerator()(); - -interface IndexSettingOptionsProps { - accelerationFormData: CreateAccelerationForm; - setAccelerationFormData: React.Dispatch>; -} - -export const IndexSettingOptions = ({ - accelerationFormData, - setAccelerationFormData, -}: IndexSettingOptionsProps) => { - const refreshOptions = [ - { - id: `${idPrefix}0`, - label: 'Auto Refresh', - }, - { - id: `${idPrefix}1`, - label: 'Refresh by interval', - }, - ]; - const [indexName, setIndexName] = useState(''); - const [indexAlias, setIndexAlias] = useState(''); - const [primaryShards, setPrimaryShards] = useState(5); - const [replicaCount, setReplicaCount] = useState(1); - const [refreshTypeSelected, setRefreshTypeSelected] = useState(`${idPrefix}0`); - const [refreshIntervalSeconds, setRefreshIntervalSeconds] = useState('1'); - - const onChangeIndexName = (e: ChangeEvent) => { - setAccelerationFormData({ ...accelerationFormData, accelerationIndexName: e.target.value }); - setIndexName(e.target.value); - }; - - const onChangeindexAlias = (e: ChangeEvent) => { - setAccelerationFormData({ ...accelerationFormData, accelerationIndexAlias: e.target.value }); - setIndexAlias(e.target.value); - }; - - const onChangePrimaryShards = (e: ChangeEvent) => { - setAccelerationFormData({ ...accelerationFormData, primaryShardsCount: +e.target.value }); - setPrimaryShards(+e.target.value); - }; - - const onChangeReplicaCount = (e: ChangeEvent) => { - setAccelerationFormData({ ...accelerationFormData, replicaShardsCount: +e.target.value }); - setReplicaCount(+e.target.value); - }; - - const onChangeRefreshType = (optionId: React.SetStateAction) => { - setAccelerationFormData({ - ...accelerationFormData, - refreshType: optionId === `${idPrefix}0` ? 'auto' : 'interval', - }); - setRefreshTypeSelected(optionId); - }; - - const onChangeRefreshIntervalSeconds = (e: ChangeEvent) => { - setAccelerationFormData({ - ...accelerationFormData, - refreshIntervalSeconds: e.target.value + 's', - }); - setRefreshIntervalSeconds(e.target.value); - }; - - return ( - <> - - - - - onChangeindexAlias(e)} - aria-label="Enter Index alias" - /> - - - onChangePrimaryShards(e)} - aria-label="Number of primary shards" - min={1} - max={100} - /> - - - - - - - - - {refreshTypeSelected === `${idPrefix}1` && ( - - - - )} - - ); -}; diff --git a/public/components/acceleration/visual_editors/materialized_view/add_column_popover.tsx b/public/components/acceleration/visual_editors/materialized_view/add_column_popover.tsx new file mode 100644 index 00000000..f61bdf99 --- /dev/null +++ b/public/components/acceleration/visual_editors/materialized_view/add_column_popover.tsx @@ -0,0 +1,143 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { + EuiButton, + EuiButtonEmpty, + EuiComboBox, + EuiFieldText, + EuiFlexGroup, + EuiFlexItem, + EuiFormRow, + EuiPopover, + EuiPopoverFooter, + EuiPopoverTitle, + EuiSpacer, + htmlIdGenerator, +} from '@elastic/eui'; +import React, { ChangeEvent, useEffect, useState } from 'react'; +import { ACCELERATION_AGGREGRATION_FUNCTIONS } from '../../../../../common/constants'; +import { + AggregationFunctionType, + CreateAccelerationForm, + MaterializedViewColumn, +} from '../../../../../common/types'; + +interface AddColumnPopOverProps { + isColumnPopOverOpen: boolean; + setIsColumnPopOverOpen: React.Dispatch>; + columnExpressionValues: MaterializedViewColumn[]; + setColumnExpressionValues: React.Dispatch>; + accelerationFormData: CreateAccelerationForm; +} + +export const AddColumnPopOver = ({ + isColumnPopOverOpen, + setIsColumnPopOverOpen, + columnExpressionValues, + setColumnExpressionValues, + accelerationFormData, +}: AddColumnPopOverProps) => { + const [selectedFunction, setSelectedFunction] = useState([ + ACCELERATION_AGGREGRATION_FUNCTIONS[0], + ]); + const [selectedField, setSelectedField] = useState([]); + const [selectedAlias, setSeletedAlias] = useState(''); + + const resetSelectedField = () => { + if (accelerationFormData.dataTableFields.length > 0) { + const defaultFieldName = accelerationFormData.dataTableFields[0].fieldName; + setSelectedField([{ label: defaultFieldName }]); + } + }; + + const resetValues = () => { + setSelectedFunction([ACCELERATION_AGGREGRATION_FUNCTIONS[0]]); + resetSelectedField(); + setSeletedAlias(''); + }; + + const onChangeAlias = (e: ChangeEvent) => { + setSeletedAlias(e.target.value); + }; + + useEffect(() => { + resetSelectedField(); + }, []); + + return ( + { + resetValues(); + setIsColumnPopOverOpen(!isColumnPopOverOpen); + }} + size="xs" + > + Add Column + + } + isOpen={isColumnPopOverOpen} + closePopover={() => setIsColumnPopOverOpen(false)} + > + Add Column + <> + + + + + + + + + ({ label: x.fieldName })), + ]} + selectedOptions={selectedField} + onChange={setSelectedField} + /> + + + + + + + + + + { + setColumnExpressionValues([ + ...columnExpressionValues, + { + id: htmlIdGenerator()(), + functionName: selectedFunction[0].label as AggregationFunctionType, + functionParam: selectedField[0].label, + fieldAlias: selectedAlias, + }, + ]); + setIsColumnPopOverOpen(false); + }} + > + Add + + + + ); +}; diff --git a/public/components/acceleration/visual_editors/materialized_view/column_expression.tsx b/public/components/acceleration/visual_editors/materialized_view/column_expression.tsx new file mode 100644 index 00000000..0da3d65b --- /dev/null +++ b/public/components/acceleration/visual_editors/materialized_view/column_expression.tsx @@ -0,0 +1,180 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { + EuiButtonIcon, + EuiComboBox, + EuiExpression, + EuiFieldText, + EuiFlexGroup, + EuiFlexItem, + EuiFormRow, + EuiPopover, +} from '@elastic/eui'; +import _ from 'lodash'; +import React, { useState } from 'react'; +import { ACCELERATION_AGGREGRATION_FUNCTIONS } from '../../../../../common/constants'; +import { + AggregationFunctionType, + CreateAccelerationForm, + MaterializedViewColumn, +} from '../../../../../common/types'; + +interface ColumnExpressionProps { + index: number; + currentColumnExpressionValue: MaterializedViewColumn; + columnExpressionValues: MaterializedViewColumn[]; + setColumnExpressionValues: React.Dispatch>; + accelerationFormData: CreateAccelerationForm; +} + +export const ColumnExpression = ({ + index, + currentColumnExpressionValue, + columnExpressionValues, + setColumnExpressionValues, + accelerationFormData, +}: ColumnExpressionProps) => { + const [isFunctionPopOverOpen, setIsFunctionPopOverOpen] = useState(false); + const [isAliasPopOverOpen, setIsAliasPopOverOpen] = useState(false); + + const updateColumnExpressionValue = (newValue: MaterializedViewColumn, index: number) => { + const updatedArray = [...columnExpressionValues]; + updatedArray[index] = newValue; + setColumnExpressionValues(updatedArray); + }; + + return ( + + + + { + setIsAliasPopOverOpen(false); + setIsFunctionPopOverOpen(true); + }} + /> + } + isOpen={isFunctionPopOverOpen} + closePopover={() => setIsFunctionPopOverOpen(false)} + panelPaddingSize="s" + anchorPosition="downLeft" + > + <> + + + + + updateColumnExpressionValue( + { + ...currentColumnExpressionValue, + functionName: functionOption[0].label as AggregationFunctionType, + }, + index + ) + } + /> + + + + + ({ + label: x.fieldName, + })), + ]} + selectedOptions={[ + { + label: currentColumnExpressionValue.functionParam, + }, + ]} + onChange={(fieldOption) => + updateColumnExpressionValue( + { ...currentColumnExpressionValue, functionParam: fieldOption[0].label }, + index + ) + } + /> + + + + + + + {currentColumnExpressionValue.fieldAlias !== '' && ( + + { + setIsFunctionPopOverOpen(false); + setIsAliasPopOverOpen(true); + }} + /> + } + isOpen={isAliasPopOverOpen} + closePopover={() => setIsAliasPopOverOpen(false)} + panelPaddingSize="s" + anchorPosition="downLeft" + > + + + updateColumnExpressionValue( + { ...currentColumnExpressionValue, fieldAlias: e.target.value }, + index + ) + } + /> + + + + )} + + { + setColumnExpressionValues([ + ..._.filter( + columnExpressionValues, + (o) => o.id !== currentColumnExpressionValue.id + ), + ]); + }} + iconType="trash" + aria-label="delete-column-expression" + /> + + + + ); +}; diff --git a/public/components/acceleration/visual_editors/materialized_view/group_by_tumble_expression.tsx b/public/components/acceleration/visual_editors/materialized_view/group_by_tumble_expression.tsx new file mode 100644 index 00000000..dc853414 --- /dev/null +++ b/public/components/acceleration/visual_editors/materialized_view/group_by_tumble_expression.tsx @@ -0,0 +1,117 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { + EuiComboBox, + EuiComboBoxOptionOption, + EuiExpression, + EuiFieldNumber, + EuiFlexGroup, + EuiFlexItem, + EuiFormRow, + EuiPopover, + EuiSelect, +} from '@elastic/eui'; +import React, { useEffect, useState } from 'react'; +import { ACCELERATION_TIME_INTERVAL } from '../../../../../common/constants'; +import { CreateAccelerationForm, GroupByTumbleType } from '../../../../../common/types'; +import { pluralizeTime } from '../../create/utils'; + +interface GroupByTumbleExpressionProps { + accelerationFormData: CreateAccelerationForm; + setAccelerationFormData: React.Dispatch>; +} + +export const GroupByTumbleExpression = ({ + accelerationFormData, + setAccelerationFormData, +}: GroupByTumbleExpressionProps) => { + const [IsGroupPopOverOpen, setIsGroupPopOverOpen] = useState(false); + const [groupbyValues, setGroupByValues] = useState({ + timeField: '', + tumbleWindow: 1, + tumbleInterval: ACCELERATION_TIME_INTERVAL[0].value, + }); + + const onChangeTumbleWindow = (e: React.ChangeEvent) => { + setGroupByValues({ ...groupbyValues, tumbleWindow: +e.target.value }); + }; + + const onChangeTumbleInterval = (e: React.ChangeEvent) => { + setGroupByValues({ ...groupbyValues, tumbleInterval: e.target.value }); + }; + + const onChangeTimeField = (selectedOptions: EuiComboBoxOptionOption[]) => { + if (selectedOptions.length > 0) + setGroupByValues({ ...groupbyValues, timeField: selectedOptions[0].label }); + }; + + useEffect(() => { + setAccelerationFormData({ + ...accelerationFormData, + materializedViewQueryData: { + ...accelerationFormData.materializedViewQueryData, + groupByTumbleValue: groupbyValues, + }, + }); + }, [groupbyValues]); + + return ( + + setIsGroupPopOverOpen(true)} + isInvalid={groupbyValues.timeField === ''} + /> + } + isOpen={IsGroupPopOverOpen} + closePopover={() => setIsGroupPopOverOpen(false)} + panelPaddingSize="s" + anchorPosition="downLeft" + > + + + + value.dataType.includes('TimestampType')) + .map((value) => ({ label: value.fieldName }))} + selectedOptions={[{ label: groupbyValues.timeField }]} + onChange={onChangeTimeField} + /> + + + + + + + + + + + + + + + + ); +}; diff --git a/public/components/acceleration/visual_editors/materialized_view/materialized_view_builder.tsx b/public/components/acceleration/visual_editors/materialized_view/materialized_view_builder.tsx new file mode 100644 index 00000000..0f3bdeea --- /dev/null +++ b/public/components/acceleration/visual_editors/materialized_view/materialized_view_builder.tsx @@ -0,0 +1,114 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { + EuiExpression, + EuiFlexGroup, + EuiFlexItem, + EuiSpacer, + EuiText, + htmlIdGenerator, +} from '@elastic/eui'; +import _ from 'lodash'; +import React, { useEffect, useState } from 'react'; +import { CreateAccelerationForm, MaterializedViewColumn } from '../../../../../common/types'; +import { AddColumnPopOver } from './add_column_popover'; +import { ColumnExpression } from './column_expression'; +import { GroupByTumbleExpression } from './group_by_tumble_expression'; + +interface MaterializedViewBuilderProps { + accelerationFormData: CreateAccelerationForm; + setAccelerationFormData: React.Dispatch>; +} + +const newColumnExpressionId = htmlIdGenerator()(); + +export const MaterializedViewBuilder = ({ + accelerationFormData, + setAccelerationFormData, +}: MaterializedViewBuilderProps) => { + const [isColumnPopOverOpen, setIsColumnPopOverOpen] = useState(false); + const [columnExpressionValues, setColumnExpressionValues] = useState( + [] + ); + + useEffect(() => { + if (accelerationFormData.dataTableFields.length > 0) { + setColumnExpressionValues([ + { + id: newColumnExpressionId, + functionName: 'count', + functionParam: accelerationFormData.dataTableFields[0].fieldName, + fieldAlias: 'counter1', + }, + ]); + } + }, [accelerationFormData.dataTableFields]); + + useEffect(() => { + setAccelerationFormData({ + ...accelerationFormData, + materializedViewQueryData: { + ...accelerationFormData.materializedViewQueryData, + columnsValues: columnExpressionValues, + }, + }); + }, [columnExpressionValues]); + + return ( + <> + +

Materialized view definition

+
+ + + + + + + + + + + + + + + + + {_.map(columnExpressionValues, (_, i) => { + return ( + + ); + })} + + + + + + + ); +}; diff --git a/public/components/acceleration/visual_editors/query_builder.tsx b/public/components/acceleration/visual_editors/query_builder.tsx index c4e5b47f..5be5e6df 100644 --- a/public/components/acceleration/visual_editors/query_builder.tsx +++ b/public/components/acceleration/visual_editors/query_builder.tsx @@ -3,47 +3,185 @@ * SPDX-License-Identifier: Apache-2.0 */ -import _ from 'lodash'; -import { CreateAccelerationForm, SkippingIndexRowType } from '../../../../common/types'; +import { + CreateAccelerationForm, + GroupByTumbleType, + MaterializedViewColumn, + SkippingIndexRowType, +} from '../../../../common/types'; +import { pluralizeTime } from '../create/utils'; -const buildSkippingIndexColumns = (skippingIndexQueryData: SkippingIndexRowType[]) => { - return _.reduce( - skippingIndexQueryData, - function (columns, n, index) { - const columnValue = columns + ` ${n.fieldName} ${n.accelerationMethod}`; - if (index !== skippingIndexQueryData.length - 1) return `${columnValue}, \n`; - else return `${columnValue} \n`; - }, - '' +/* Add index options to query */ +const buildIndexOptions = (accelerationformData: CreateAccelerationForm) => { + const { + primaryShardsCount, + replicaShardsCount, + refreshType, + checkpointLocation, + } = accelerationformData; + const indexOptions: string[] = []; + + // Add index settings option + indexOptions.push( + `index_settings = '{"number_of_shards":${primaryShardsCount},"number_of_replicas":${replicaShardsCount}}'` ); + + // Add auto refresh option + indexOptions.push(`auto_refresh = ${refreshType === 'auto'}`); + + // Add refresh interval option + if (refreshType === 'interval') { + const { refreshWindow, refreshInterval } = accelerationformData.refreshIntervalOptions; + indexOptions.push( + `refresh_interval = '${refreshWindow} ${refreshInterval}${pluralizeTime(refreshWindow)}'` + ); + } + + // Add checkpoint location option + if (checkpointLocation) { + indexOptions.push(`checkpoint_location = '${checkpointLocation}'`); + } + + // Combine all options with commas and return as a single string + return `WITH (\n${indexOptions.join(',\n')}\n)`; }; +/* Add skipping index columns to query */ +const buildSkippingIndexColumns = (skippingIndexQueryData: SkippingIndexRowType[]) => { + return skippingIndexQueryData + .map((n) => ` ${n.fieldName} ${n.accelerationMethod}`) + .join(', \n'); +}; + +/* + * Builds create skipping index query + * Skipping Index create query example: + * + * CREATE SKIPPING INDEX + * [IF NOT EXISTS] + * ON datasource.database.table + * FOR COLUMNS ( + * field1 VALUE_SET, + * field2 PARTITION, + * field3 MIN_MAX, + * ) WITH ( + * auto_refresh = false, + * refresh_interval = '1 minute', + * checkpoint_location = 's3://test/', + * index_settings = '{"number_of_shards":9,"number_of_replicas":2}' + * ) + */ const skippingIndexQueryBuilder = (accelerationformData: CreateAccelerationForm) => { - /* - * Skipping Index Example - * - * CREATE SKIPPING INDEX ON table_name - * FOR COLUMNS ( - * field1 VALUE_SET, - * field2 PARTITION, - * field3 MIN_MAX, - * ) - */ - let codeQuery = 'CREATE SKIPPING INDEX ON ' + accelerationformData.dataTable; - codeQuery = codeQuery + '\n FOR COLUMNS ( \n'; - codeQuery = codeQuery + buildSkippingIndexColumns(accelerationformData.skippingIndexQueryData); - codeQuery = codeQuery + ')'; + const { dataSource, database, dataTable, skippingIndexQueryData } = accelerationformData; + + const codeQuery = `CREATE SKIPPING INDEX +[IF NOT EXISTS] +ON ${dataSource}.${database}.${dataTable} + FOR COLUMNS ( +${buildSkippingIndexColumns(skippingIndexQueryData)} + ) ${buildIndexOptions(accelerationformData)}`; + return codeQuery; }; +/* Add covering index columns to query */ +const buildCoveringIndexColumns = (coveringIndexQueryData: string[]) => { + return coveringIndexQueryData.map((field) => ` ${field}`).join(', \n'); +}; + +/* + * Builds create covering index query + * Covering Index create query example: + * + * CREATE INDEX index_name + * [IF NOT EXISTS] + * ON datasource.database.table + * FOR COLUMNS ( + * field1, + * field2, + * field3, + * ) WITH ( + * auto_refresh = false, + * refresh_interval = '1 minute', + * checkpoint_location = 's3://test/', + * index_settings = '{"number_of_shards":9,"number_of_replicas":2}' + * ) + */ const coveringIndexQueryBuilder = (accelerationformData: CreateAccelerationForm) => { - return ''; + const { + dataSource, + database, + dataTable, + accelerationIndexName, + coveringIndexQueryData, + } = accelerationformData; + + const codeQuery = `CREATE INDEX ${accelerationIndexName} +[IF NOT EXISTS] +ON ${dataSource}.${database}.${dataTable} + FOR COLUMNS ( +${buildCoveringIndexColumns(coveringIndexQueryData)} + ) ${buildIndexOptions(accelerationformData)}`; + + return codeQuery; +}; + +const buildMaterializedViewColumns = (columnsValues: MaterializedViewColumn[]) => { + return columnsValues + .map( + (column) => + ` ${column.functionName}(${column.functionParam}) ${ + column.fieldAlias && `AS ${column.fieldAlias}` + }` + ) + .join(', \n'); }; +/* Build group by tumble values */ +const buildTumbleValue = (GroupByTumbleValue: GroupByTumbleType) => { + const { timeField, tumbleWindow, tumbleInterval } = GroupByTumbleValue; + return `(${timeField}, '${tumbleWindow} ${tumbleInterval}${pluralizeTime(tumbleWindow)}')`; +}; + +/* + * Builds create materialized view query + * Materialized View create query example: + * + * CREATE MATERIALIZED VIEW datasource.database.index_name + * [IF NOT EXISTS] + * AS SELECT + * count(field) as counter, + * count(*) as counter1, + * sum(field2), + * avg(field3) as average + * WITH ( + * auto_refresh = false, + * refresh_interval = '1 minute', + * checkpoint_location = 's3://test/', + * index_settings = '{"number_of_shards":9,"number_of_replicas":2}' + * ) + */ const materializedQueryViewBuilder = (accelerationformData: CreateAccelerationForm) => { - return ''; + const { + dataSource, + database, + dataTable, + accelerationIndexName, + materializedViewQueryData, + } = accelerationformData; + + const codeQuery = `CREATE MATERIALIZED VIEW ${dataSource}.${database}.${accelerationIndexName} +[IF NOT EXISTS] +AS SELECT +${buildMaterializedViewColumns(materializedViewQueryData.columnsValues)} +FROM ${dataSource}.${database}.${dataTable} +GROUP BY TUMBLE ${buildTumbleValue(materializedViewQueryData.groupByTumbleValue)} + ${buildIndexOptions(accelerationformData)}`; + + return codeQuery; }; +/* Builds create acceleration index query */ export const accelerationQueryBuilder = (accelerationformData: CreateAccelerationForm) => { switch (accelerationformData.accelerationIndexType) { case 'skipping': { diff --git a/public/components/acceleration/visual_editors/query_visual_editor.tsx b/public/components/acceleration/visual_editors/query_visual_editor.tsx index d8f933f8..737f5d1e 100644 --- a/public/components/acceleration/visual_editors/query_visual_editor.tsx +++ b/public/components/acceleration/visual_editors/query_visual_editor.tsx @@ -3,12 +3,12 @@ * SPDX-License-Identifier: Apache-2.0 */ -import React from 'react'; import { EuiSpacer } from '@elastic/eui'; +import React from 'react'; import { CreateAccelerationForm } from '../../../../common/types'; -import { IndexSettingOptions } from './index_setting_options'; -import { SkippingIndexBuilder } from './skipping_index/skipping_index_builder'; import { CoveringIndexBuilder } from './covering_index/covering_index_builder'; +import { MaterializedViewBuilder } from './materialized_view/materialized_view_builder'; +import { SkippingIndexBuilder } from './skipping_index/skipping_index_builder'; interface QueryVisualEditorProps { accelerationFormData: CreateAccelerationForm; @@ -21,10 +21,6 @@ export const QueryVisualEditor = ({ }: QueryVisualEditorProps) => { return ( <> - {accelerationFormData.accelerationIndexType === 'skipping' && ( )} + {accelerationFormData.accelerationIndexType === 'materialized' && ( + + )} ); }; diff --git a/public/components/acceleration/visual_editors/skipping_index/add_fields_modal.tsx b/public/components/acceleration/visual_editors/skipping_index/add_fields_modal.tsx index 92e7c498..e771c1b1 100644 --- a/public/components/acceleration/visual_editors/skipping_index/add_fields_modal.tsx +++ b/public/components/acceleration/visual_editors/skipping_index/add_fields_modal.tsx @@ -4,18 +4,18 @@ */ import { + EuiButton, + EuiInMemoryTable, EuiModal, - EuiModalHeader, - EuiModalHeaderTitle, EuiModalBody, EuiModalFooter, - EuiButton, - EuiInMemoryTable, + EuiModalHeader, + EuiModalHeaderTitle, EuiTableFieldDataColumnType, } from '@elastic/eui'; +import _ from 'lodash'; import React, { useState } from 'react'; import { CreateAccelerationForm, DataTableFieldsType } from '../../../../../common/types'; -import _ from 'lodash'; interface AddFieldsModalProps { setIsAddModalVisible: React.Dispatch>; diff --git a/public/components/acceleration/visual_editors/skipping_index/delete_fields_modal.tsx b/public/components/acceleration/visual_editors/skipping_index/delete_fields_modal.tsx index 58a6fa79..adfa29b0 100644 --- a/public/components/acceleration/visual_editors/skipping_index/delete_fields_modal.tsx +++ b/public/components/acceleration/visual_editors/skipping_index/delete_fields_modal.tsx @@ -3,18 +3,18 @@ * SPDX-License-Identifier: Apache-2.0 */ -import React, { useState } from 'react'; import { - EuiTableFieldDataColumnType, + EuiButton, + EuiInMemoryTable, EuiModal, - EuiModalHeader, - EuiModalHeaderTitle, EuiModalBody, - EuiInMemoryTable, EuiModalFooter, - EuiButton, + EuiModalHeader, + EuiModalHeaderTitle, + EuiTableFieldDataColumnType, } from '@elastic/eui'; import _ from 'lodash'; +import React, { useState } from 'react'; import { CreateAccelerationForm, SkippingIndexRowType } from '../../../../../common/types'; interface AddFieldsModalProps { diff --git a/public/components/acceleration/visual_editors/skipping_index/skipping_index_builder.tsx b/public/components/acceleration/visual_editors/skipping_index/skipping_index_builder.tsx index 0c22baf2..f38b120b 100644 --- a/public/components/acceleration/visual_editors/skipping_index/skipping_index_builder.tsx +++ b/public/components/acceleration/visual_editors/skipping_index/skipping_index_builder.tsx @@ -3,18 +3,18 @@ * SPDX-License-Identifier: Apache-2.0 */ -import React, { useEffect, useState } from 'react'; import { EuiBasicTable, - EuiSelect, - EuiSpacer, - EuiText, - EuiButtonIcon, EuiButton, + EuiButtonIcon, EuiFlexGroup, EuiFlexItem, + EuiSelect, + EuiSpacer, + EuiText, } from '@elastic/eui'; import _ from 'lodash'; +import React, { useEffect, useState } from 'react'; import { CreateAccelerationForm, SkippingIndexRowType } from '../../../../../common/types'; import { AddFieldsModal } from './add_fields_modal'; import { DeleteFieldsModal } from './delete_fields_modal';