Skip to content

Commit

Permalink
Add materialized view visual builder and query builders (opensearch-p…
Browse files Browse the repository at this point in the history
…roject#129)

* add materialized view visual builder and query builders

Signed-off-by: Shenoy Pratik <sgguruda@amazon.com>

* organize header and PR comments

Signed-off-by: Shenoy Pratik <sgguruda@amazon.com>

---------

Signed-off-by: Shenoy Pratik <sgguruda@amazon.com>
  • Loading branch information
ps48 committed Oct 4, 2023
1 parent 9a69a57 commit a0db3f1
Show file tree
Hide file tree
Showing 20 changed files with 1,313 additions and 339 deletions.
35 changes: 30 additions & 5 deletions common/constants/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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' },
Expand All @@ -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_<index name>_suffix\`. They share a common prefix structure, which is \`flint_<data source name>_<database name>_<table name>_\`. 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.
`;
34 changes: 27 additions & 7 deletions common/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand All @@ -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;
}
50 changes: 34 additions & 16 deletions public/components/acceleration/create/create_acceleration.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -37,18 +39,28 @@ export const CreateAcceleration = ({
const [accelerationFormData, setAccelerationFormData] = useState<CreateAccelerationForm>({
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 = () => {
Expand All @@ -73,7 +85,13 @@ export const CreateAcceleration = ({
accelerationFormData={accelerationFormData}
setAccelerationFormData={setAccelerationFormData}
/>
<IndexTypeSelector
<EuiSpacer size="xxl" />
<IndexSettingOptions
accelerationFormData={accelerationFormData}
setAccelerationFormData={setAccelerationFormData}
/>
<EuiSpacer size="xxl" />
<DefineIndexOptions
accelerationFormData={accelerationFormData}
setAccelerationFormData={setAccelerationFormData}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -20,7 +20,7 @@ export const CreateAccelerationHeader = () => {
<EuiPageHeader>
<EuiPageHeaderSection>
<EuiTitle size="l" data-test-subj="acceleration-header">
<h1>Create Acceleration Index</h1>
<h1>Accelerate data</h1>
</EuiTitle>
</EuiPageHeaderSection>
</EuiPageHeader>
Expand Down
15 changes: 15 additions & 0 deletions public/components/acceleration/create/utils.tsx
Original file line number Diff line number Diff line change
@@ -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);
};
119 changes: 119 additions & 0 deletions public/components/acceleration/selectors/define_index_options.tsx
Original file line number Diff line number Diff line change
@@ -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<React.SetStateAction<CreateAccelerationForm>>;
}

export const DefineIndexOptions = ({
accelerationFormData,
setAccelerationFormData,
}: DefineIndexOptionsProps) => {
const [indexName, setIndexName] = useState('');
const [modalComponent, setModalComponent] = useState(<></>);

const modalValue = (
<EuiModal maxWidth={850} onClose={() => setModalComponent(<></>)}>
<EuiModalHeader>
<EuiModalHeaderTitle>
<h1>Acceleration index naming</h1>
</EuiModalHeaderTitle>
</EuiModalHeader>
<EuiModalBody>
<EuiFlexGroup>
<EuiFlexItem grow={false}>
<EuiMarkdownFormat>{ACCELERATION_INDEX_NAME_INFO}</EuiMarkdownFormat>
</EuiFlexItem>
</EuiFlexGroup>
</EuiModalBody>
<EuiModalFooter>
<EuiButton onClick={() => setModalComponent(<></>)} fill>
Close
</EuiButton>
</EuiModalFooter>
</EuiModal>
);

const onChangeIndexName = (e: ChangeEvent<HTMLInputElement>) => {
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,
<EuiIconTip type="iInCircle" color="subdued" content={prependValue} position="top" />,
];
};

const getAppend = () => {
const appendValue =
accelerationFormData.accelerationIndexType === 'materialized' ? '' : '_index';
return appendValue;
};

return (
<>
<EuiText data-test-subj="define-index-header">
<h3>Index settings</h3>
</EuiText>
<EuiSpacer size="s" />
<EuiFormRow
label="Index name"
helpText='Must be in lowercase letters. Cannot begin with underscores or hyphens. Spaces, commas, and characters :, ", *, +, /, \, |, ?, #, >, or < are not allowed.
Prefix and suffix are added to the name of generated OpenSearch index.'
labelAppend={
<EuiText size="xs">
<EuiLink onClick={() => setModalComponent(modalValue)}>Help</EuiLink>
</EuiText>
}
>
<EuiFieldText
placeholder="Enter index name"
value={accelerationFormData.accelerationIndexType === 'skipping' ? 'skipping' : indexName}
onChange={onChangeIndexName}
aria-label="Enter Index Name"
prepend={getPreprend()}
append={getAppend()}
disabled={accelerationFormData.accelerationIndexType === 'skipping'}
isInvalid={validateIndexName(indexName)}
/>
</EuiFormRow>
{modalComponent}
</>
);
};
Loading

0 comments on commit a0db3f1

Please sign in to comment.