diff --git a/openmetadata-ui/src/main/resources/ui/cypress/common/advancedSearch.js b/openmetadata-ui/src/main/resources/ui/cypress/common/advancedSearch.js index a7f5d01de80..c60d1c765a9 100644 --- a/openmetadata-ui/src/main/resources/ui/cypress/common/advancedSearch.js +++ b/openmetadata-ui/src/main/resources/ui/cypress/common/advancedSearch.js @@ -52,9 +52,9 @@ export const FIELDS = { Owner: { name: 'Owner', testid: '[title="Owner"]', - searchTerm1: 'Colin Ho', - searchCriteriaFirstGroup: 'Colin Ho', - responseValueFirstGroup: `"displayName":"Colin Ho"`, + searchTerm1: 'Aaron Johnson', + searchCriteriaFirstGroup: 'Aaron Johnson', + responseValueFirstGroup: `"displayName":"Aaron Johnson"`, searchCriteriaSecondGroup: 'Aaron Singh', owner: true, responseValueSecondGroup: 'Aaron Singh', @@ -276,25 +276,12 @@ export const addOwner = (searchTerm, ownerName) => { .should('exist') .should('be.visible') .click(); - - interceptURL( - 'GET', - `api/v1/search/query?q=*${encodeURI(searchTerm)}*&from=0&size=*&index=*`, - 'searchOwner' - ); - cy.get('.user-team-select-popover [data-testid="searchbar"]') - .eq(1) - .should('be.visible') - .and('exist') - .trigger('click') - .type(searchTerm); - - verifyResponseStatusCode('@searchOwner', 200); + cy.wait(3000); interceptURL('PATCH', '/api/v1/tables/*', 'tablePatch'); + // Selecting the user - cy.get(`[data-testid="user-tag"]`) - .contains(ownerName) + cy.get(`[title="${ownerName}"]`) .should('exist') .scrollIntoView() .and('be.visible') diff --git a/openmetadata-ui/src/main/resources/ui/cypress/constants/advancedSearchQuickFilters.constants.js b/openmetadata-ui/src/main/resources/ui/cypress/constants/advancedSearchQuickFilters.constants.js index b02bd396346..547192d27c0 100644 --- a/openmetadata-ui/src/main/resources/ui/cypress/constants/advancedSearchQuickFilters.constants.js +++ b/openmetadata-ui/src/main/resources/ui/cypress/constants/advancedSearchQuickFilters.constants.js @@ -70,9 +70,9 @@ export const DASHBOARD_DROPDOWN_ITEMS = [ label: 'Chart', key: 'charts.name', selectOption1: 'ETA Predictions Accuracy', - selectOptionTestId1: '210', + selectOptionTestId1: 'ETA Predictions Accuracy', selectOption2: 'Birth in France by department in 2016', - selectOptionTestId2: '161', + selectOptionTestId2: 'Birth in France by department in 2016', }, ]; @@ -81,10 +81,10 @@ export const PIPELINE_DROPDOWN_ITEMS = [ { label: 'Task', key: 'tasks.name', - selectOption1: 'hive_create_table', - selectOptionTestId1: 'hive_create_table', - selectOption2: 'presto_task', - selectOptionTestId2: 'presto_task', + selectOption1: 'Hive Create Table', + selectOptionTestId1: 'Hive Create Table', + selectOption2: 'Presto Task', + selectOptionTestId2: 'Presto Task', }, ]; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Explore/ExploreQuickFilters.interface.ts b/openmetadata-ui/src/main/resources/ui/src/components/Explore/ExploreQuickFilters.interface.ts index 480265b17d9..7f6eb717c04 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Explore/ExploreQuickFilters.interface.ts +++ b/openmetadata-ui/src/main/resources/ui/src/components/Explore/ExploreQuickFilters.interface.ts @@ -12,7 +12,7 @@ */ import { SearchIndex } from '../../enums/search.enum'; -import { EntityUnion, ExploreQuickFilterField } from './explore.interface'; +import { ExploreQuickFilterField } from './explore.interface'; export interface ExploreQuickFiltersProps { index: SearchIndex; @@ -26,8 +26,3 @@ export interface FilterFieldsMenuItem { label: string; defaultField: boolean; } - -export interface FormattedSuggestResponseObject { - text: string; - source: EntityUnion; -} diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Explore/ExploreQuickFilters.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Explore/ExploreQuickFilters.tsx index 63204dda3ef..20ce94c3881 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Explore/ExploreQuickFilters.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Explore/ExploreQuickFilters.tsx @@ -29,12 +29,11 @@ import { } from '../../constants/AdvancedSearch.constants'; import { getAdvancedField, - getOptionsObject, + getOptionTextFromKey, } from '../../utils/AdvancedSearchUtils'; import { showErrorToast } from '../../utils/ToastUtils'; import SearchDropdown from '../SearchDropdown/SearchDropdown'; import { SearchDropdownOption } from '../SearchDropdown/SearchDropdown.interface'; -import { EntityUnion } from './explore.interface'; import { ExploreQuickFiltersProps } from './ExploreQuickFilters.interface'; const ExploreQuickFilters: FC = ({ @@ -98,14 +97,16 @@ const ExploreQuickFilters: FC = ({ const suggestOptions = res.data.suggest['metadata-suggest'][0].options ?? []; - const formattedSuggestions = suggestOptions.map((op) => ({ - text: op.text, - source: op._source as EntityUnion, - })); + const formattedSuggestions = suggestOptions.map((option) => { + const optionsText = getOptionTextFromKey(index, option, key); - const optionsArray = getOptionsObject(key, formattedSuggestions); + return { + key: optionsText, + label: optionsText, + }; + }); - setOptions(uniqWith(optionsArray, isEqual)); + setOptions(uniqWith(formattedSuggestions, isEqual)); } else { if (key === 'tags.tagFQN') { const res = await getTagSuggestions(value); diff --git a/openmetadata-ui/src/main/resources/ui/src/components/searched-data/SearchedData.interface.ts b/openmetadata-ui/src/main/resources/ui/src/components/searched-data/SearchedData.interface.ts index 836436c9504..b90e8fd8e3c 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/searched-data/SearchedData.interface.ts +++ b/openmetadata-ui/src/main/resources/ui/src/components/searched-data/SearchedData.interface.ts @@ -23,6 +23,8 @@ import { SearchHitBody, TableSearchSource, TagClassSearchSource, + TeamSearchSource, + UserSearchSource, } from '../../interface/search.interface'; import { ExploreSearchIndex } from '../Explore/explore.interface'; @@ -49,6 +51,8 @@ export type SourceType = ( | GlossarySearchSource | TagClassSearchSource | QuerySearchSource + | UserSearchSource + | TeamSearchSource >, Fields > diff --git a/openmetadata-ui/src/main/resources/ui/src/constants/AdvancedSearch.constants.ts b/openmetadata-ui/src/main/resources/ui/src/constants/AdvancedSearch.constants.ts index 3b68410c24c..a3d7fade3f8 100644 --- a/openmetadata-ui/src/main/resources/ui/src/constants/AdvancedSearch.constants.ts +++ b/openmetadata-ui/src/main/resources/ui/src/constants/AdvancedSearch.constants.ts @@ -61,16 +61,20 @@ export const TABLE_DROPDOWN_ITEMS = [ ]; export const DASHBOARD_DROPDOWN_ITEMS = [ + { + label: t('label.data-model'), + key: 'dataModels.displayName.keyword', + }, { label: t('label.chart'), - key: 'charts.name', + key: 'charts.displayName.keyword', }, ]; export const PIPELINE_DROPDOWN_ITEMS = [ { label: t('label.task'), - key: 'tasks.name', + key: 'tasks.displayName.keyword', }, ]; @@ -142,9 +146,9 @@ export const emptyJsonTree: JsonTree = { */ export const autocomplete: (args: { searchIndex: SearchIndex | SearchIndex[]; + entitySearchIndex: SearchIndex | SearchIndex[]; + entityField: EntityFields; suggestField?: SuggestionField; - entitySearchIndex?: SearchIndex; - entityField?: EntityFields; }) => SelectFieldSettings['asyncFetch'] = ({ searchIndex, suggestField, @@ -183,8 +187,8 @@ export const autocomplete: (args: { }); } else { return getAdvancedFieldDefaultOptions( - entitySearchIndex as SearchIndex, - entityField ?? '' + entitySearchIndex, + entityField ).then((response) => { const buckets = response.data.aggregations[`sterms#${entityField}`].buckets; @@ -227,7 +231,7 @@ const getCommonQueryBuilderFields = ( fieldSettings: { asyncFetch: autocomplete({ searchIndex: [SearchIndex.USER, SearchIndex.TEAM], - entitySearchIndex, + entitySearchIndex: [SearchIndex.USER, SearchIndex.TEAM], entityField: EntityFields.OWNER, }), useAsyncSearch: true, @@ -278,9 +282,9 @@ const getServiceQueryBuilderFields = (index: SearchIndex) => { fieldSettings: { asyncFetch: autocomplete({ searchIndex: index, - suggestField: SuggestionField.SERVICE, entitySearchIndex: index, entityField: EntityFields.SERVICE, + suggestField: SuggestionField.SERVICE, }), useAsyncSearch: true, }, @@ -301,9 +305,9 @@ const tableQueryBuilderFields: Fields = { fieldSettings: { asyncFetch: autocomplete({ searchIndex: SearchIndex.TABLE, - suggestField: SuggestionField.DATABASE, entitySearchIndex: SearchIndex.TABLE, entityField: EntityFields.DATABASE, + suggestField: SuggestionField.DATABASE, }), useAsyncSearch: true, }, @@ -316,9 +320,9 @@ const tableQueryBuilderFields: Fields = { fieldSettings: { asyncFetch: autocomplete({ searchIndex: SearchIndex.TABLE, - suggestField: SuggestionField.SCHEMA, entitySearchIndex: SearchIndex.TABLE, entityField: EntityFields.DATABASE_SCHEMA, + suggestField: SuggestionField.SCHEMA, }), useAsyncSearch: true, }, @@ -331,9 +335,9 @@ const tableQueryBuilderFields: Fields = { fieldSettings: { asyncFetch: autocomplete({ searchIndex: SearchIndex.TABLE, - suggestField: SuggestionField.COLUMN, entitySearchIndex: SearchIndex.TABLE, entityField: EntityFields.COLUMN, + suggestField: SuggestionField.COLUMN, }), useAsyncSearch: true, }, diff --git a/openmetadata-ui/src/main/resources/ui/src/enums/AdvancedSearch.enum.ts b/openmetadata-ui/src/main/resources/ui/src/enums/AdvancedSearch.enum.ts index a2128064adf..b46790b5e03 100644 --- a/openmetadata-ui/src/main/resources/ui/src/enums/AdvancedSearch.enum.ts +++ b/openmetadata-ui/src/main/resources/ui/src/enums/AdvancedSearch.enum.ts @@ -31,16 +31,17 @@ export enum AdvancedFields { CHART = 'chart_suggest', DATA_MODEL = 'data_model_suggest', TASK = 'task_suggest', + FIELD = 'field_suggest', } export enum EntityFields { - OWNER = 'owner.displayName', + OWNER = 'displayName.keyword', TAG = 'tags.tagFQN', TIER = 'tier.tagFQN', SERVICE = 'service.name', DATABASE = 'database.name', DATABASE_SCHEMA = 'databaseSchema.name', COLUMN = 'columns.name', - CHART = 'charts.name', - TASK = 'tasks.name', + CHART = 'charts.displayName.keyword', + TASK = 'tasks.displayName.keyword', } diff --git a/openmetadata-ui/src/main/resources/ui/src/interface/search.interface.ts b/openmetadata-ui/src/main/resources/ui/src/interface/search.interface.ts index b6700aa96a3..adc500b23db 100644 --- a/openmetadata-ui/src/main/resources/ui/src/interface/search.interface.ts +++ b/openmetadata-ui/src/main/resources/ui/src/interface/search.interface.ts @@ -72,7 +72,7 @@ export interface TeamSearchSource extends SearchSourceBase, Team {} // extends E export interface ContainerSearchSource extends SearchSourceBase, Container {} // extends EntityInterface -export interface dashboardDataModelSearchSource +export interface DashboardDataModelSearchSource extends SearchSourceBase, DashboardDataModel {} // extends EntityInterface @@ -92,7 +92,10 @@ export type ExploreSearchSource = | ContainerSearchSource | GlossarySearchSource | QuerySearchSource - | TagClassSearchSource; + | UserSearchSource + | TeamSearchSource + | TagClassSearchSource + | DashboardDataModelSearchSource; export type SearchIndexSearchSourceMapping = { [SearchIndex.TABLE]: TableSearchSource; diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/DataInsightPage/DataInsightPage.component.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/DataInsightPage/DataInsightPage.component.tsx index ac92c97b809..864e9b4da0a 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/DataInsightPage/DataInsightPage.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/pages/DataInsightPage/DataInsightPage.component.tsx @@ -35,6 +35,7 @@ import TopViewEntities from 'components/DataInsightDetail/TopViewEntities'; import TotalEntityInsight from 'components/DataInsightDetail/TotalEntityInsight'; import SearchDropdown from 'components/SearchDropdown/SearchDropdown'; import { SearchDropdownOption } from 'components/SearchDropdown/SearchDropdown.interface'; +import { EntityFields } from 'enums/AdvancedSearch.enum'; import { t } from 'i18next'; import { isEmpty } from 'lodash'; import React, { useEffect, useLayoutEffect, useMemo, useState } from 'react'; @@ -73,7 +74,11 @@ import './DataInsight.less'; import DataInsightLeftPanel from './DataInsightLeftPanel'; import KPIList from './KPIList'; -const fetchTeamSuggestions = autocomplete({ searchIndex: SearchIndex.TEAM }); +const fetchTeamSuggestions = autocomplete({ + searchIndex: SearchIndex.TEAM, + entitySearchIndex: SearchIndex.TEAM, + entityField: EntityFields.OWNER, +}); const DataInsightPage = () => { const { tab } = useParams<{ tab: DataInsightTabs }>(); diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/AdvancedSearchUtils.test.tsx b/openmetadata-ui/src/main/resources/ui/src/utils/AdvancedSearchUtils.test.tsx index c77cc7e9170..24824cb557f 100644 --- a/openmetadata-ui/src/main/resources/ui/src/utils/AdvancedSearchUtils.test.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/utils/AdvancedSearchUtils.test.tsx @@ -12,13 +12,31 @@ */ import { SearchDropdownOption } from 'components/SearchDropdown/SearchDropdown.interface'; +import { SearchIndex } from 'enums/search.enum'; import { + getChartsOptions, + getColumnsOptions, + getSchemaFieldOptions, getSearchDropdownLabels, getSearchLabel, getSelectedOptionLabelString, + getServiceOptions, + getTasksOptions, } from './AdvancedSearchUtils'; import { highlightedItemLabel, + mockGetChartsOptionsData, + mockGetChartsOptionsDataWithoutDN, + mockGetChartsOptionsDataWithoutNameDN, + mockGetColumnOptionsData, + mockGetColumnOptionsDataWithoutDN, + mockGetSchemaFieldOptionsData, + mockGetSchemaFieldOptionsDataWithoutDN, + mockGetServiceOptionData, + mockGetServiceOptionDataWithoutDN, + mockGetServiceOptionDataWithoutNameDN, + mockGetTasksOptionsData, + mockGetTasksOptionsDataWithoutDN, mockItemLabel, mockLongOptionsArray, mockOptionsArray, @@ -91,4 +109,98 @@ describe('AdvancedSearchUtils tests', () => { expect(resultSearchLabel).toBe(mockItemLabel); }); + + it('Function getServiceOptions should return displayName of the service', () => { + const resultGetServiceOptions = getServiceOptions(mockGetServiceOptionData); + + expect(resultGetServiceOptions).toBe('sample_data display'); + }); + + it('Function getServiceOptions should return name of the service if no display name present', () => { + const resultGetServiceOptions = getServiceOptions( + mockGetServiceOptionDataWithoutDN + ); + + expect(resultGetServiceOptions).toBe('sample_data'); + }); + + it('Function getServiceOptions should return text value in case not name or display name of service present', () => { + const resultGetServiceOptions = getServiceOptions( + mockGetServiceOptionDataWithoutNameDN + ); + + expect(resultGetServiceOptions).toBe('sample_data text'); + }); + + it('Function getColumnsOptions should return displayName of the column', () => { + const resultGetColumnsOptions = getColumnsOptions( + mockGetColumnOptionsData, + SearchIndex.TABLE + ); + + expect(resultGetColumnsOptions).toBe('ad_id display'); + }); + + it('Function getColumnsOptions should return name of the column if no display name present', () => { + const resultGetColumnsOptions = getColumnsOptions( + mockGetColumnOptionsDataWithoutDN, + SearchIndex.TABLE + ); + + expect(resultGetColumnsOptions).toBe('ad_id'); + }); + + it('Function getSchemaFieldOptions should return displayName of the schemaField', () => { + const resultGetSchemaFieldOptions = getSchemaFieldOptions( + mockGetSchemaFieldOptionsData + ); + + expect(resultGetSchemaFieldOptions).toBe('AddressBook display'); + }); + + it('Function getSchemaFieldOptions should return name of the schemaField if no display name present', () => { + const resultGetSchemaFieldOptions = getSchemaFieldOptions( + mockGetSchemaFieldOptionsDataWithoutDN + ); + + expect(resultGetSchemaFieldOptions).toBe('AddressBook'); + }); + + it('Function getTasksOptions should return displayName of the Task', () => { + const resultGetTasksOptionsOptions = getTasksOptions( + mockGetTasksOptionsData + ); + + expect(resultGetTasksOptionsOptions).toBe('task display'); + }); + + it('Function getTasksOptions should return name of the Task if no display name present', () => { + const resultGetTasksOptionsOptions = getTasksOptions( + mockGetTasksOptionsDataWithoutDN + ); + + expect(resultGetTasksOptionsOptions).toBe('task name'); + }); + + it('Function getChartsOptions should return displayName of the chart', () => { + const resultGetChartsOptions = getChartsOptions(mockGetChartsOptionsData); + + expect(resultGetChartsOptions).toBe('chart display'); + }); + + it('Function getChartsOptions should return name of the chart if no display name present', () => { + const resultGetChartsOptions = getChartsOptions( + mockGetChartsOptionsDataWithoutDN + ); + + expect(resultGetChartsOptions).toBe('chart name'); + }); + + it('Function getChartsOptions should return text value in case no name or display name of chart is present', () => { + const resultGetChartsOptions = getChartsOptions( + mockGetChartsOptionsDataWithoutNameDN + ); + + expect(resultGetChartsOptions).toBe('chart text'); + }); }); diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/AdvancedSearchUtils.tsx b/openmetadata-ui/src/main/resources/ui/src/utils/AdvancedSearchUtils.tsx index 13e0f8835c5..38c5ac3b4dc 100644 --- a/openmetadata-ui/src/main/resources/ui/src/utils/AdvancedSearchUtils.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/utils/AdvancedSearchUtils.tsx @@ -14,10 +14,19 @@ import Icon, { CloseCircleOutlined, PlusOutlined } from '@ant-design/icons'; import { Button, Checkbox, MenuProps, Space, Typography } from 'antd'; import ProfilePicture from 'components/common/ProfilePicture/ProfilePicture'; -import { FormattedSuggestResponseObject } from 'components/Explore/ExploreQuickFilters.interface'; import { SearchDropdownOption } from 'components/SearchDropdown/SearchDropdown.interface'; import i18next from 'i18next'; -import { isArray, isUndefined } from 'lodash'; +import { + ContainerSearchSource, + DashboardSearchSource, + ExploreSearchSource, + MlmodelSearchSource, + PipelineSearchSource, + SuggestOption, + TableSearchSource, + TopicSearchSource, +} from 'interface/search.interface'; +import { isArray, isEmpty, isUndefined } from 'lodash'; import React from 'react'; import { RenderSettings } from 'react-awesome-query-builder'; import { @@ -30,11 +39,9 @@ import { TABLE_DROPDOWN_ITEMS, TOPIC_DROPDOWN_ITEMS, } from '../constants/AdvancedSearch.constants'; - -import { AdvancedFields, EntityFields } from '../enums/AdvancedSearch.enum'; +import { AdvancedFields } from '../enums/AdvancedSearch.enum'; import { SearchIndex } from '../enums/search.enum'; -import { Dashboard } from '../generated/entity/data/dashboard'; -import { Pipeline } from '../generated/entity/data/pipeline'; +import { getEntityName } from './EntityUtils'; import SVGIcons, { Icons } from './SvgUtils'; export const getDropDownItems = (index: string) => { @@ -74,6 +81,7 @@ export const getItemLabel = (key: string) => { export const getAdvancedField = (field: string) => { switch (field) { case 'columns.name': + case 'dataModel.columns.name': return AdvancedFields.COLUMN; case 'databaseSchema.name': @@ -82,12 +90,18 @@ export const getAdvancedField = (field: string) => { case 'database.name': return AdvancedFields.DATABASE; - case 'charts.name': + case 'charts.displayName.keyword': return AdvancedFields.CHART; - case 'tasks.name': + case 'dataModels.displayName.keyword': + return AdvancedFields.DATA_MODEL; + + case 'tasks.displayName.keyword': return AdvancedFields.TASK; + case 'messageSchema.schemaFields.name': + return AdvancedFields.FIELD; + case 'service.name': return AdvancedFields.SERVICE; @@ -221,72 +235,146 @@ export const getSelectedOptionLabelString = ( } }; -export const getOptionFromDashboardSource = ( - uniqueOption: FormattedSuggestResponseObject -): SearchDropdownOption => { - const charts = (uniqueOption.source as Dashboard).charts; - const option: SearchDropdownOption = { key: '', label: '' }; +export const getChartsOptions = ( + option: SuggestOption +) => { + const chartRef = ( + option as SuggestOption + )._source.charts?.find( + (chart) => chart.displayName === option.text || chart.name === option.text + ); - if (charts) { - // As of now, the value sent by suggest API in uniqueOption.text is uncertain - // It is either from name or sometimes from displayName, - // we are checking both for now to figure out which 'Dashboard' has desired chart - const chart = charts.find( - (chart) => - chart.displayName === uniqueOption.text || - chart.name === uniqueOption.text - ); + const entityName = getEntityName(chartRef); - if (chart) { - option.key = chart.name ?? ''; - option.label = chart.displayName ?? chart.name ?? ''; - } - } - - return option; + return isEmpty(entityName) ? option.text : entityName; }; -export const getOptionFromPipelineSource = ( - uniqueOption: FormattedSuggestResponseObject -): SearchDropdownOption => { - const tasks = (uniqueOption.source as Pipeline).tasks; - const option: SearchDropdownOption = { key: '', label: '' }; +export const getDataModelOptions = ( + option: SuggestOption +) => { + const chartRef = ( + option as SuggestOption + )._source.dataModels?.find( + (dataModel) => + dataModel.displayName === option.text || dataModel.name === option.text + ); - if (tasks) { - // As of now, the value sent by suggest API in uniqueOption.text is uncertain - // It is either from name or sometimes from displayName, - // we are checking both for now to figure out which 'Pipeline' has desired task - const task = tasks.find( - (task) => - task.name === uniqueOption.text || - task.displayName === uniqueOption.text - ); + const entityName = getEntityName(chartRef); - if (task) { - option.key = task.name; - option.label = task.displayName ?? task.name; - } - } - - return option; + return isEmpty(entityName) ? option.text : entityName; }; -export const getOptionsObject = ( - key: string, - uniqueOptions: FormattedSuggestResponseObject[] -): SearchDropdownOption[] => { +export const getTasksOptions = ( + option: SuggestOption +) => { + const taskRef = ( + option as SuggestOption + )._source.tasks?.find( + (task) => task.displayName === option.text || task.name === option.text + ); + + const entityName = getEntityName(taskRef); + + return isEmpty(entityName) ? option.text : entityName; +}; + +export const getColumnsOptions = ( + option: SuggestOption, + index: SearchIndex +) => { + if (index === SearchIndex.TABLE) { + const columnRef = ( + option as SuggestOption + )._source.columns.find( + (column) => + column.displayName === option.text || column.name === option.text + ); + + const entityName = getEntityName(columnRef); + + return isEmpty(entityName) ? option.text : entityName; + } else { + const dataModel = ( + option as SuggestOption + )._source.dataModel; + const columnRef = dataModel + ? dataModel.columns.find( + (column) => + column.displayName === option.text || column.name === option.text + ) + : undefined; + + const entityName = getEntityName(columnRef); + + return isEmpty(entityName) ? option.text : entityName; + } +}; + +export const getSchemaFieldOptions = ( + option: SuggestOption +) => { + const schemaFields = ( + option as SuggestOption + )._source.messageSchema?.schemaFields; + + const schemaRef = schemaFields + ? schemaFields.find( + (field) => + field.displayName === option.text || field.name === option.text + ) + : undefined; + + const entityName = getEntityName(schemaRef); + + return isEmpty(entityName) ? option.text : entityName; +}; + +export const getServiceOptions = ( + option: SuggestOption +) => { + const service = ( + option as SuggestOption< + SearchIndex, + | TableSearchSource + | DashboardSearchSource + | PipelineSearchSource + | MlmodelSearchSource + | TopicSearchSource + > + )._source.service; + + return service + ? service.displayName ?? service.name ?? option.text + : option.text; +}; + +// Function to get the display name to show in the options for search Dropdowns +export const getOptionTextFromKey = ( + index: SearchIndex, + option: SuggestOption, + key: string +) => { switch (key) { - case EntityFields.CHART: { - return uniqueOptions.map((op) => getOptionFromDashboardSource(op)); + case 'charts.displayName.keyword': { + return getChartsOptions(option); } - case EntityFields.TASK: { - return uniqueOptions.map((op) => getOptionFromPipelineSource(op)); + case 'dataModels.displayName.keyword': { + return getDataModelOptions(option); + } + case 'tasks.displayName.keyword': { + return getTasksOptions(option); + } + case 'columns.name': { + return getColumnsOptions(option, index); + } + case 'service.name': { + return getServiceOptions(option); + } + case 'messageSchema.schemaFields.name': { + return getSchemaFieldOptions(option); } default: { - return uniqueOptions.map((op) => ({ - key: op.text, - label: op.text, - })); + return option.text; } } }; diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/mocks/AdvancedSearchUtils.mock.ts b/openmetadata-ui/src/main/resources/ui/src/utils/mocks/AdvancedSearchUtils.mock.ts index e5739ecd48c..4954ec13e65 100644 --- a/openmetadata-ui/src/main/resources/ui/src/utils/mocks/AdvancedSearchUtils.mock.ts +++ b/openmetadata-ui/src/main/resources/ui/src/utils/mocks/AdvancedSearchUtils.mock.ts @@ -11,6 +11,15 @@ * limitations under the License. */ +import { SearchIndex } from 'enums/search.enum'; +import { + DatabaseServiceType, + DataType, + TableType, +} from 'generated/entity/data/table'; +import { DataTypeTopic } from 'generated/entity/data/topic'; +import { ExploreSearchSource, SuggestOption } from 'interface/search.interface'; + export const mockOptionsArray = [ { key: 'option_1', label: 'option_1' }, { key: 'option_2', label: 'option_2' }, @@ -34,3 +43,370 @@ export const mockItemLabel = 'Aaron Warren and Aaron Warner'; export const highlightedItemLabel = 'Aaron Warren and Aaron Warner'; + +export const mockGetServiceOptionData: SuggestOption< + SearchIndex, + ExploreSearchSource +> = { + text: 'sample_data text', + _index: SearchIndex.TABLE, + _id: 'a363ad5e-14c9-47ff-8a9e-48aa7731daf5', + _source: { + id: 'a363ad5e-14c9-47ff-8a9e-48aa7731daf5', + name: 'dim.api/client', + fullyQualifiedName: 'sample_data.ecommerce_db.shopify."dim.api/client"', + description: 'This dimension table contains a row for each channel.', + version: 0.1, + updatedAt: 1680584064080, + updatedBy: 'admin', + href: 'http://localhost:8585/api/v1/tables/a363ad5e-14c9-47ff-8a9e-48aa7731daf5', + tableType: TableType.Regular, + type: '', + columns: [ + { + name: 'api_client_id', + dataType: DataType.Numeric, + dataTypeDisplay: 'numeric', + description: + 'ID of the API client that called the Shopify API. For example, the ID for the online store is 580111.', + fullyQualifiedName: + 'sample_data.ecommerce_db.shopify."dim.api/client".api_client_id', + tags: [], + ordinalPosition: 1, + customMetrics: [], + }, + { + name: 'title', + dataType: DataType.Varchar, + dataLength: 100, + dataTypeDisplay: 'varchar', + description: + 'Full name of the app or channel. For example, Point of Sale, Online Store.', + fullyQualifiedName: + 'sample_data.ecommerce_db.shopify."dim.api/client".title', + tags: [], + ordinalPosition: 2, + customMetrics: [], + }, + ], + databaseSchema: { + id: '10cb1ad0-9245-42ff-8bbc-fce4bc9a451d', + type: 'databaseSchema', + name: 'shopify', + fullyQualifiedName: 'sample_data.ecommerce_db.shopify', + description: + 'This **mock** database contains schema related to shopify sales and orders with related dimension tables.', + deleted: false, + }, + database: { + id: 'e45c1989-b970-47e9-9b25-c78938835303', + type: 'database', + name: 'ecommerce_db', + fullyQualifiedName: 'sample_data.ecommerce_db', + description: + 'This **mock** database contains schemas related to shopify sales and orders with related dimension tables.', + deleted: false, + }, + service: { + id: '6952b1c3-0518-4f61-9519-a4e39562fe69', + type: 'databaseService', + name: 'sample_data', + displayName: 'sample_data display', + fullyQualifiedName: 'sample_data', + description: 'd', + deleted: false, + }, + serviceType: DatabaseServiceType.BigQuery, + tags: [], + followers: [], + deleted: false, + displayName: 'dim.api/client', + tier: undefined, + entityType: 'table', + }, +}; + +export const mockGetServiceOptionDataWithoutDN: SuggestOption< + SearchIndex, + ExploreSearchSource +> = { + ...mockGetServiceOptionData, + _source: { + ...mockGetServiceOptionData._source, + type: '', + service: { + id: '6952b1c3-0518-4f61-9519-a4e39562fe69', + type: 'databaseService', + name: 'sample_data', + fullyQualifiedName: 'sample_data', + description: 'd', + deleted: false, + }, + }, +}; + +export const mockGetServiceOptionDataWithoutNameDN: SuggestOption< + SearchIndex, + ExploreSearchSource +> = { + ...mockGetServiceOptionData, + _source: { + ...mockGetServiceOptionData._source, + type: '', + service: { + id: '6952b1c3-0518-4f61-9519-a4e39562fe69', + type: 'databaseService', + fullyQualifiedName: 'sample_data', + description: 'd', + deleted: false, + }, + }, +}; + +export const mockGetColumnOptionsData: SuggestOption< + SearchIndex, + ExploreSearchSource +> = { + ...mockGetServiceOptionData, + text: 'ad_id', + _source: { + ...mockGetServiceOptionData, + type: '', + entityType: '', + id: '', + name: '', + columns: [ + { + name: 'ad_id', + displayName: 'ad_id display', + dataType: DataType.Numeric, + dataTypeDisplay: 'numeric', + description: + 'ID of the API client that called the Shopify API. For example, the ID for the online store is 580111.', + fullyQualifiedName: + 'sample_data.ecommerce_db.shopify."dim.api/client".api_client_id', + tags: [], + ordinalPosition: 1, + customMetrics: [], + }, + ], + }, +}; + +export const mockGetColumnOptionsDataWithoutDN: SuggestOption< + SearchIndex, + ExploreSearchSource +> = { + ...mockGetServiceOptionData, + text: 'ad_id', + _source: { + ...mockGetServiceOptionData, + type: '', + entityType: '', + id: '', + name: '', + columns: [ + { + name: 'ad_id', + dataType: DataType.Numeric, + dataTypeDisplay: 'numeric', + description: + 'ID of the API client that called the Shopify API. For example, the ID for the online store is 580111.', + fullyQualifiedName: + 'sample_data.ecommerce_db.shopify."dim.api/client".api_client_id', + tags: [], + ordinalPosition: 1, + customMetrics: [], + }, + ], + }, +}; + +export const mockGetSchemaFieldOptionsData: SuggestOption< + SearchIndex, + ExploreSearchSource +> = { + ...mockGetServiceOptionData, + text: 'AddressBook display', + _source: { + ...mockGetServiceOptionData, + type: '', + partitions: 2, + entityType: '', + id: '', + name: '', + service: { + id: '2', + type: '', + }, + messageSchema: { + schemaFields: [ + { + name: 'AddressBook', + displayName: 'AddressBook display', + dataType: DataTypeTopic.Array, + }, + ], + }, + }, +}; + +export const mockGetSchemaFieldOptionsDataWithoutDN: SuggestOption< + SearchIndex, + ExploreSearchSource +> = { + ...mockGetServiceOptionData, + text: 'AddressBook', + _source: { + ...mockGetServiceOptionData, + type: '', + partitions: 2, + entityType: '', + id: '', + name: '', + service: { + id: '2', + type: '', + }, + messageSchema: { + schemaFields: [ + { + name: 'AddressBook', + dataType: DataTypeTopic.Array, + }, + ], + }, + }, +}; + +export const mockGetTasksOptionsData: SuggestOption< + SearchIndex, + ExploreSearchSource +> = { + ...mockGetServiceOptionData, + text: 'task display', + _source: { + ...mockGetServiceOptionData, + type: '', + partitions: 2, + entityType: '', + id: '', + name: '', + service: { + id: '2', + type: '', + }, + tasks: [ + { + name: 'task name', + displayName: 'task display', + }, + ], + }, +}; + +export const mockGetTasksOptionsDataWithoutDN: SuggestOption< + SearchIndex, + ExploreSearchSource +> = { + ...mockGetServiceOptionData, + text: 'task name', + _source: { + ...mockGetServiceOptionData, + type: '', + partitions: 2, + entityType: '', + id: '', + name: '', + service: { + id: '2', + type: '', + }, + tasks: [ + { + name: 'task name', + }, + ], + }, +}; + +export const mockGetChartsOptionsData: SuggestOption< + SearchIndex, + ExploreSearchSource +> = { + ...mockGetServiceOptionData, + text: 'chart display', + _source: { + ...mockGetServiceOptionData, + type: '', + partitions: 2, + entityType: '', + id: '', + name: '', + service: { + id: '2', + type: '', + }, + charts: [ + { + id: '3', + name: 'chart name', + displayName: 'chart display', + type: '', + }, + ], + }, +}; + +export const mockGetChartsOptionsDataWithoutDN: SuggestOption< + SearchIndex, + ExploreSearchSource +> = { + ...mockGetServiceOptionData, + text: 'chart name', + _source: { + ...mockGetServiceOptionData, + type: '', + partitions: 2, + entityType: '', + id: '', + name: '', + service: { + id: '2', + type: '', + }, + charts: [ + { + id: '3', + name: 'chart name', + type: '', + }, + ], + }, +}; + +export const mockGetChartsOptionsDataWithoutNameDN: SuggestOption< + SearchIndex, + ExploreSearchSource +> = { + ...mockGetServiceOptionData, + text: 'chart text', + _source: { + ...mockGetServiceOptionData, + type: '', + partitions: 2, + entityType: '', + id: '', + name: '', + service: { + id: '2', + type: '', + }, + charts: [ + { + id: '3', + type: '', + }, + ], + }, +};