mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2025-09-06 07:32:46 +00:00
feat: add custom property filter in advanced search modal (#13559)
* fix: explore loading issue * fix: add custom properties in advanced filter
This commit is contained in:
parent
6111e62466
commit
4e1e17f378
@ -12,9 +12,19 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { Button, Modal, Space, Typography } from 'antd';
|
import { Button, Modal, Space, Typography } from 'antd';
|
||||||
import React, { FunctionComponent } from 'react';
|
import { cloneDeep } from 'lodash';
|
||||||
import { Builder, Query } from 'react-awesome-query-builder';
|
import React, { FunctionComponent, useEffect } from 'react';
|
||||||
|
import {
|
||||||
|
Builder,
|
||||||
|
FieldGroup,
|
||||||
|
Query,
|
||||||
|
ValueSource,
|
||||||
|
} from 'react-awesome-query-builder';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { getTypeByFQN } from '../../rest/metadataTypeAPI';
|
||||||
|
import { EntitiesSupportedCustomProperties } from '../../utils/CustomProperties/CustomProperty.utils';
|
||||||
|
import { getEntityTypeFromSearchIndex } from '../../utils/SearchUtils';
|
||||||
|
import './advanced-search-modal.less';
|
||||||
import { useAdvanceSearch } from './AdvanceSearchProvider/AdvanceSearchProvider.component';
|
import { useAdvanceSearch } from './AdvanceSearchProvider/AdvanceSearchProvider.component';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
@ -29,12 +39,57 @@ export const AdvancedSearchModal: FunctionComponent<Props> = ({
|
|||||||
onCancel,
|
onCancel,
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { config, treeInternal, onTreeUpdate, onReset } = useAdvanceSearch();
|
const {
|
||||||
|
config,
|
||||||
|
treeInternal,
|
||||||
|
onTreeUpdate,
|
||||||
|
onReset,
|
||||||
|
onUpdateConfig,
|
||||||
|
searchIndex,
|
||||||
|
} = useAdvanceSearch();
|
||||||
|
|
||||||
|
const updatedConfig = cloneDeep(config);
|
||||||
|
|
||||||
|
async function getCustomAttributesSubfields() {
|
||||||
|
try {
|
||||||
|
const entityType = getEntityTypeFromSearchIndex(searchIndex);
|
||||||
|
if (!entityType) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const res = await getTypeByFQN(entityType);
|
||||||
|
const customAttributes = res.customProperties;
|
||||||
|
|
||||||
|
const subfields: Record<
|
||||||
|
string,
|
||||||
|
{ type: string; valueSources: ValueSource[] }
|
||||||
|
> = {};
|
||||||
|
|
||||||
|
if (customAttributes) {
|
||||||
|
customAttributes.forEach((attr) => {
|
||||||
|
subfields[attr.name] = {
|
||||||
|
type: 'text',
|
||||||
|
valueSources: ['value'],
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
(updatedConfig.fields.extension as FieldGroup).subfields = subfields;
|
||||||
|
onUpdateConfig(updatedConfig);
|
||||||
|
} catch (error) {
|
||||||
|
// Error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (visible && EntitiesSupportedCustomProperties.includes(searchIndex)) {
|
||||||
|
getCustomAttributesSubfields();
|
||||||
|
}
|
||||||
|
}, [visible, searchIndex]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
closable
|
closable
|
||||||
destroyOnClose
|
destroyOnClose
|
||||||
|
className="advanced-search-modal"
|
||||||
closeIcon={null}
|
closeIcon={null}
|
||||||
footer={
|
footer={
|
||||||
<Space className="justify-between w-full">
|
<Space className="justify-between w-full">
|
||||||
|
@ -111,7 +111,9 @@ export const AdvanceSearchProvider = ({
|
|||||||
treeInternal ? QbUtils.sqlFormat(treeInternal, config) ?? '' : ''
|
treeInternal ? QbUtils.sqlFormat(treeInternal, config) ?? '' : ''
|
||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => setConfig(getQbConfigs(searchIndex)), [searchIndex]);
|
useEffect(() => {
|
||||||
|
setConfig(getQbConfigs(searchIndex));
|
||||||
|
}, [searchIndex]);
|
||||||
|
|
||||||
const handleChange = useCallback(
|
const handleChange = useCallback(
|
||||||
(nTree, nConfig) => {
|
(nTree, nConfig) => {
|
||||||
@ -145,6 +147,10 @@ export const AdvanceSearchProvider = ({
|
|||||||
setSQLQuery('');
|
setSQLQuery('');
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
const handleConfigUpdate = (updatedConfig: Config) => {
|
||||||
|
setConfig(updatedConfig);
|
||||||
|
};
|
||||||
|
|
||||||
// Reset all filters, quick filter and query filter
|
// Reset all filters, quick filter and query filter
|
||||||
const handleResetAllFilters = useCallback(() => {
|
const handleResetAllFilters = useCallback(() => {
|
||||||
setQueryFilter(undefined);
|
setQueryFilter(undefined);
|
||||||
@ -195,8 +201,10 @@ export const AdvanceSearchProvider = ({
|
|||||||
toggleModal,
|
toggleModal,
|
||||||
treeInternal,
|
treeInternal,
|
||||||
config,
|
config,
|
||||||
|
searchIndex,
|
||||||
onReset: handleReset,
|
onReset: handleReset,
|
||||||
onResetAllFilters: handleResetAllFilters,
|
onResetAllFilters: handleResetAllFilters,
|
||||||
|
onUpdateConfig: handleConfigUpdate,
|
||||||
}),
|
}),
|
||||||
[
|
[
|
||||||
queryFilter,
|
queryFilter,
|
||||||
@ -205,8 +213,10 @@ export const AdvanceSearchProvider = ({
|
|||||||
toggleModal,
|
toggleModal,
|
||||||
treeInternal,
|
treeInternal,
|
||||||
config,
|
config,
|
||||||
|
searchIndex,
|
||||||
handleReset,
|
handleReset,
|
||||||
handleResetAllFilters,
|
handleResetAllFilters,
|
||||||
|
handleConfigUpdate,
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -26,6 +26,8 @@ export interface AdvanceSearchContext {
|
|||||||
config: Config;
|
config: Config;
|
||||||
onReset: () => void;
|
onReset: () => void;
|
||||||
onResetAllFilters: () => void;
|
onResetAllFilters: () => void;
|
||||||
|
onUpdateConfig: (config: Config) => void;
|
||||||
|
searchIndex: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type FilterObject = Record<string, string[]>;
|
export type FilterObject = Record<string, string[]>;
|
||||||
|
@ -0,0 +1,35 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2023 Collate.
|
||||||
|
* Licensed 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
.advanced-search-modal {
|
||||||
|
.group.rule_group {
|
||||||
|
border: none !important;
|
||||||
|
padding: 0;
|
||||||
|
.group--children {
|
||||||
|
padding-top: 0;
|
||||||
|
padding-bottom: 0;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.group--field {
|
||||||
|
width: 180px;
|
||||||
|
.ant-select {
|
||||||
|
width: 100% !important;
|
||||||
|
}
|
||||||
|
label {
|
||||||
|
font-weight: normal;
|
||||||
|
margin-bottom: 6px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -266,3 +266,61 @@ export const MOCK_EXPLORE_SEARCH_RESULTS: SearchResponse<ExploreSearchIndex> = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const MOCK_EXPLORE_TAB_ITEMS = [
|
||||||
|
{
|
||||||
|
key: 'table_search_index',
|
||||||
|
label: 'table_search_index',
|
||||||
|
count: 60,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'stored_procedure_search_index',
|
||||||
|
label: 'stored_procedure_search_index',
|
||||||
|
count: 6,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'dashboard_search_index',
|
||||||
|
label: 'dashboard_search_index',
|
||||||
|
count: 42,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'dashboard_data_model_search_index',
|
||||||
|
label: 'dashboard_data_model_search_index',
|
||||||
|
count: 18,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'pipeline_search_index',
|
||||||
|
label: 'pipeline_search_index',
|
||||||
|
count: 24,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'topic_search_index',
|
||||||
|
label: 'topic_search_index',
|
||||||
|
count: 30,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'mlmodel_search_index',
|
||||||
|
label: 'mlmodel_search_index',
|
||||||
|
count: 6,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'container_search_index',
|
||||||
|
label: 'container_search_index',
|
||||||
|
count: 51,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'glossary_term_search_index',
|
||||||
|
label: 'glossary_term_search_index',
|
||||||
|
count: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'tag_search_index',
|
||||||
|
label: 'tag_search_index',
|
||||||
|
count: 40,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'search_entity_search_index',
|
||||||
|
label: 'search_entity_search_index',
|
||||||
|
count: 3,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { ItemType } from 'antd/lib/menu/hooks/useItems';
|
||||||
import { DefaultOptionType } from 'antd/lib/select';
|
import { DefaultOptionType } from 'antd/lib/select';
|
||||||
import { SearchedDataProps } from '../../components/searched-data/SearchedData.interface';
|
import { SearchedDataProps } from '../../components/searched-data/SearchedData.interface';
|
||||||
import { SORT_ORDER } from '../../enums/common.enum';
|
import { SORT_ORDER } from '../../enums/common.enum';
|
||||||
@ -69,9 +70,12 @@ export type SearchHitCounts = Record<ExploreSearchIndex, number>;
|
|||||||
|
|
||||||
export interface ExploreProps {
|
export interface ExploreProps {
|
||||||
aggregations?: Aggregations;
|
aggregations?: Aggregations;
|
||||||
|
activeTabKey: SearchIndex;
|
||||||
|
|
||||||
tabCounts?: SearchHitCounts;
|
tabCounts?: SearchHitCounts;
|
||||||
|
|
||||||
|
tabItems: ItemType[];
|
||||||
|
|
||||||
searchResults?: SearchResponse<ExploreSearchIndex>;
|
searchResults?: SearchResponse<ExploreSearchIndex>;
|
||||||
|
|
||||||
onChangeAdvancedSearchQuickFilters: (
|
onChangeAdvancedSearchQuickFilters: (
|
||||||
|
@ -27,11 +27,10 @@ import {
|
|||||||
} from 'antd';
|
} from 'antd';
|
||||||
import { Content } from 'antd/lib/layout/layout';
|
import { Content } from 'antd/lib/layout/layout';
|
||||||
import Sider from 'antd/lib/layout/Sider';
|
import Sider from 'antd/lib/layout/Sider';
|
||||||
import { isEmpty, isNil, isString, isUndefined, lowerCase, noop } from 'lodash';
|
import { isEmpty, isString, isUndefined, noop } from 'lodash';
|
||||||
import Qs from 'qs';
|
import Qs from 'qs';
|
||||||
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useParams } from 'react-router-dom';
|
|
||||||
import ErrorPlaceHolder from '../../components/common/error-with-placeholder/ErrorPlaceHolder';
|
import ErrorPlaceHolder from '../../components/common/error-with-placeholder/ErrorPlaceHolder';
|
||||||
import { useAdvanceSearch } from '../../components/Explore/AdvanceSearchProvider/AdvanceSearchProvider.component';
|
import { useAdvanceSearch } from '../../components/Explore/AdvanceSearchProvider/AdvanceSearchProvider.component';
|
||||||
import AppliedFilterText from '../../components/Explore/AppliedFilterText/AppliedFilterText';
|
import AppliedFilterText from '../../components/Explore/AppliedFilterText/AppliedFilterText';
|
||||||
@ -44,25 +43,23 @@ import {
|
|||||||
import { getSelectedValuesFromQuickFilter } from '../../components/Explore/Explore.utils';
|
import { getSelectedValuesFromQuickFilter } from '../../components/Explore/Explore.utils';
|
||||||
import ExploreQuickFilters from '../../components/Explore/ExploreQuickFilters';
|
import ExploreQuickFilters from '../../components/Explore/ExploreQuickFilters';
|
||||||
import SortingDropDown from '../../components/Explore/SortingDropDown';
|
import SortingDropDown from '../../components/Explore/SortingDropDown';
|
||||||
import { useGlobalSearchProvider } from '../../components/GlobalSearchProvider/GlobalSearchProvider';
|
|
||||||
import SearchedData from '../../components/searched-data/SearchedData';
|
import SearchedData from '../../components/searched-data/SearchedData';
|
||||||
import { SearchedDataProps } from '../../components/searched-data/SearchedData.interface';
|
import { SearchedDataProps } from '../../components/searched-data/SearchedData.interface';
|
||||||
import { tabsInfo } from '../../constants/explore.constants';
|
import { tabsInfo } from '../../constants/explore.constants';
|
||||||
import { ERROR_PLACEHOLDER_TYPE, SORT_ORDER } from '../../enums/common.enum';
|
import { ERROR_PLACEHOLDER_TYPE, SORT_ORDER } from '../../enums/common.enum';
|
||||||
import { SearchIndex } from '../../enums/search.enum';
|
|
||||||
import {
|
import {
|
||||||
QueryFieldInterface,
|
QueryFieldInterface,
|
||||||
QueryFieldValueInterface,
|
QueryFieldValueInterface,
|
||||||
} from '../../pages/explore/ExplorePage.interface';
|
} from '../../pages/explore/ExplorePage.interface';
|
||||||
import { getDropDownItems } from '../../utils/AdvancedSearchUtils';
|
import { getDropDownItems } from '../../utils/AdvancedSearchUtils';
|
||||||
import { getCountBadge } from '../../utils/CommonUtils';
|
|
||||||
import { getSearchIndexFromPath } from '../../utils/ExplorePage/ExplorePageUtils';
|
|
||||||
import PageLayoutV1 from '../containers/PageLayoutV1';
|
import PageLayoutV1 from '../containers/PageLayoutV1';
|
||||||
import Loader from '../Loader/Loader';
|
import Loader from '../Loader/Loader';
|
||||||
import './ExploreV1.style.less';
|
import './ExploreV1.style.less';
|
||||||
|
|
||||||
const ExploreV1: React.FC<ExploreProps> = ({
|
const ExploreV1: React.FC<ExploreProps> = ({
|
||||||
aggregations,
|
aggregations,
|
||||||
|
activeTabKey,
|
||||||
|
tabItems = [],
|
||||||
searchResults,
|
searchResults,
|
||||||
tabCounts,
|
tabCounts,
|
||||||
onChangeAdvancedSearchQuickFilters,
|
onChangeAdvancedSearchQuickFilters,
|
||||||
@ -79,7 +76,7 @@ const ExploreV1: React.FC<ExploreProps> = ({
|
|||||||
quickFilters,
|
quickFilters,
|
||||||
}) => {
|
}) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { tab } = useParams<{ tab: string }>();
|
// const { tab } = useParams<{ tab: string }>();
|
||||||
const [selectedQuickFilters, setSelectedQuickFilters] = useState<
|
const [selectedQuickFilters, setSelectedQuickFilters] = useState<
|
||||||
ExploreQuickFilterField[]
|
ExploreQuickFilterField[]
|
||||||
>([] as ExploreQuickFilterField[]);
|
>([] as ExploreQuickFilterField[]);
|
||||||
@ -87,8 +84,6 @@ const ExploreV1: React.FC<ExploreProps> = ({
|
|||||||
const [entityDetails, setEntityDetails] =
|
const [entityDetails, setEntityDetails] =
|
||||||
useState<SearchedDataProps['data'][number]['_source']>();
|
useState<SearchedDataProps['data'][number]['_source']>();
|
||||||
|
|
||||||
const { searchCriteria } = useGlobalSearchProvider();
|
|
||||||
|
|
||||||
const parsedSearch = useMemo(
|
const parsedSearch = useMemo(
|
||||||
() =>
|
() =>
|
||||||
Qs.parse(
|
Qs.parse(
|
||||||
@ -122,63 +117,6 @@ const ExploreV1: React.FC<ExploreProps> = ({
|
|||||||
[]
|
[]
|
||||||
);
|
);
|
||||||
|
|
||||||
const tabItems = useMemo(() => {
|
|
||||||
const items = Object.entries(tabsInfo).map(
|
|
||||||
([tabSearchIndex, tabDetail]) => ({
|
|
||||||
key: tabSearchIndex,
|
|
||||||
label: (
|
|
||||||
<div data-testid={`${lowerCase(tabDetail.label)}-tab`}>
|
|
||||||
<Space className="w-full justify-between">
|
|
||||||
<Typography.Text
|
|
||||||
className={
|
|
||||||
tabSearchIndex === searchIndex ? 'text-primary' : ''
|
|
||||||
}>
|
|
||||||
{tabDetail.label}
|
|
||||||
</Typography.Text>
|
|
||||||
<span>
|
|
||||||
{!isNil(tabCounts)
|
|
||||||
? getCountBadge(
|
|
||||||
tabCounts[tabSearchIndex as ExploreSearchIndex],
|
|
||||||
'',
|
|
||||||
tabSearchIndex === searchIndex
|
|
||||||
)
|
|
||||||
: getCountBadge()}
|
|
||||||
</span>
|
|
||||||
</Space>
|
|
||||||
</div>
|
|
||||||
),
|
|
||||||
count: tabCounts ? tabCounts[tabSearchIndex as ExploreSearchIndex] : 0,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
return searchQueryParam
|
|
||||||
? items.filter((tabItem) => {
|
|
||||||
return tabItem.count > 0 || tabItem.key === searchCriteria;
|
|
||||||
})
|
|
||||||
: items;
|
|
||||||
}, [tabsInfo, tabCounts]);
|
|
||||||
|
|
||||||
const activeTabKey = useMemo(() => {
|
|
||||||
if (tab) {
|
|
||||||
return searchIndex;
|
|
||||||
} else if (tabItems.length > 0) {
|
|
||||||
return tabItems[0].key as ExploreSearchIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
return searchIndex;
|
|
||||||
}, [tab, searchIndex, tabItems]);
|
|
||||||
|
|
||||||
// get entity active tab by URL params
|
|
||||||
const defaultActiveTab = useMemo(() => {
|
|
||||||
if (tab) {
|
|
||||||
return getSearchIndexFromPath(tab) ?? SearchIndex.TABLE;
|
|
||||||
} else if (tabItems.length > 0) {
|
|
||||||
return tabItems[0].key;
|
|
||||||
}
|
|
||||||
|
|
||||||
return SearchIndex.TABLE;
|
|
||||||
}, [tab, tabItems]);
|
|
||||||
|
|
||||||
const handleSummaryPanelDisplay = useCallback(
|
const handleSummaryPanelDisplay = useCallback(
|
||||||
(details: SearchedDataProps['data'][number]['_source']) => {
|
(details: SearchedDataProps['data'][number]['_source']) => {
|
||||||
setShowSummaryPanel(true);
|
setShowSummaryPanel(true);
|
||||||
@ -278,7 +216,7 @@ const ExploreV1: React.FC<ExploreProps> = ({
|
|||||||
setShowSummaryPanel(false);
|
setShowSummaryPanel(false);
|
||||||
setEntityDetails(undefined);
|
setEntityDetails(undefined);
|
||||||
}
|
}
|
||||||
}, [tab, searchResults]);
|
}, [searchResults]);
|
||||||
|
|
||||||
if (tabItems.length === 0 && !searchQueryParam) {
|
if (tabItems.length === 0 && !searchQueryParam) {
|
||||||
return <Loader />;
|
return <Loader />;
|
||||||
@ -299,7 +237,7 @@ const ExploreV1: React.FC<ExploreProps> = ({
|
|||||||
items={tabItems}
|
items={tabItems}
|
||||||
mode="inline"
|
mode="inline"
|
||||||
rootClassName="left-container"
|
rootClassName="left-container"
|
||||||
selectedKeys={[defaultActiveTab]}
|
selectedKeys={[activeTabKey]}
|
||||||
onClick={(info) => {
|
onClick={(info) => {
|
||||||
info && onChangeSearchIndex(info.key as ExploreSearchIndex);
|
info && onChangeSearchIndex(info.key as ExploreSearchIndex);
|
||||||
setShowSummaryPanel(false);
|
setShowSummaryPanel(false);
|
||||||
|
@ -13,7 +13,10 @@
|
|||||||
import { act, render, screen } from '@testing-library/react';
|
import { act, render, screen } from '@testing-library/react';
|
||||||
import userEvent from '@testing-library/user-event';
|
import userEvent from '@testing-library/user-event';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { MOCK_EXPLORE_SEARCH_RESULTS } from '../../components/Explore/exlore.mock';
|
import {
|
||||||
|
MOCK_EXPLORE_SEARCH_RESULTS,
|
||||||
|
MOCK_EXPLORE_TAB_ITEMS,
|
||||||
|
} from '../../components/Explore/exlore.mock';
|
||||||
import { ExploreSearchIndex } from '../../components/Explore/explore.interface';
|
import { ExploreSearchIndex } from '../../components/Explore/explore.interface';
|
||||||
import { SearchIndex } from '../../enums/search.enum';
|
import { SearchIndex } from '../../enums/search.enum';
|
||||||
import ExploreV1 from './ExploreV1.component';
|
import ExploreV1 from './ExploreV1.component';
|
||||||
@ -61,6 +64,8 @@ const onChangePage = jest.fn();
|
|||||||
const props = {
|
const props = {
|
||||||
aggregations: {},
|
aggregations: {},
|
||||||
searchResults: MOCK_EXPLORE_SEARCH_RESULTS,
|
searchResults: MOCK_EXPLORE_SEARCH_RESULTS,
|
||||||
|
tabItems: MOCK_EXPLORE_TAB_ITEMS,
|
||||||
|
activeTabKey: SearchIndex.TABLE,
|
||||||
tabCounts: {
|
tabCounts: {
|
||||||
table_search_index: 20,
|
table_search_index: 20,
|
||||||
topic_search_index: 10,
|
topic_search_index: 10,
|
||||||
|
@ -330,6 +330,12 @@ const getCommonQueryBuilderFields = (
|
|||||||
useAsyncSearch: true,
|
useAsyncSearch: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
extension: {
|
||||||
|
label: t('label.custom-attribute-plural'),
|
||||||
|
type: '!group',
|
||||||
|
mainWidgetProps,
|
||||||
|
subfields: {},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
return commonQueryBuilderFields;
|
return commonQueryBuilderFields;
|
||||||
|
@ -13,7 +13,8 @@
|
|||||||
|
|
||||||
import { useAdvanceSearch } from '../../components/Explore/AdvanceSearchProvider/AdvanceSearchProvider.component';
|
import { useAdvanceSearch } from '../../components/Explore/AdvanceSearchProvider/AdvanceSearchProvider.component';
|
||||||
|
|
||||||
import { get, isEmpty, isString } from 'lodash';
|
import { Space, Typography } from 'antd';
|
||||||
|
import { get, isEmpty, isNil, isString, lowerCase } from 'lodash';
|
||||||
import Qs from 'qs';
|
import Qs from 'qs';
|
||||||
import React, {
|
import React, {
|
||||||
FunctionComponent,
|
FunctionComponent,
|
||||||
@ -30,7 +31,9 @@ import {
|
|||||||
SearchHitCounts,
|
SearchHitCounts,
|
||||||
UrlParams,
|
UrlParams,
|
||||||
} from '../../components/Explore/explore.interface';
|
} from '../../components/Explore/explore.interface';
|
||||||
|
import { findActiveSearchIndex } from '../../components/Explore/Explore.utils';
|
||||||
import ExploreV1 from '../../components/ExploreV1/ExploreV1.component';
|
import ExploreV1 from '../../components/ExploreV1/ExploreV1.component';
|
||||||
|
import { useGlobalSearchProvider } from '../../components/GlobalSearchProvider/GlobalSearchProvider';
|
||||||
import { withAdvanceSearch } from '../../components/router/withAdvanceSearch';
|
import { withAdvanceSearch } from '../../components/router/withAdvanceSearch';
|
||||||
import { useTourProvider } from '../../components/TourProvider/TourProvider';
|
import { useTourProvider } from '../../components/TourProvider/TourProvider';
|
||||||
import { getExplorePath, PAGE_SIZE } from '../../constants/constants';
|
import { getExplorePath, PAGE_SIZE } from '../../constants/constants';
|
||||||
@ -44,6 +47,7 @@ import { SORT_ORDER } from '../../enums/common.enum';
|
|||||||
import { SearchIndex } from '../../enums/search.enum';
|
import { SearchIndex } from '../../enums/search.enum';
|
||||||
import { Aggregations, SearchResponse } from '../../interface/search.interface';
|
import { Aggregations, SearchResponse } from '../../interface/search.interface';
|
||||||
import { searchQuery } from '../../rest/searchAPI';
|
import { searchQuery } from '../../rest/searchAPI';
|
||||||
|
import { getCountBadge } from '../../utils/CommonUtils';
|
||||||
import { getCombinedQueryFilterObject } from '../../utils/ExplorePage/ExplorePageUtils';
|
import { getCombinedQueryFilterObject } from '../../utils/ExplorePage/ExplorePageUtils';
|
||||||
import { showErrorToast } from '../../utils/ToastUtils';
|
import { showErrorToast } from '../../utils/ToastUtils';
|
||||||
import {
|
import {
|
||||||
@ -59,6 +63,8 @@ const ExplorePageV1: FunctionComponent = () => {
|
|||||||
|
|
||||||
const { tab } = useParams<UrlParams>();
|
const { tab } = useParams<UrlParams>();
|
||||||
|
|
||||||
|
const { searchCriteria } = useGlobalSearchProvider();
|
||||||
|
|
||||||
const [searchResults, setSearchResults] =
|
const [searchResults, setSearchResults] =
|
||||||
useState<SearchResponse<ExploreSearchIndex>>();
|
useState<SearchResponse<ExploreSearchIndex>>();
|
||||||
|
|
||||||
@ -177,12 +183,59 @@ const ExplorePageV1: FunctionComponent = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const searchIndex = useMemo(() => {
|
const searchIndex = useMemo(() => {
|
||||||
const tabInfo = Object.entries(tabsInfo).find(
|
if (searchHitCounts) {
|
||||||
([, tabInfo]) => tabInfo.path === tab
|
const tabInfo = Object.entries(tabsInfo).find(
|
||||||
|
([, tabInfo]) => tabInfo.path === tab
|
||||||
|
);
|
||||||
|
if (isNil(tabInfo)) {
|
||||||
|
const activeKey = findActiveSearchIndex(searchHitCounts);
|
||||||
|
|
||||||
|
return activeKey ? activeKey : SearchIndex.TABLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return tabInfo[0] as ExploreSearchIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
return SearchIndex.TABLE;
|
||||||
|
}, [tab, searchHitCounts]);
|
||||||
|
|
||||||
|
const tabItems = useMemo(() => {
|
||||||
|
const items = Object.entries(tabsInfo).map(
|
||||||
|
([tabSearchIndex, tabDetail]) => ({
|
||||||
|
key: tabSearchIndex,
|
||||||
|
label: (
|
||||||
|
<div data-testid={`${lowerCase(tabDetail.label)}-tab`}>
|
||||||
|
<Space className="w-full justify-between">
|
||||||
|
<Typography.Text
|
||||||
|
className={
|
||||||
|
tabSearchIndex === searchIndex ? 'text-primary' : ''
|
||||||
|
}>
|
||||||
|
{tabDetail.label}
|
||||||
|
</Typography.Text>
|
||||||
|
<span>
|
||||||
|
{!isNil(searchHitCounts)
|
||||||
|
? getCountBadge(
|
||||||
|
searchHitCounts[tabSearchIndex as ExploreSearchIndex],
|
||||||
|
'',
|
||||||
|
tabSearchIndex === searchIndex
|
||||||
|
)
|
||||||
|
: getCountBadge()}
|
||||||
|
</span>
|
||||||
|
</Space>
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
count: searchHitCounts
|
||||||
|
? searchHitCounts[tabSearchIndex as ExploreSearchIndex]
|
||||||
|
: 0,
|
||||||
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
return (tabInfo?.[0] as ExploreSearchIndex) ?? SearchIndex.TABLE;
|
return searchQueryParam
|
||||||
}, [tab]);
|
? items.filter((tabItem) => {
|
||||||
|
return tabItem.count > 0 || tabItem.key === searchCriteria;
|
||||||
|
})
|
||||||
|
: items;
|
||||||
|
}, [tabsInfo, searchHitCounts, searchIndex]);
|
||||||
|
|
||||||
const page = useMemo(() => {
|
const page = useMemo(() => {
|
||||||
const pageParam = parsedSearch.page;
|
const pageParam = parsedSearch.page;
|
||||||
@ -353,6 +406,7 @@ const ExplorePageV1: FunctionComponent = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<ExploreV1
|
<ExploreV1
|
||||||
|
activeTabKey={searchIndex}
|
||||||
aggregations={updatedAggregations}
|
aggregations={updatedAggregations}
|
||||||
loading={isLoading && !isTourOpen}
|
loading={isLoading && !isTourOpen}
|
||||||
quickFilters={advancesSearchQuickFilters}
|
quickFilters={advancesSearchQuickFilters}
|
||||||
@ -366,6 +420,7 @@ const ExplorePageV1: FunctionComponent = () => {
|
|||||||
sortOrder={sortOrder}
|
sortOrder={sortOrder}
|
||||||
sortValue={sortValue}
|
sortValue={sortValue}
|
||||||
tabCounts={searchHitCounts}
|
tabCounts={searchHitCounts}
|
||||||
|
tabItems={tabItems}
|
||||||
onChangeAdvancedSearchQuickFilters={handleAdvanceSearchQuickFiltersChange}
|
onChangeAdvancedSearchQuickFilters={handleAdvanceSearchQuickFiltersChange}
|
||||||
onChangePage={handlePageChange}
|
onChangePage={handlePageChange}
|
||||||
onChangeSearchIndex={handleSearchIndexChange}
|
onChangeSearchIndex={handleSearchIndexChange}
|
||||||
|
@ -15,6 +15,7 @@ import {
|
|||||||
ExtentionEntitiesKeys,
|
ExtentionEntitiesKeys,
|
||||||
} from '../../components/common/CustomPropertyTable/CustomPropertyTable.interface';
|
} from '../../components/common/CustomPropertyTable/CustomPropertyTable.interface';
|
||||||
import { EntityType, TabSpecificField } from '../../enums/entity.enum';
|
import { EntityType, TabSpecificField } from '../../enums/entity.enum';
|
||||||
|
import { SearchIndex } from '../../enums/search.enum';
|
||||||
import { getDashboardByFqn } from '../../rest/dashboardAPI';
|
import { getDashboardByFqn } from '../../rest/dashboardAPI';
|
||||||
import {
|
import {
|
||||||
getDatabaseDetailsByFQN,
|
getDatabaseDetailsByFQN,
|
||||||
@ -63,3 +64,17 @@ export const getEntityExtentionDetailsFromEntityType = <
|
|||||||
console.error(`Custom properties for Entity: ${type} not supported yet.`);
|
console.error(`Custom properties for Entity: ${type} not supported yet.`);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const EntitiesSupportedCustomProperties: string[] = [
|
||||||
|
SearchIndex.DATABASE,
|
||||||
|
SearchIndex.DATABASE_SCHEMA,
|
||||||
|
SearchIndex.TABLE,
|
||||||
|
SearchIndex.STORED_PROCEDURE,
|
||||||
|
SearchIndex.DASHBOARD,
|
||||||
|
SearchIndex.PIPELINE,
|
||||||
|
SearchIndex.TOPIC,
|
||||||
|
SearchIndex.CONTAINER,
|
||||||
|
SearchIndex.MLMODEL,
|
||||||
|
SearchIndex.SEARCH_INDEX,
|
||||||
|
SearchIndex.GLOSSARY,
|
||||||
|
];
|
||||||
|
@ -12,10 +12,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { isEmpty, isEqual, isUndefined, uniqWith } from 'lodash';
|
import { isEmpty, isEqual, isUndefined, uniqWith } from 'lodash';
|
||||||
import { ExploreSearchIndex } from '../../components/Explore/explore.interface';
|
|
||||||
import { tabsInfo } from '../../constants/explore.constants';
|
|
||||||
import { QueryFilterFieldsEnum } from '../../enums/Explore.enum';
|
import { QueryFilterFieldsEnum } from '../../enums/Explore.enum';
|
||||||
import { SearchIndex } from '../../enums/search.enum';
|
|
||||||
import { Aggregations, Bucket } from '../../interface/search.interface';
|
import { Aggregations, Bucket } from '../../interface/search.interface';
|
||||||
import {
|
import {
|
||||||
QueryFieldInterface,
|
QueryFieldInterface,
|
||||||
@ -119,13 +116,3 @@ export const getBucketsWithUpdatedCounts = (
|
|||||||
};
|
};
|
||||||
})
|
})
|
||||||
.sort((a, b) => b.doc_count - a.doc_count); // Sorting buckets according to the entity counts
|
.sort((a, b) => b.doc_count - a.doc_count); // Sorting buckets according to the entity counts
|
||||||
|
|
||||||
export const getSearchIndexFromPath = (path: string): SearchIndex | null => {
|
|
||||||
for (const key in tabsInfo) {
|
|
||||||
if (tabsInfo[key as ExploreSearchIndex].path === path) {
|
|
||||||
return key as SearchIndex;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
};
|
|
||||||
|
@ -24,7 +24,7 @@ import {
|
|||||||
FQN_SEPARATOR_CHAR,
|
FQN_SEPARATOR_CHAR,
|
||||||
WILD_CARD_CHAR,
|
WILD_CARD_CHAR,
|
||||||
} from '../constants/char.constants';
|
} from '../constants/char.constants';
|
||||||
import { FqnPart } from '../enums/entity.enum';
|
import { EntityType, FqnPart } from '../enums/entity.enum';
|
||||||
import { SearchIndex } from '../enums/search.enum';
|
import { SearchIndex } from '../enums/search.enum';
|
||||||
import { getPartialNameFromTableFQN } from './CommonUtils';
|
import { getPartialNameFromTableFQN } from './CommonUtils';
|
||||||
import { serviceTypeLogo } from './ServiceUtils';
|
import { serviceTypeLogo } from './ServiceUtils';
|
||||||
@ -227,3 +227,27 @@ export const filterOptionsByIndex = (
|
|||||||
.filter((option) => option._index === searchIndex)
|
.filter((option) => option._index === searchIndex)
|
||||||
.map((option) => option._source)
|
.map((option) => option._source)
|
||||||
.slice(0, maxItemsPerType);
|
.slice(0, maxItemsPerType);
|
||||||
|
|
||||||
|
export const getEntityTypeFromSearchIndex = (searchIndex: string) => {
|
||||||
|
const commonAssets: Record<string, string> = {
|
||||||
|
[SearchIndex.TABLE]: EntityType.TABLE,
|
||||||
|
[SearchIndex.PIPELINE]: EntityType.PIPELINE,
|
||||||
|
[SearchIndex.DASHBOARD]: EntityType.DASHBOARD,
|
||||||
|
[SearchIndex.MLMODEL]: EntityType.MLMODEL,
|
||||||
|
[SearchIndex.TOPIC]: EntityType.TOPIC,
|
||||||
|
[SearchIndex.CONTAINER]: EntityType.CONTAINER,
|
||||||
|
[SearchIndex.STORED_PROCEDURE]: EntityType.STORED_PROCEDURE,
|
||||||
|
[SearchIndex.DASHBOARD_DATA_MODEL]: EntityType.DASHBOARD_DATA_MODEL,
|
||||||
|
[SearchIndex.SEARCH_INDEX]: EntityType.SEARCH_INDEX,
|
||||||
|
[SearchIndex.DATABASE_SERVICE]: EntityType.DATABASE_SERVICE,
|
||||||
|
[SearchIndex.MESSAGING_SERVICE]: EntityType.MESSAGING_SERVICE,
|
||||||
|
[SearchIndex.DASHBOARD_SERVICE]: EntityType.DASHBOARD_SERVICE,
|
||||||
|
[SearchIndex.PIPELINE_SERVICE]: EntityType.PIPELINE_SERVICE,
|
||||||
|
[SearchIndex.ML_MODEL_SERVICE]: EntityType.MLMODEL_SERVICE,
|
||||||
|
[SearchIndex.STORAGE_SERVICE]: EntityType.STORAGE_SERVICE,
|
||||||
|
[SearchIndex.SEARCH_SERVICE]: EntityType.SEARCH_SERVICE,
|
||||||
|
[SearchIndex.GLOSSARY]: EntityType.GLOSSARY,
|
||||||
|
};
|
||||||
|
|
||||||
|
return commonAssets[searchIndex] || null; // Return null if not found
|
||||||
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user