From 09755263c85e2c708d4fbe001544c2ea5b67819a Mon Sep 17 00:00:00 2001 From: Josh Eilers Date: Wed, 9 Aug 2023 14:24:24 -0700 Subject: [PATCH 01/17] feat(search): combine autocomplete siblings --- .../src/app/entity/shared/siblingUtils.ts | 1 + .../src/app/home/HomePageHeader.tsx | 22 ++++---------- .../src/app/search/SearchablePage.tsx | 29 +++++-------------- .../useSuggestionsWithCombinedSiblings.ts | 23 +++++++++++++++ 4 files changed, 37 insertions(+), 38 deletions(-) create mode 100644 datahub-web-react/src/app/search/useSuggestionsWithCombinedSiblings.ts diff --git a/datahub-web-react/src/app/entity/shared/siblingUtils.ts b/datahub-web-react/src/app/entity/shared/siblingUtils.ts index 2cad28d754a800..af961ae404f0fe 100644 --- a/datahub-web-react/src/app/entity/shared/siblingUtils.ts +++ b/datahub-web-react/src/app/entity/shared/siblingUtils.ts @@ -246,6 +246,7 @@ export function combineSiblingsInSearchResults( } combinedResults.push(combinedResult); }); + console.log({ combinedResults }); return combinedResults; } diff --git a/datahub-web-react/src/app/home/HomePageHeader.tsx b/datahub-web-react/src/app/home/HomePageHeader.tsx index def413e13213fa..a2002bd62e0bee 100644 --- a/datahub-web-react/src/app/home/HomePageHeader.tsx +++ b/datahub-web-react/src/app/home/HomePageHeader.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useMemo, useState } from 'react'; +import React, { useMemo } from 'react'; import { useHistory } from 'react-router'; import { Typography, Image, Row, Button, Tag } from 'antd'; import styled, { useTheme } from 'styled-components/macro'; @@ -7,11 +7,7 @@ import { ManageAccount } from '../shared/ManageAccount'; import { useEntityRegistry } from '../useEntityRegistry'; import { navigateToSearchUrl } from '../search/utils/navigateToSearchUrl'; import { SearchBar } from '../search/SearchBar'; -import { - GetAutoCompleteMultipleResultsQuery, - useGetAutoCompleteMultipleResultsLazyQuery, - useGetSearchResultsForMultipleQuery, -} from '../../graphql/search.generated'; +import { useGetSearchResultsForMultipleQuery } from '../../graphql/search.generated'; import { EntityType, FacetFilterInput } from '../../types.generated'; import analytics, { EventType } from '../analytics'; import { HeaderLinks } from '../shared/admin/HeaderLinks'; @@ -24,6 +20,7 @@ import { getAutoCompleteInputFromQuickFilter } from '../search/utils/filterUtils import { useUserContext } from '../context/useUserContext'; import AcrylDemoBanner from './AcrylDemoBanner'; import DemoButton from '../entity/shared/components/styled/DemoButton'; +import useSuggestionsWithCombinedSiblings from '../search/useSuggestionsWithCombinedSiblings'; const Background = styled.div` width: 100%; @@ -143,23 +140,16 @@ function sortRandom() { export const HomePageHeader = () => { const history = useHistory(); const entityRegistry = useEntityRegistry(); - const [getAutoCompleteResultsForMultiple, { data: suggestionsData }] = useGetAutoCompleteMultipleResultsLazyQuery(); const userContext = useUserContext(); const themeConfig = useTheme(); const appConfig = useAppConfig(); - const [newSuggestionData, setNewSuggestionData] = useState(); + const [getSuggestions, { suggestions }] = useSuggestionsWithCombinedSiblings(); const { selectedQuickFilter } = useQuickFiltersContext(); const showAcrylInfo = useIsShowAcrylInfoEnabled(); const { user } = userContext; const viewUrn = userContext.localState?.selectedViewUrn; const viewsEnabled = appConfig.config?.viewsConfig?.enabled || false; - useEffect(() => { - if (suggestionsData !== undefined) { - setNewSuggestionData(suggestionsData); - } - }, [suggestionsData]); - const onSearch = (query: string, type?: EntityType, filters?: FacetFilterInput[]) => { analytics.event({ type: EventType.HomePageSearchEvent, @@ -178,7 +168,7 @@ export const HomePageHeader = () => { const onAutoComplete = (query: string) => { if (query && query.trim() !== '') { - getAutoCompleteResultsForMultiple({ + getSuggestions({ variables: { input: { query, @@ -267,7 +257,7 @@ export const HomePageHeader = () => { const themeConfig = useTheme(); const { selectedQuickFilter } = useQuickFiltersContext(); - const [getAutoCompleteResults, { data: suggestionsData }] = useGetAutoCompleteMultipleResultsLazyQuery(); + const [getSuggestions, { suggestions }] = useSuggestionsWithCombinedSiblings(); const userContext = useUserContext(); - const [newSuggestionData, setNewSuggestionData] = useState(); const { user } = userContext; const viewUrn = userContext.localState?.selectedViewUrn; - useEffect(() => { - if (suggestionsData !== undefined) { - setNewSuggestionData(suggestionsData); - } - }, [suggestionsData]); - const search = (query: string, type?: EntityType, quickFilters?: FacetFilterInput[]) => { analytics.event({ type: EventType.SearchEvent, @@ -92,7 +82,7 @@ export const SearchablePage = ({ onSearch, onAutoComplete, children }: Props) => const autoComplete = (query: string) => { if (query && query.trim() !== '') { - getAutoCompleteResults({ + getSuggestions({ variables: { input: { query, @@ -107,7 +97,7 @@ export const SearchablePage = ({ onSearch, onAutoComplete, children }: Props) => // Load correct autocomplete results on initial page load. useEffect(() => { if (currentQuery && currentQuery.trim() !== '') { - getAutoCompleteResults({ + getSuggestions({ variables: { input: { query: currentQuery, @@ -116,19 +106,14 @@ export const SearchablePage = ({ onSearch, onAutoComplete, children }: Props) => }, }); } - }, [currentQuery, getAutoCompleteResults, viewUrn]); + }, [currentQuery, getSuggestions, viewUrn]); return ( <> { + const [getSuggestions, { data }] = useGetAutoCompleteMultipleResultsLazyQuery(); + const [suggestions, setSuggestions] = useState>(); + + useEffect(() => { + if (data === undefined) return; + const newSuggestions = data.autoCompleteForMultiple?.suggestions; + // todo - for each suggestion.entities, de-dupe those + // ultimately constructing a new complex suggestions object where each type has a new list of CombinedSearchResult? + const entities = newSuggestions?.map((suggestion) => suggestion.entities); + // const combinedResults = combineSiblingsInSearchResults(newSuggestions); + // setSuggestions(combinedResults); + setSuggestions(newSuggestions); + }, [data]); + + return [getSuggestions, { suggestions } as const] as const; +}; + +export default useSuggestionsWithCombinedSiblings; From 0116d1543921faa042fdc0efc627cb72a7967620 Mon Sep 17 00:00:00 2001 From: Josh Eilers Date: Wed, 9 Aug 2023 16:14:41 -0700 Subject: [PATCH 02/17] really rough rendering of siblings in the autocomplete --- .../src/app/entity/shared/siblingUtils.ts | 76 ++++++++++++++++++- .../src/app/home/HomePageHeader.tsx | 23 ++++-- .../src/app/search/SearchBar.tsx | 43 ++++++----- .../src/app/search/SearchHeader.tsx | 1 + .../src/app/search/SearchablePage.tsx | 29 +++++-- .../search/autoComplete/AutoCompleteItem.tsx | 5 +- .../useSuggestionsWithCombinedSiblings.ts | 23 ------ datahub-web-react/src/graphql/search.graphql | 20 +++++ 8 files changed, 161 insertions(+), 59 deletions(-) delete mode 100644 datahub-web-react/src/app/search/useSuggestionsWithCombinedSiblings.ts diff --git a/datahub-web-react/src/app/entity/shared/siblingUtils.ts b/datahub-web-react/src/app/entity/shared/siblingUtils.ts index af961ae404f0fe..771ef63d2a4a99 100644 --- a/datahub-web-react/src/app/entity/shared/siblingUtils.ts +++ b/datahub-web-react/src/app/entity/shared/siblingUtils.ts @@ -2,7 +2,15 @@ import merge from 'deepmerge'; import { unionBy, keyBy, values } from 'lodash'; import { useLocation } from 'react-router-dom'; import * as QueryString from 'query-string'; -import { Dataset, Entity, MatchedField, Maybe, SiblingProperties } from '../../../types.generated'; +import { + AutoCompleteResultForEntity, + Dataset, + Entity, + EntityType, + MatchedField, + Maybe, + SiblingProperties, +} from '../../../types.generated'; export function stripSiblingsFromEntity(entity: any) { return { @@ -216,7 +224,7 @@ export function combineSiblingsInSearchResults( }[] | undefined, ) { - const combinedResults: CombinedSearchResult[] | undefined = []; + const combinedResults: CombinedSearchResult[] = []; const siblingsToPair: Record = {}; // set sibling associations @@ -246,10 +254,72 @@ export function combineSiblingsInSearchResults( } combinedResults.push(combinedResult); }); - console.log({ combinedResults }); return combinedResults; } + +type CombinedAutoCompleteEntityResult = { + entity: Entity; + matchedEntities?: Entity[]; +}; + +export type CombinedSuggestion = { + type: EntityType; + combinedEntities: Array; + suggestions?: AutoCompleteResultForEntity['suggestions']; +}; + +// todo - combine logic +export function combineSiblingsInAutoComplete( + autoCompleteResultForEntity: AutoCompleteResultForEntity, + { combine = true } = {}, +) { + const combinedEntities: CombinedAutoCompleteEntityResult[] = []; + const siblingsToPair: Record = {}; + + // set sibling associations + autoCompleteResultForEntity.entities.forEach((entityReadOnly) => { + if (!combine) { + combinedEntities.push({ entity: entityReadOnly }); + return; + } + + if (entityReadOnly.urn in siblingsToPair) { + // filter from repeating + // const siblingsCombinedResult = siblingsToPair[result.entity.urn]; + // siblingsCombinedResult.matchedEntities?.push(result.entity); + return; + } + + const combinedResult: CombinedAutoCompleteEntityResult = { entity: entityReadOnly }; + // todo - get rid of this `any` cast both here and in the search results (previous tech debt) + const entity: any = entityReadOnly; + const siblingUrns = entity?.siblings?.siblings?.map((sibling) => sibling.urn) || []; + if (siblingUrns.length > 0) { + combinedResult.matchedEntities = entity.siblings.isPrimary + ? [stripSiblingsFromEntity(entity), ...entity.siblings.siblings] + : [...entity.siblings.siblings, stripSiblingsFromEntity(entity)]; + + combinedResult.matchedEntities = combinedResult.matchedEntities.filter( + (resultToFilter) => (resultToFilter as Dataset).exists, + ); + + siblingUrns.forEach((urn) => { + siblingsToPair[urn] = combinedResult; + }); + } + combinedEntities.push(combinedResult); + }); + + const combinedSuggestion: CombinedSuggestion = { + type: autoCompleteResultForEntity.type, + suggestions: autoCompleteResultForEntity.suggestions, + combinedEntities, + }; + + return combinedSuggestion; +} + // used to determine whether sibling entities should be shown merged or not export const SEPARATE_SIBLINGS_URL_PARAM = 'separate_siblings'; diff --git a/datahub-web-react/src/app/home/HomePageHeader.tsx b/datahub-web-react/src/app/home/HomePageHeader.tsx index a2002bd62e0bee..5919d2dbf5b7ee 100644 --- a/datahub-web-react/src/app/home/HomePageHeader.tsx +++ b/datahub-web-react/src/app/home/HomePageHeader.tsx @@ -1,4 +1,4 @@ -import React, { useMemo } from 'react'; +import React, { useEffect, useMemo, useState } from 'react'; import { useHistory } from 'react-router'; import { Typography, Image, Row, Button, Tag } from 'antd'; import styled, { useTheme } from 'styled-components/macro'; @@ -7,7 +7,11 @@ import { ManageAccount } from '../shared/ManageAccount'; import { useEntityRegistry } from '../useEntityRegistry'; import { navigateToSearchUrl } from '../search/utils/navigateToSearchUrl'; import { SearchBar } from '../search/SearchBar'; -import { useGetSearchResultsForMultipleQuery } from '../../graphql/search.generated'; +import { + GetAutoCompleteMultipleResultsQuery, + useGetAutoCompleteMultipleResultsLazyQuery, + useGetSearchResultsForMultipleQuery, +} from '../../graphql/search.generated'; import { EntityType, FacetFilterInput } from '../../types.generated'; import analytics, { EventType } from '../analytics'; import { HeaderLinks } from '../shared/admin/HeaderLinks'; @@ -20,7 +24,6 @@ import { getAutoCompleteInputFromQuickFilter } from '../search/utils/filterUtils import { useUserContext } from '../context/useUserContext'; import AcrylDemoBanner from './AcrylDemoBanner'; import DemoButton from '../entity/shared/components/styled/DemoButton'; -import useSuggestionsWithCombinedSiblings from '../search/useSuggestionsWithCombinedSiblings'; const Background = styled.div` width: 100%; @@ -140,16 +143,23 @@ function sortRandom() { export const HomePageHeader = () => { const history = useHistory(); const entityRegistry = useEntityRegistry(); + const [getAutoCompleteResultsForMultiple, { data: suggestionsData }] = useGetAutoCompleteMultipleResultsLazyQuery(); const userContext = useUserContext(); const themeConfig = useTheme(); const appConfig = useAppConfig(); - const [getSuggestions, { suggestions }] = useSuggestionsWithCombinedSiblings(); + const [newSuggestionData, setNewSuggestionData] = useState(); const { selectedQuickFilter } = useQuickFiltersContext(); const showAcrylInfo = useIsShowAcrylInfoEnabled(); const { user } = userContext; const viewUrn = userContext.localState?.selectedViewUrn; const viewsEnabled = appConfig.config?.viewsConfig?.enabled || false; + useEffect(() => { + if (suggestionsData !== undefined) { + setNewSuggestionData(suggestionsData); + } + }, [suggestionsData]); + const onSearch = (query: string, type?: EntityType, filters?: FacetFilterInput[]) => { analytics.event({ type: EventType.HomePageSearchEvent, @@ -168,7 +178,7 @@ export const HomePageHeader = () => { const onAutoComplete = (query: string) => { if (query && query.trim() !== '') { - getSuggestions({ + getAutoCompleteResultsForMultiple({ variables: { input: { query, @@ -257,12 +267,13 @@ export const HomePageHeader = () => { {searchResultsToShow && searchResultsToShow.length > 0 && ( diff --git a/datahub-web-react/src/app/search/SearchBar.tsx b/datahub-web-react/src/app/search/SearchBar.tsx index 97be6ab6b65e3b..4691107625563f 100644 --- a/datahub-web-react/src/app/search/SearchBar.tsx +++ b/datahub-web-react/src/app/search/SearchBar.tsx @@ -3,7 +3,7 @@ import { Input, AutoComplete, Button } from 'antd'; import { CloseCircleFilled, SearchOutlined } from '@ant-design/icons'; import styled from 'styled-components/macro'; import { useHistory } from 'react-router'; -import { AutoCompleteResultForEntity, Entity, EntityType, FacetFilterInput, ScenarioType } from '../../types.generated'; +import { AutoCompleteResultForEntity, EntityType, FacetFilterInput, ScenarioType } from '../../types.generated'; import EntityRegistry from '../entity/EntityRegistry'; import filterSearchQuery from './utils/filterSearchQuery'; import { ANTD_GRAY, ANTD_GRAY_V2 } from '../entity/shared/constants'; @@ -23,6 +23,7 @@ import { navigateToSearchUrl } from './utils/navigateToSearchUrl'; import { getQuickFilterDetails } from './autoComplete/quickFilters/utils'; import ViewAllSearchItem from './ViewAllSearchItem'; import { ViewSelect } from '../entity/view/select/ViewSelect'; +import { combineSiblingsInAutoComplete } from '../entity/shared/siblingUtils'; const StyledAutoComplete = styled(AutoComplete)` width: 100%; @@ -88,15 +89,6 @@ const QUICK_FILTER_AUTO_COMPLETE_OPTION = { ], }; -const renderItem = (query: string, entity: Entity) => { - return { - value: entity.urn, - label: , - type: entity.type, - style: { padding: '12px 12px 12px 16px' }, - }; -}; - const renderRecommendedQuery = (query: string) => { return { value: query, @@ -123,6 +115,7 @@ interface Props { hideRecommendations?: boolean; showQuickFilters?: boolean; viewsEnabled?: boolean; + combineSiblings?: boolean; setIsSearchBarFocused?: (isSearchBarFocused: boolean) => void; onFocus?: () => void; onBlur?: () => void; @@ -149,6 +142,7 @@ export const SearchBar = ({ hideRecommendations, showQuickFilters, viewsEnabled = false, + combineSiblings = false, setIsSearchBarFocused, onFocus, onBlur, @@ -227,14 +221,27 @@ export const SearchBar = ({ ]; }, [showQuickFilters, suggestions.length, effectiveQuery, selectedQuickFilter, entityRegistry]); - const autoCompleteEntityOptions = useMemo( - () => - suggestions.map((entity: AutoCompleteResultForEntity) => ({ - label: , - options: [...entity.entities.map((e: Entity) => renderItem(effectiveQuery, e))], - })), - [effectiveQuery, suggestions], - ); + const autoCompleteEntityOptions = useMemo(() => { + // todo - we need to actually render the other sibling / icon / data into the autocomplete + return suggestions.map((suggestion: AutoCompleteResultForEntity) => { + const combinedSuggestion = combineSiblingsInAutoComplete(suggestion, { combine: combineSiblings }); + return { + label: , + options: combinedSuggestion.combinedEntities.map((combinedEntity) => ({ + value: combinedEntity.entity.urn, + label: ( + + ), + type: combinedEntity.entity.type, + style: { padding: '12px 12px 12px 16px' }, + })), + }; + }); + }, [combineSiblings, effectiveQuery, suggestions]); const previousSelectedQuickFilterValue = usePrevious(selectedQuickFilter?.value); useEffect(() => { diff --git a/datahub-web-react/src/app/search/SearchHeader.tsx b/datahub-web-react/src/app/search/SearchHeader.tsx index 1cdd82c8216e17..74bc562e275d11 100644 --- a/datahub-web-react/src/app/search/SearchHeader.tsx +++ b/datahub-web-react/src/app/search/SearchHeader.tsx @@ -104,6 +104,7 @@ export const SearchHeader = ({ entityRegistry={entityRegistry} setIsSearchBarFocused={setIsSearchBarFocused} viewsEnabled={viewsEnabled} + combineSiblings fixAutoComplete showQuickFilters /> diff --git a/datahub-web-react/src/app/search/SearchablePage.tsx b/datahub-web-react/src/app/search/SearchablePage.tsx index f79447e6637179..489687050c749d 100644 --- a/datahub-web-react/src/app/search/SearchablePage.tsx +++ b/datahub-web-react/src/app/search/SearchablePage.tsx @@ -1,10 +1,14 @@ -import React, { useEffect } from 'react'; +import React, { useEffect, useState } from 'react'; import { useHistory, useLocation } from 'react-router'; import * as QueryString from 'query-string'; import { useTheme } from 'styled-components'; import { SearchHeader } from './SearchHeader'; import { useEntityRegistry } from '../useEntityRegistry'; import { EntityType, FacetFilterInput } from '../../types.generated'; +import { + GetAutoCompleteMultipleResultsQuery, + useGetAutoCompleteMultipleResultsLazyQuery, +} from '../../graphql/search.generated'; import { navigateToSearchUrl } from './utils/navigateToSearchUrl'; import analytics, { EventType } from '../analytics'; import useFilters from './utils/useFilters'; @@ -12,7 +16,6 @@ import { PageRoutes } from '../../conf/Global'; import { getAutoCompleteInputFromQuickFilter } from './utils/filterUtils'; import { useQuickFiltersContext } from '../../providers/QuickFiltersContext'; import { useUserContext } from '../context/useUserContext'; -import useSuggestionsWithCombinedSiblings from './useSuggestionsWithCombinedSiblings'; import { useSelectedSortOption } from './context/SearchContext'; const styles = { @@ -57,11 +60,18 @@ export const SearchablePage = ({ onSearch, onAutoComplete, children }: Props) => const themeConfig = useTheme(); const { selectedQuickFilter } = useQuickFiltersContext(); - const [getSuggestions, { suggestions }] = useSuggestionsWithCombinedSiblings(); + const [getAutoCompleteResults, { data: suggestionsData }] = useGetAutoCompleteMultipleResultsLazyQuery(); const userContext = useUserContext(); + const [newSuggestionData, setNewSuggestionData] = useState(); const { user } = userContext; const viewUrn = userContext.localState?.selectedViewUrn; + useEffect(() => { + if (suggestionsData !== undefined) { + setNewSuggestionData(suggestionsData); + } + }, [suggestionsData]); + const search = (query: string, type?: EntityType, quickFilters?: FacetFilterInput[]) => { analytics.event({ type: EventType.SearchEvent, @@ -85,7 +95,7 @@ export const SearchablePage = ({ onSearch, onAutoComplete, children }: Props) => const autoComplete = (query: string) => { if (query && query.trim() !== '') { - getSuggestions({ + getAutoCompleteResults({ variables: { input: { query, @@ -100,7 +110,7 @@ export const SearchablePage = ({ onSearch, onAutoComplete, children }: Props) => // Load correct autocomplete results on initial page load. useEffect(() => { if (currentQuery && currentQuery.trim() !== '') { - getSuggestions({ + getAutoCompleteResults({ variables: { input: { query: currentQuery, @@ -109,14 +119,19 @@ export const SearchablePage = ({ onSearch, onAutoComplete, children }: Props) => }, }); } - }, [currentQuery, getSuggestions, viewUrn]); + }, [currentQuery, getAutoCompleteResults, viewUrn]); return ( <> ; } -export default function AutoCompleteItem({ query, entity }: Props) { +export default function AutoCompleteItem({ query, entity, siblings }: Props) { const entityRegistry = useEntityRegistry(); const displayTooltip = getShouldDisplayTooltip(entity, entityRegistry); let componentToRender: React.ReactNode = null; @@ -46,7 +47,7 @@ export default function AutoCompleteItem({ query, entity }: Props) { color="rgba(0, 0, 0, 0.9)" > - {componentToRender} + {componentToRender} {siblings?.map((sibling) => sibling.urn)} ); diff --git a/datahub-web-react/src/app/search/useSuggestionsWithCombinedSiblings.ts b/datahub-web-react/src/app/search/useSuggestionsWithCombinedSiblings.ts deleted file mode 100644 index 59ab494a6c8aa2..00000000000000 --- a/datahub-web-react/src/app/search/useSuggestionsWithCombinedSiblings.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { useEffect, useState } from 'react'; -import { useGetAutoCompleteMultipleResultsLazyQuery } from '../../graphql/search.generated'; -// import { CombinedSearchResult, combineSiblingsInSearchResults } from '../entity/shared/siblingUtils'; - -const useSuggestionsWithCombinedSiblings = () => { - const [getSuggestions, { data }] = useGetAutoCompleteMultipleResultsLazyQuery(); - const [suggestions, setSuggestions] = useState>(); - - useEffect(() => { - if (data === undefined) return; - const newSuggestions = data.autoCompleteForMultiple?.suggestions; - // todo - for each suggestion.entities, de-dupe those - // ultimately constructing a new complex suggestions object where each type has a new list of CombinedSearchResult? - const entities = newSuggestions?.map((suggestion) => suggestion.entities); - // const combinedResults = combineSiblingsInSearchResults(newSuggestions); - // setSuggestions(combinedResults); - setSuggestions(newSuggestions); - }, [data]); - - return [getSuggestions, { suggestions } as const] as const; -}; - -export default useSuggestionsWithCombinedSiblings; diff --git a/datahub-web-react/src/graphql/search.graphql b/datahub-web-react/src/graphql/search.graphql index f18b0717053939..4c4ad0a4c3bab0 100644 --- a/datahub-web-react/src/graphql/search.graphql +++ b/datahub-web-react/src/graphql/search.graphql @@ -19,6 +19,26 @@ fragment autoCompleteFields on Entity { subTypes { typeNames } + siblings { + isPrimary + siblings { + urn + type + ... on Dataset { + exists + platform { + ...platformFields + } + name + properties { + name + description + qualifiedName + externalUrl + } + } + } + } ...datasetStatsFields } ... on CorpUser { From 4aa73e1788ea88dba90b1a3e3fc25755387d2248 Mon Sep 17 00:00:00 2001 From: Josh Eilers Date: Thu, 10 Aug 2023 09:10:03 -0700 Subject: [PATCH 03/17] icons --- .../src/app/preview/DefaultPreviewCard.tsx | 1 - .../src/app/search/SearchBar.tsx | 2 + .../autoComplete/AutoCompleteEntity.tsx | 39 +++++++++------ .../autoComplete/AutoCompleteEntityIcon.tsx | 49 +++++++++++++++++++ .../search/autoComplete/AutoCompleteItem.tsx | 11 ++++- 5 files changed, 83 insertions(+), 19 deletions(-) create mode 100644 datahub-web-react/src/app/search/autoComplete/AutoCompleteEntityIcon.tsx diff --git a/datahub-web-react/src/app/preview/DefaultPreviewCard.tsx b/datahub-web-react/src/app/preview/DefaultPreviewCard.tsx index 0a7d16ade0ac00..e73127cc65b9e4 100644 --- a/datahub-web-react/src/app/preview/DefaultPreviewCard.tsx +++ b/datahub-web-react/src/app/preview/DefaultPreviewCard.tsx @@ -304,7 +304,6 @@ export default function DefaultPreviewCard({ /> )} - {degree !== undefined && degree !== null && ( diff --git a/datahub-web-react/src/app/search/autoComplete/AutoCompleteEntity.tsx b/datahub-web-react/src/app/search/autoComplete/AutoCompleteEntity.tsx index 1942497057304a..34dbb7e80b0c8b 100644 --- a/datahub-web-react/src/app/search/autoComplete/AutoCompleteEntity.tsx +++ b/datahub-web-react/src/app/search/autoComplete/AutoCompleteEntity.tsx @@ -1,14 +1,13 @@ -import { Image, Typography } from 'antd'; +import { Typography } from 'antd'; import React from 'react'; import styled from 'styled-components/macro'; import { Entity } from '../../../types.generated'; import { useEntityRegistry } from '../../useEntityRegistry'; -import { getPlatformName } from '../../entity/shared/utils'; -import { IconStyleType } from '../../entity/Entity'; import { getAutoCompleteEntityText } from './utils'; import { SuggestionText } from './AutoCompleteUser'; import ParentContainers from './ParentContainers'; import { ANTD_GRAY } from '../../entity/shared/constants'; +import AutoCompleteEntityIcon from './AutoCompleteEntityIcon'; const AutoCompleteEntityWrapper = styled.div` display: flex; @@ -17,12 +16,9 @@ const AutoCompleteEntityWrapper = styled.div` align-items: center; `; -const PreviewImage = styled(Image)` - height: 22px; - width: 22px; - width: auto; - object-fit: contain; - background-color: transparent; +const IconsContainer = styled.div` + display: flex; + flex-direction: column; `; const ContentWrapper = styled.div` @@ -44,28 +40,39 @@ const Subtype = styled.span` interface Props { query: string; entity: Entity; + siblings?: Array; hasParentTooltip: boolean; } -export default function AutoCompleteEntity({ query, entity, hasParentTooltip }: Props) { +// todo - this is mostly working well, but we really need to grab the entity details (path etc) from the bigquery one +// check out the search results de-dupe and see if there's something going on here that lets it pick the bq stuff + +// todo - maybe for some reason the dbt being the primary is throwing things off here? + +// todo - look at how Chris did the search result stuff, maybe we have some extra de-dupe step somewhere? +export default function AutoCompleteEntity({ query, entity, siblings, hasParentTooltip }: Props) { const entityRegistry = useEntityRegistry(); const genericEntityProps = entityRegistry.getGenericEntityProperties(entity.type, entity); - const platformName = getPlatformName(genericEntityProps); - const platformLogoUrl = genericEntityProps?.platform?.properties?.logoUrl; const displayName = entityRegistry.getDisplayName(entity.type, entity); - const icon = - (platformLogoUrl && ) || - entityRegistry.getIcon(entity.type, 12, IconStyleType.ACCENT); + + const iconScale = siblings?.length ? 0.66 : 1; + const { matchedText, unmatchedText } = getAutoCompleteEntityText(displayName, query); const parentContainers = genericEntityProps?.parentContainers?.containers || []; // Need to reverse parentContainers since it returns direct parent first. const orderedParentContainers = [...parentContainers].reverse(); const subtype = genericEntityProps?.subTypes?.typeNames?.[0]; + const entitiesForIconRendering = [entity, ...(siblings ?? [])]; + return ( - {icon} + + {entitiesForIconRendering.map((renderableIcon) => ( + + ))} + { + const entityRegistry = useEntityRegistry(); + + const iconFontSize = Math.floor(DEFAULT_ICON_SIZE * scale); + const previewImageSize = Math.floor(DEFAULT_PREVIEW_IMAGE_SIZE * scale); + + const genericEntityParops = entityRegistry.getGenericEntityProperties(entity.type, entity); + const platformLogoUrl = genericEntityParops?.platform?.properties?.logoUrl; + const platformName = getPlatformName(genericEntityParops); + const icon = + (platformLogoUrl && ( + + )) || + entityRegistry.getIcon(entity.type, iconFontSize, IconStyleType.ACCENT); + + return icon; +}; + +export default AutoCompleteEntityIcon; diff --git a/datahub-web-react/src/app/search/autoComplete/AutoCompleteItem.tsx b/datahub-web-react/src/app/search/autoComplete/AutoCompleteItem.tsx index be834275ac55a9..7d29342b1f4c37 100644 --- a/datahub-web-react/src/app/search/autoComplete/AutoCompleteItem.tsx +++ b/datahub-web-react/src/app/search/autoComplete/AutoCompleteItem.tsx @@ -34,7 +34,14 @@ export default function AutoCompleteItem({ query, entity, siblings }: Props) { componentToRender = ; break; default: - componentToRender = ; + componentToRender = ( + + ); break; } @@ -47,7 +54,7 @@ export default function AutoCompleteItem({ query, entity, siblings }: Props) { color="rgba(0, 0, 0, 0.9)" > - {componentToRender} {siblings?.map((sibling) => sibling.urn)} + {componentToRender} ); From e72aaeade76093579bdc30bdcb5a4e6a1f0b782e Mon Sep 17 00:00:00 2001 From: Josh Eilers Date: Thu, 10 Aug 2023 13:25:03 -0700 Subject: [PATCH 04/17] combine sibling helper functions --- .../src/app/entity/shared/siblingUtils.ts | 107 +++++++----------- .../src/app/search/SearchBar.tsx | 1 - .../src/app/search/SearchResults.tsx | 1 + .../autoComplete/AutoCompleteEntity.tsx | 29 +++-- .../autoComplete/AutoCompleteEntityIcon.tsx | 6 +- datahub-web-react/src/graphql/search.graphql | 4 + 6 files changed, 61 insertions(+), 87 deletions(-) diff --git a/datahub-web-react/src/app/entity/shared/siblingUtils.ts b/datahub-web-react/src/app/entity/shared/siblingUtils.ts index 771ef63d2a4a99..64d60218cc662a 100644 --- a/datahub-web-react/src/app/entity/shared/siblingUtils.ts +++ b/datahub-web-react/src/app/entity/shared/siblingUtils.ts @@ -210,35 +210,32 @@ export const combineEntityDataWithSiblings = (baseEntity: T): T => { return { [baseEntityKey]: combinedBaseEntity } as unknown as T; }; -export type CombinedSearchResult = { +type CombinedEntityResult = { entity: Entity; - matchedFields: MatchedField[]; - matchedEntities?: Entity[]; + matchedEntities?: Array; }; -export function combineSiblingsInSearchResults( - results: - | { - entity: Entity; - matchedFields: MatchedField[]; - }[] - | undefined, -) { - const combinedResults: CombinedSearchResult[] = []; - const siblingsToPair: Record = {}; +function combineSiblingEntities(entities?: Array, { combine = true } = {}): Array { + const combinedResults: CombinedEntityResult[] = []; + const siblingsToPair: Set = new Set(); - // set sibling associations - results?.forEach((result) => { - if (result.entity.urn in siblingsToPair) { + entities?.forEach((entityReadOnly) => { + if (!combine) { + combinedResults.push({ entity: entityReadOnly }); + return; + } + + if (siblingsToPair.has(entityReadOnly.urn)) { // filter from repeating // const siblingsCombinedResult = siblingsToPair[result.entity.urn]; // siblingsCombinedResult.matchedEntities?.push(result.entity); return; } - const combinedResult: CombinedSearchResult = result; - const { entity }: { entity: any } = result; + const combinedResult: CombinedEntityResult = { entity: entityReadOnly }; + const entity: any = entityReadOnly; const siblingUrns = entity?.siblings?.siblings?.map((sibling) => sibling.urn) || []; + if (siblingUrns.length > 0) { combinedResult.matchedEntities = entity.siblings.isPrimary ? [stripSiblingsFromEntity(entity), ...entity.siblings.siblings] @@ -248,73 +245,47 @@ export function combineSiblingsInSearchResults( (resultToFilter) => (resultToFilter as Dataset).exists, ); - siblingUrns.forEach((urn) => { - siblingsToPair[urn] = combinedResult; - }); + siblingUrns.forEach((urn) => siblingsToPair.add(urn)); } + combinedResults.push(combinedResult); }); return combinedResults; } -type CombinedAutoCompleteEntityResult = { - entity: Entity; - matchedEntities?: Entity[]; +export type CombinedSearchResult = CombinedEntityResult & { + matchedFields: Array; }; +export function combineSiblingsInSearchResults( + input: + | Array<{ + entity: Entity; + matchedFields: Array; + }> + | undefined, +): Array { + return combineSiblingEntities(input?.map((value) => value.entity)).map((combinedResult) => ({ + ...combinedResult, + matchedFields: [], + })); +} + export type CombinedSuggestion = { type: EntityType; - combinedEntities: Array; + combinedEntities: Array; suggestions?: AutoCompleteResultForEntity['suggestions']; }; -// todo - combine logic export function combineSiblingsInAutoComplete( - autoCompleteResultForEntity: AutoCompleteResultForEntity, + input: AutoCompleteResultForEntity, { combine = true } = {}, -) { - const combinedEntities: CombinedAutoCompleteEntityResult[] = []; - const siblingsToPair: Record = {}; - - // set sibling associations - autoCompleteResultForEntity.entities.forEach((entityReadOnly) => { - if (!combine) { - combinedEntities.push({ entity: entityReadOnly }); - return; - } - - if (entityReadOnly.urn in siblingsToPair) { - // filter from repeating - // const siblingsCombinedResult = siblingsToPair[result.entity.urn]; - // siblingsCombinedResult.matchedEntities?.push(result.entity); - return; - } - - const combinedResult: CombinedAutoCompleteEntityResult = { entity: entityReadOnly }; - // todo - get rid of this `any` cast both here and in the search results (previous tech debt) - const entity: any = entityReadOnly; - const siblingUrns = entity?.siblings?.siblings?.map((sibling) => sibling.urn) || []; - if (siblingUrns.length > 0) { - combinedResult.matchedEntities = entity.siblings.isPrimary - ? [stripSiblingsFromEntity(entity), ...entity.siblings.siblings] - : [...entity.siblings.siblings, stripSiblingsFromEntity(entity)]; - - combinedResult.matchedEntities = combinedResult.matchedEntities.filter( - (resultToFilter) => (resultToFilter as Dataset).exists, - ); - - siblingUrns.forEach((urn) => { - siblingsToPair[urn] = combinedResult; - }); - } - combinedEntities.push(combinedResult); - }); - +): CombinedSuggestion { const combinedSuggestion: CombinedSuggestion = { - type: autoCompleteResultForEntity.type, - suggestions: autoCompleteResultForEntity.suggestions, - combinedEntities, + type: input.type, + suggestions: input.suggestions, + combinedEntities: combineSiblingEntities(input.entities, { combine }), }; return combinedSuggestion; diff --git a/datahub-web-react/src/app/search/SearchBar.tsx b/datahub-web-react/src/app/search/SearchBar.tsx index 907b9429335e99..a45b7e6974a5c1 100644 --- a/datahub-web-react/src/app/search/SearchBar.tsx +++ b/datahub-web-react/src/app/search/SearchBar.tsx @@ -222,7 +222,6 @@ export const SearchBar = ({ }, [showQuickFilters, suggestions.length, effectiveQuery, selectedQuickFilter, entityRegistry]); const autoCompleteEntityOptions = useMemo(() => { - // todo - we need to actually render the other sibling / icon / data into the autocomplete return suggestions.map((suggestion: AutoCompleteResultForEntity) => { const combinedSuggestion = combineSiblingsInAutoComplete(suggestion, { combine: combineSiblings }); return { diff --git a/datahub-web-react/src/app/search/SearchResults.tsx b/datahub-web-react/src/app/search/SearchResults.tsx index 4885715fe200fa..cfc19bdc0c26c4 100644 --- a/datahub-web-react/src/app/search/SearchResults.tsx +++ b/datahub-web-react/src/app/search/SearchResults.tsx @@ -169,6 +169,7 @@ export const SearchResults = ({ const lastResultIndex = pageStart + pageSize > totalResults ? totalResults : pageStart + pageSize; const authenticatedUserUrn = useUserContext().user?.urn; const combinedSiblingSearchResults = combineSiblingsInSearchResults(searchResponse?.searchResults); + console.log('search', { combinedSiblingSearchResults }); const searchResultUrns = combinedSiblingSearchResults.map((result) => result.entity.urn) || []; const selectedEntityUrns = selectedEntities.map((entity) => entity.urn); diff --git a/datahub-web-react/src/app/search/autoComplete/AutoCompleteEntity.tsx b/datahub-web-react/src/app/search/autoComplete/AutoCompleteEntity.tsx index 34dbb7e80b0c8b..0e1ce2f80f2441 100644 --- a/datahub-web-react/src/app/search/autoComplete/AutoCompleteEntity.tsx +++ b/datahub-web-react/src/app/search/autoComplete/AutoCompleteEntity.tsx @@ -44,37 +44,36 @@ interface Props { hasParentTooltip: boolean; } -// todo - this is mostly working well, but we really need to grab the entity details (path etc) from the bigquery one -// check out the search results de-dupe and see if there's something going on here that lets it pick the bq stuff - -// todo - maybe for some reason the dbt being the primary is throwing things off here? - -// todo - look at how Chris did the search result stuff, maybe we have some extra de-dupe step somewhere? export default function AutoCompleteEntity({ query, entity, siblings, hasParentTooltip }: Props) { const entityRegistry = useEntityRegistry(); const genericEntityProps = entityRegistry.getGenericEntityProperties(entity.type, entity); const displayName = entityRegistry.getDisplayName(entity.type, entity); - const iconScale = siblings?.length ? 0.66 : 1; + const entities = siblings?.length ? siblings : [entity]; + const iconScale = siblings && siblings.length > 1 ? 0.66 : 1; + + const parentContainers = + entities + .map((ent) => { + const genericEntityPropsForEnt = entityRegistry.getGenericEntityProperties(ent.type, ent); + const entParentContainers = genericEntityPropsForEnt?.parentContainers?.containers || []; + return [...entParentContainers].reverse(); + }) + .find((containers) => containers.length > 0) ?? []; const { matchedText, unmatchedText } = getAutoCompleteEntityText(displayName, query); - const parentContainers = genericEntityProps?.parentContainers?.containers || []; - // Need to reverse parentContainers since it returns direct parent first. - const orderedParentContainers = [...parentContainers].reverse(); const subtype = genericEntityProps?.subTypes?.typeNames?.[0]; - const entitiesForIconRendering = [entity, ...(siblings ?? [])]; - return ( - {entitiesForIconRendering.map((renderableIcon) => ( - + {entities.map((ent) => ( + ))} - + { const iconFontSize = Math.floor(DEFAULT_ICON_SIZE * scale); const previewImageSize = Math.floor(DEFAULT_PREVIEW_IMAGE_SIZE * scale); - const genericEntityParops = entityRegistry.getGenericEntityProperties(entity.type, entity); - const platformLogoUrl = genericEntityParops?.platform?.properties?.logoUrl; - const platformName = getPlatformName(genericEntityParops); + const genericEntityProps = entityRegistry.getGenericEntityProperties(entity.type, entity); + const platformLogoUrl = genericEntityProps?.platform?.properties?.logoUrl; + const platformName = getPlatformName(genericEntityProps); const icon = (platformLogoUrl && ( Date: Thu, 10 Aug 2023 13:41:21 -0700 Subject: [PATCH 05/17] move some files around --- .../shared/__tests__/siblingsUtils.test.ts | 520 +----------------- .../src/app/entity/shared/siblingUtils.ts | 76 +-- .../src/app/search/SearchBar.tsx | 2 +- .../src/app/search/SearchResultList.tsx | 3 +- .../src/app/search/SearchResults.tsx | 3 +- .../utils/combineSiblingsInAutoComplete.ts | 21 + .../combineSiblingsInSearchResults.test.ts | 517 +++++++++++++++++ .../utils/combineSiblingsInSearchResults.ts | 20 + 8 files changed, 582 insertions(+), 580 deletions(-) create mode 100644 datahub-web-react/src/app/search/utils/combineSiblingsInAutoComplete.ts create mode 100644 datahub-web-react/src/app/search/utils/combineSiblingsInSearchResults.test.ts create mode 100644 datahub-web-react/src/app/search/utils/combineSiblingsInSearchResults.ts diff --git a/datahub-web-react/src/app/entity/shared/__tests__/siblingsUtils.test.ts b/datahub-web-react/src/app/entity/shared/__tests__/siblingsUtils.test.ts index 6e23d5400ab77c..00e89e5943c176 100644 --- a/datahub-web-react/src/app/entity/shared/__tests__/siblingsUtils.test.ts +++ b/datahub-web-react/src/app/entity/shared/__tests__/siblingsUtils.test.ts @@ -1,10 +1,6 @@ import { dataset3WithLineage, dataset3WithSchema, dataset4WithLineage } from '../../../../Mocks'; import { EntityType, SchemaFieldDataType } from '../../../../types.generated'; -import { - combineEntityDataWithSiblings, - combineSiblingsInSearchResults, - shouldEntityBeTreatedAsPrimary, -} from '../siblingUtils'; +import { combineEntityDataWithSiblings, shouldEntityBeTreatedAsPrimary } from '../siblingUtils'; const usageStats = { buckets: [ @@ -191,494 +187,6 @@ const datasetUnprimaryWithNoPrimarySiblings = { }, }; -const searchResultWithSiblings = [ - { - entity: { - urn: 'urn:li:dataset:(urn:li:dataPlatform:bigquery,cypress_project.jaffle_shop.raw_orders,PROD)', - exists: true, - type: 'DATASET', - name: 'cypress_project.jaffle_shop.raw_orders', - origin: 'PROD', - uri: null, - platform: { - urn: 'urn:li:dataPlatform:bigquery', - type: 'DATA_PLATFORM', - name: 'bigquery', - properties: { - type: 'RELATIONAL_DB', - displayName: 'BigQuery', - datasetNameDelimiter: '.', - logoUrl: '/assets/platforms/bigquerylogo.png', - __typename: 'DataPlatformProperties', - }, - displayName: null, - info: null, - __typename: 'DataPlatform', - }, - dataPlatformInstance: null, - editableProperties: null, - platformNativeType: null, - properties: { - name: 'raw_orders', - description: null, - qualifiedName: null, - customProperties: [], - __typename: 'DatasetProperties', - }, - ownership: null, - globalTags: null, - glossaryTerms: null, - subTypes: { - typeNames: ['table'], - __typename: 'SubTypes', - }, - domain: null, - container: { - urn: 'urn:li:container:348c96555971d3f5c1ffd7dd2e7446cb', - platform: { - urn: 'urn:li:dataPlatform:bigquery', - type: 'DATA_PLATFORM', - name: 'bigquery', - properties: { - type: 'RELATIONAL_DB', - displayName: 'BigQuery', - datasetNameDelimiter: '.', - logoUrl: '/assets/platforms/bigquerylogo.png', - __typename: 'DataPlatformProperties', - }, - displayName: null, - info: null, - __typename: 'DataPlatform', - }, - properties: { - name: 'jaffle_shop', - __typename: 'ContainerProperties', - }, - subTypes: { - typeNames: ['Dataset'], - __typename: 'SubTypes', - }, - deprecation: null, - __typename: 'Container', - }, - parentContainers: { - count: 2, - containers: [ - { - urn: 'urn:li:container:348c96555971d3f5c1ffd7dd2e7446cb', - platform: { - urn: 'urn:li:dataPlatform:bigquery', - type: 'DATA_PLATFORM', - name: 'bigquery', - properties: { - type: 'RELATIONAL_DB', - displayName: 'BigQuery', - datasetNameDelimiter: '.', - logoUrl: '/assets/platforms/bigquerylogo.png', - __typename: 'DataPlatformProperties', - }, - displayName: null, - info: null, - __typename: 'DataPlatform', - }, - properties: { - name: 'jaffle_shop', - __typename: 'ContainerProperties', - }, - subTypes: { - typeNames: ['Dataset'], - __typename: 'SubTypes', - }, - deprecation: null, - __typename: 'Container', - }, - { - urn: 'urn:li:container:b5e95fce839e7d78151ed7e0a7420d84', - platform: { - urn: 'urn:li:dataPlatform:bigquery', - type: 'DATA_PLATFORM', - name: 'bigquery', - properties: { - type: 'RELATIONAL_DB', - displayName: 'BigQuery', - datasetNameDelimiter: '.', - logoUrl: '/assets/platforms/bigquerylogo.png', - __typename: 'DataPlatformProperties', - }, - displayName: null, - info: null, - __typename: 'DataPlatform', - }, - properties: { - name: 'cypress_project', - __typename: 'ContainerProperties', - }, - subTypes: { - typeNames: ['Project'], - __typename: 'SubTypes', - }, - deprecation: null, - __typename: 'Container', - }, - ], - __typename: 'ParentContainersResult', - }, - deprecation: null, - siblings: { - isPrimary: false, - siblings: [ - { - urn: 'urn:li:dataset:(urn:li:dataPlatform:dbt,cypress_project.jaffle_shop.raw_orders,PROD)', - exists: true, - type: 'DATASET', - platform: { - urn: 'urn:li:dataPlatform:dbt', - type: 'DATA_PLATFORM', - name: 'dbt', - properties: { - type: 'OTHERS', - displayName: 'dbt', - datasetNameDelimiter: '.', - logoUrl: '/assets/platforms/dbtlogo.png', - __typename: 'DataPlatformProperties', - }, - displayName: null, - info: null, - __typename: 'DataPlatform', - }, - name: 'cypress_project.jaffle_shop.raw_orders', - properties: { - name: 'raw_orders', - description: '', - qualifiedName: null, - __typename: 'DatasetProperties', - }, - __typename: 'Dataset', - }, - ], - __typename: 'SiblingProperties', - }, - __typename: 'Dataset', - }, - matchedFields: [ - { - name: 'name', - value: 'raw_orders', - __typename: 'MatchedField', - }, - { - name: 'id', - value: 'cypress_project.jaffle_shop.raw_orders', - __typename: 'MatchedField', - }, - ], - insights: [], - __typename: 'SearchResult', - }, - { - entity: { - urn: 'urn:li:dataset:(urn:li:dataPlatform:dbt,cypress_project.jaffle_shop.raw_orders,PROD)', - exists: true, - type: 'DATASET', - name: 'cypress_project.jaffle_shop.raw_orders', - origin: 'PROD', - uri: null, - platform: { - urn: 'urn:li:dataPlatform:dbt', - type: 'DATA_PLATFORM', - name: 'dbt', - properties: { - type: 'OTHERS', - displayName: 'dbt', - datasetNameDelimiter: '.', - logoUrl: '/assets/platforms/dbtlogo.png', - __typename: 'DataPlatformProperties', - }, - displayName: null, - info: null, - __typename: 'DataPlatform', - }, - dataPlatformInstance: null, - editableProperties: null, - platformNativeType: null, - properties: { - name: 'raw_orders', - description: '', - qualifiedName: null, - customProperties: [ - { - key: 'catalog_version', - value: '1.0.4', - __typename: 'StringMapEntry', - }, - { - key: 'node_type', - value: 'seed', - __typename: 'StringMapEntry', - }, - { - key: 'materialization', - value: 'seed', - __typename: 'StringMapEntry', - }, - { - key: 'dbt_file_path', - value: 'data/raw_orders.csv', - __typename: 'StringMapEntry', - }, - { - key: 'catalog_schema', - value: 'https://schemas.getdbt.com/dbt/catalog/v1.json', - __typename: 'StringMapEntry', - }, - { - key: 'catalog_type', - value: 'table', - __typename: 'StringMapEntry', - }, - { - key: 'manifest_version', - value: '1.0.4', - __typename: 'StringMapEntry', - }, - { - key: 'manifest_schema', - value: 'https://schemas.getdbt.com/dbt/manifest/v4.json', - __typename: 'StringMapEntry', - }, - ], - __typename: 'DatasetProperties', - }, - ownership: null, - globalTags: null, - glossaryTerms: null, - subTypes: { - typeNames: ['seed'], - __typename: 'SubTypes', - }, - domain: null, - container: null, - parentContainers: { - count: 0, - containers: [], - __typename: 'ParentContainersResult', - }, - deprecation: null, - siblings: { - isPrimary: true, - siblings: [ - { - urn: 'urn:li:dataset:(urn:li:dataPlatform:bigquery,cypress_project.jaffle_shop.raw_orders,PROD)', - type: 'DATASET', - platform: { - urn: 'urn:li:dataPlatform:bigquery', - type: 'DATA_PLATFORM', - name: 'bigquery', - properties: { - type: 'RELATIONAL_DB', - displayName: 'BigQuery', - datasetNameDelimiter: '.', - logoUrl: '/assets/platforms/bigquerylogo.png', - __typename: 'DataPlatformProperties', - }, - displayName: null, - info: null, - __typename: 'DataPlatform', - }, - name: 'cypress_project.jaffle_shop.raw_orders', - properties: { - name: 'raw_orders', - description: null, - qualifiedName: null, - __typename: 'DatasetProperties', - }, - __typename: 'Dataset', - }, - ], - __typename: 'SiblingProperties', - }, - __typename: 'Dataset', - }, - matchedFields: [ - { - name: 'name', - value: 'raw_orders', - __typename: 'MatchedField', - }, - { - name: 'id', - value: 'cypress_project.jaffle_shop.raw_orders', - __typename: 'MatchedField', - }, - ], - insights: [], - __typename: 'SearchResult', - }, -]; - -const searchResultWithGhostSiblings = [ - { - entity: { - urn: 'urn:li:dataset:(urn:li:dataPlatform:bigquery,cypress_project.jaffle_shop.raw_orders,PROD)', - exists: true, - type: 'DATASET', - name: 'cypress_project.jaffle_shop.raw_orders', - origin: 'PROD', - uri: null, - platform: { - urn: 'urn:li:dataPlatform:bigquery', - type: 'DATA_PLATFORM', - name: 'bigquery', - properties: { - type: 'RELATIONAL_DB', - displayName: 'BigQuery', - datasetNameDelimiter: '.', - logoUrl: '/assets/platforms/bigquerylogo.png', - __typename: 'DataPlatformProperties', - }, - displayName: null, - info: null, - __typename: 'DataPlatform', - }, - dataPlatformInstance: null, - editableProperties: null, - platformNativeType: null, - properties: { - name: 'raw_orders', - description: null, - qualifiedName: null, - customProperties: [], - __typename: 'DatasetProperties', - }, - ownership: null, - globalTags: null, - glossaryTerms: null, - subTypes: { - typeNames: ['table'], - __typename: 'SubTypes', - }, - domain: null, - container: { - urn: 'urn:li:container:348c96555971d3f5c1ffd7dd2e7446cb', - platform: { - urn: 'urn:li:dataPlatform:bigquery', - type: 'DATA_PLATFORM', - name: 'bigquery', - properties: { - type: 'RELATIONAL_DB', - displayName: 'BigQuery', - datasetNameDelimiter: '.', - logoUrl: '/assets/platforms/bigquerylogo.png', - __typename: 'DataPlatformProperties', - }, - displayName: null, - info: null, - __typename: 'DataPlatform', - }, - properties: { - name: 'jaffle_shop', - __typename: 'ContainerProperties', - }, - subTypes: { - typeNames: ['Dataset'], - __typename: 'SubTypes', - }, - deprecation: null, - __typename: 'Container', - }, - parentContainers: { - count: 2, - containers: [ - { - urn: 'urn:li:container:348c96555971d3f5c1ffd7dd2e7446cb', - platform: { - urn: 'urn:li:dataPlatform:bigquery', - type: 'DATA_PLATFORM', - name: 'bigquery', - properties: { - type: 'RELATIONAL_DB', - displayName: 'BigQuery', - datasetNameDelimiter: '.', - logoUrl: '/assets/platforms/bigquerylogo.png', - __typename: 'DataPlatformProperties', - }, - displayName: null, - info: null, - __typename: 'DataPlatform', - }, - properties: { - name: 'jaffle_shop', - __typename: 'ContainerProperties', - }, - subTypes: { - typeNames: ['Dataset'], - __typename: 'SubTypes', - }, - deprecation: null, - __typename: 'Container', - }, - { - urn: 'urn:li:container:b5e95fce839e7d78151ed7e0a7420d84', - platform: { - urn: 'urn:li:dataPlatform:bigquery', - type: 'DATA_PLATFORM', - name: 'bigquery', - properties: { - type: 'RELATIONAL_DB', - displayName: 'BigQuery', - datasetNameDelimiter: '.', - logoUrl: '/assets/platforms/bigquerylogo.png', - __typename: 'DataPlatformProperties', - }, - displayName: null, - info: null, - __typename: 'DataPlatform', - }, - properties: { - name: 'cypress_project', - __typename: 'ContainerProperties', - }, - subTypes: { - typeNames: ['Project'], - __typename: 'SubTypes', - }, - deprecation: null, - __typename: 'Container', - }, - ], - __typename: 'ParentContainersResult', - }, - deprecation: null, - siblings: { - isPrimary: false, - siblings: [ - { - urn: 'urn:li:dataset:(urn:li:dataPlatform:dbt,cypress_project.jaffle_shop.raw_orders,PROD)', - exists: false, - type: 'DATASET', - }, - ], - __typename: 'SiblingProperties', - }, - __typename: 'Dataset', - }, - matchedFields: [ - { - name: 'name', - value: 'raw_orders', - __typename: 'MatchedField', - }, - { - name: 'id', - value: 'cypress_project.jaffle_shop.raw_orders', - __typename: 'MatchedField', - }, - ], - insights: [], - __typename: 'SearchResult', - }, -]; - describe('siblingUtils', () => { describe('combineEntityDataWithSiblings', () => { it('combines my metadata with my siblings as primary', () => { @@ -719,32 +227,6 @@ describe('siblingUtils', () => { }); }); - describe('combineSiblingsInSearchResults', () => { - it('combines search results to deduplicate siblings', () => { - const result = combineSiblingsInSearchResults(searchResultWithSiblings as any); - - expect(result).toHaveLength(1); - expect(result?.[0]?.matchedEntities?.[0]?.urn).toEqual( - 'urn:li:dataset:(urn:li:dataPlatform:dbt,cypress_project.jaffle_shop.raw_orders,PROD)', - ); - expect(result?.[0]?.matchedEntities?.[1]?.urn).toEqual( - 'urn:li:dataset:(urn:li:dataPlatform:bigquery,cypress_project.jaffle_shop.raw_orders,PROD)', - ); - - expect(result?.[0]?.matchedEntities).toHaveLength(2); - }); - - it('will not combine an entity with a ghost node', () => { - const result = combineSiblingsInSearchResults(searchResultWithGhostSiblings as any); - - expect(result).toHaveLength(1); - expect(result?.[0]?.matchedEntities?.[0]?.urn).toEqual( - 'urn:li:dataset:(urn:li:dataPlatform:bigquery,cypress_project.jaffle_shop.raw_orders,PROD)', - ); - expect(result?.[0]?.matchedEntities).toHaveLength(1); - }); - }); - describe('shouldEntityBeTreatedAsPrimary', () => { it('will say a primary entity is primary', () => { expect(shouldEntityBeTreatedAsPrimary(datasetPrimaryWithSiblings)).toBeTruthy(); diff --git a/datahub-web-react/src/app/entity/shared/siblingUtils.ts b/datahub-web-react/src/app/entity/shared/siblingUtils.ts index 64d60218cc662a..46b4cab96e0b40 100644 --- a/datahub-web-react/src/app/entity/shared/siblingUtils.ts +++ b/datahub-web-react/src/app/entity/shared/siblingUtils.ts @@ -2,15 +2,8 @@ import merge from 'deepmerge'; import { unionBy, keyBy, values } from 'lodash'; import { useLocation } from 'react-router-dom'; import * as QueryString from 'query-string'; -import { - AutoCompleteResultForEntity, - Dataset, - Entity, - EntityType, - MatchedField, - Maybe, - SiblingProperties, -} from '../../../types.generated'; +import { Dataset, Entity, Maybe, SiblingProperties } from '../../../types.generated'; +import { GenericEntityProperties } from './types'; export function stripSiblingsFromEntity(entity: any) { return { @@ -210,12 +203,19 @@ export const combineEntityDataWithSiblings = (baseEntity: T): T => { return { [baseEntityKey]: combinedBaseEntity } as unknown as T; }; -type CombinedEntityResult = { +export type CombinedEntityResult = { entity: Entity; matchedEntities?: Array; }; -function combineSiblingEntities(entities?: Array, { combine = true } = {}): Array { +type CombineOptions = { + combine?: boolean; +}; + +export function combineSiblingEntities( + entities?: Array, + { combine = true }: CombineOptions = {}, +): Array { const combinedResults: CombinedEntityResult[] = []; const siblingsToPair: Set = new Set(); @@ -226,26 +226,25 @@ function combineSiblingEntities(entities?: Array, { combine = true } = { } if (siblingsToPair.has(entityReadOnly.urn)) { - // filter from repeating - // const siblingsCombinedResult = siblingsToPair[result.entity.urn]; - // siblingsCombinedResult.matchedEntities?.push(result.entity); return; } const combinedResult: CombinedEntityResult = { entity: entityReadOnly }; - const entity: any = entityReadOnly; - const siblingUrns = entity?.siblings?.siblings?.map((sibling) => sibling.urn) || []; + const entity = entityReadOnly as GenericEntityProperties; + const siblings = entity.siblings?.siblings ?? []; + const isPrimary = entity.siblings?.isPrimary; + const siblingUrns = siblings.map((sibling) => sibling?.urn) || []; if (siblingUrns.length > 0) { - combinedResult.matchedEntities = entity.siblings.isPrimary - ? [stripSiblingsFromEntity(entity), ...entity.siblings.siblings] - : [...entity.siblings.siblings, stripSiblingsFromEntity(entity)]; + combinedResult.matchedEntities = isPrimary + ? [stripSiblingsFromEntity(entity), ...siblings] + : [...siblings, stripSiblingsFromEntity(entity)]; combinedResult.matchedEntities = combinedResult.matchedEntities.filter( (resultToFilter) => (resultToFilter as Dataset).exists, ); - siblingUrns.forEach((urn) => siblingsToPair.add(urn)); + siblingUrns.forEach((urn) => urn && siblingsToPair.add(urn)); } combinedResults.push(combinedResult); @@ -254,43 +253,6 @@ function combineSiblingEntities(entities?: Array, { combine = true } = { return combinedResults; } -export type CombinedSearchResult = CombinedEntityResult & { - matchedFields: Array; -}; - -export function combineSiblingsInSearchResults( - input: - | Array<{ - entity: Entity; - matchedFields: Array; - }> - | undefined, -): Array { - return combineSiblingEntities(input?.map((value) => value.entity)).map((combinedResult) => ({ - ...combinedResult, - matchedFields: [], - })); -} - -export type CombinedSuggestion = { - type: EntityType; - combinedEntities: Array; - suggestions?: AutoCompleteResultForEntity['suggestions']; -}; - -export function combineSiblingsInAutoComplete( - input: AutoCompleteResultForEntity, - { combine = true } = {}, -): CombinedSuggestion { - const combinedSuggestion: CombinedSuggestion = { - type: input.type, - suggestions: input.suggestions, - combinedEntities: combineSiblingEntities(input.entities, { combine }), - }; - - return combinedSuggestion; -} - // used to determine whether sibling entities should be shown merged or not export const SEPARATE_SIBLINGS_URL_PARAM = 'separate_siblings'; diff --git a/datahub-web-react/src/app/search/SearchBar.tsx b/datahub-web-react/src/app/search/SearchBar.tsx index a45b7e6974a5c1..b07775712638ba 100644 --- a/datahub-web-react/src/app/search/SearchBar.tsx +++ b/datahub-web-react/src/app/search/SearchBar.tsx @@ -23,7 +23,7 @@ import { navigateToSearchUrl } from './utils/navigateToSearchUrl'; import { getQuickFilterDetails } from './autoComplete/quickFilters/utils'; import ViewAllSearchItem from './ViewAllSearchItem'; import { ViewSelect } from '../entity/view/select/ViewSelect'; -import { combineSiblingsInAutoComplete } from '../entity/shared/siblingUtils'; +import { combineSiblingsInAutoComplete } from './utils/combineSiblingsInAutoComplete'; const StyledAutoComplete = styled(AutoComplete)` width: 100%; diff --git a/datahub-web-react/src/app/search/SearchResultList.tsx b/datahub-web-react/src/app/search/SearchResultList.tsx index b860e7b670c33c..48efb9907628d6 100644 --- a/datahub-web-react/src/app/search/SearchResultList.tsx +++ b/datahub-web-react/src/app/search/SearchResultList.tsx @@ -5,13 +5,14 @@ import { useHistory } from 'react-router'; import { RocketOutlined } from '@ant-design/icons'; import { navigateToSearchUrl } from './utils/navigateToSearchUrl'; import { ANTD_GRAY } from '../entity/shared/constants'; -import { CombinedSearchResult, SEPARATE_SIBLINGS_URL_PARAM } from '../entity/shared/siblingUtils'; +import { SEPARATE_SIBLINGS_URL_PARAM } from '../entity/shared/siblingUtils'; import { CompactEntityNameList } from '../recommendations/renderer/component/CompactEntityNameList'; import { useEntityRegistry } from '../useEntityRegistry'; import { SearchResult } from '../../types.generated'; import analytics, { EventType } from '../analytics'; import { EntityAndType } from '../entity/shared/types'; import { useIsSearchV2 } from './useSearchAndBrowseVersion'; +import { CombinedSearchResult } from './utils/combineSiblingsInSearchResults'; const ResultList = styled(List)` &&& { diff --git a/datahub-web-react/src/app/search/SearchResults.tsx b/datahub-web-react/src/app/search/SearchResults.tsx index cfc19bdc0c26c4..19f762c1c6cf20 100644 --- a/datahub-web-react/src/app/search/SearchResults.tsx +++ b/datahub-web-react/src/app/search/SearchResults.tsx @@ -6,7 +6,6 @@ import { Entity, FacetFilterInput, FacetMetadata, MatchedField } from '../../typ import { SearchCfg } from '../../conf'; import { SearchResultsRecommendations } from './SearchResultsRecommendations'; import SearchExtendedMenu from '../entity/shared/components/styled/search/SearchExtendedMenu'; -import { combineSiblingsInSearchResults } from '../entity/shared/siblingUtils'; import { SearchSelectBar } from '../entity/shared/components/styled/search/SearchSelectBar'; import { SearchResultList } from './SearchResultList'; import { isListSubset } from '../entity/shared/utils'; @@ -26,6 +25,7 @@ import { BrowseProvider } from './sidebar/BrowseContext'; import { useIsBrowseV2, useIsSearchV2 } from './useSearchAndBrowseVersion'; import useToggleSidebar from './useToggleSidebar'; import SearchSortSelect from './sorting/SearchSortSelect'; +import { combineSiblingsInSearchResults } from './utils/combineSiblingsInSearchResults'; const SearchResultsWrapper = styled.div<{ v2Styles: boolean }>` display: flex; @@ -169,7 +169,6 @@ export const SearchResults = ({ const lastResultIndex = pageStart + pageSize > totalResults ? totalResults : pageStart + pageSize; const authenticatedUserUrn = useUserContext().user?.urn; const combinedSiblingSearchResults = combineSiblingsInSearchResults(searchResponse?.searchResults); - console.log('search', { combinedSiblingSearchResults }); const searchResultUrns = combinedSiblingSearchResults.map((result) => result.entity.urn) || []; const selectedEntityUrns = selectedEntities.map((entity) => entity.urn); diff --git a/datahub-web-react/src/app/search/utils/combineSiblingsInAutoComplete.ts b/datahub-web-react/src/app/search/utils/combineSiblingsInAutoComplete.ts new file mode 100644 index 00000000000000..e66eb1fcfc96e4 --- /dev/null +++ b/datahub-web-react/src/app/search/utils/combineSiblingsInAutoComplete.ts @@ -0,0 +1,21 @@ +import { AutoCompleteResultForEntity, EntityType } from '../../../types.generated'; +import { CombinedEntityResult, combineSiblingEntities } from '../../entity/shared/siblingUtils'; + +export type CombinedSuggestion = { + type: EntityType; + combinedEntities: Array; + suggestions?: AutoCompleteResultForEntity['suggestions']; +}; + +export function combineSiblingsInAutoComplete( + input: AutoCompleteResultForEntity, + { combine = true } = {}, +): CombinedSuggestion { + const combinedSuggestion: CombinedSuggestion = { + type: input.type, + suggestions: input.suggestions, + combinedEntities: combineSiblingEntities(input.entities, { combine }), + }; + + return combinedSuggestion; +} diff --git a/datahub-web-react/src/app/search/utils/combineSiblingsInSearchResults.test.ts b/datahub-web-react/src/app/search/utils/combineSiblingsInSearchResults.test.ts new file mode 100644 index 00000000000000..5ce62933955745 --- /dev/null +++ b/datahub-web-react/src/app/search/utils/combineSiblingsInSearchResults.test.ts @@ -0,0 +1,517 @@ +import { combineSiblingsInSearchResults } from './combineSiblingsInSearchResults'; + +const searchResultWithSiblings = [ + { + entity: { + urn: 'urn:li:dataset:(urn:li:dataPlatform:bigquery,cypress_project.jaffle_shop.raw_orders,PROD)', + exists: true, + type: 'DATASET', + name: 'cypress_project.jaffle_shop.raw_orders', + origin: 'PROD', + uri: null, + platform: { + urn: 'urn:li:dataPlatform:bigquery', + type: 'DATA_PLATFORM', + name: 'bigquery', + properties: { + type: 'RELATIONAL_DB', + displayName: 'BigQuery', + datasetNameDelimiter: '.', + logoUrl: '/assets/platforms/bigquerylogo.png', + __typename: 'DataPlatformProperties', + }, + displayName: null, + info: null, + __typename: 'DataPlatform', + }, + dataPlatformInstance: null, + editableProperties: null, + platformNativeType: null, + properties: { + name: 'raw_orders', + description: null, + qualifiedName: null, + customProperties: [], + __typename: 'DatasetProperties', + }, + ownership: null, + globalTags: null, + glossaryTerms: null, + subTypes: { + typeNames: ['table'], + __typename: 'SubTypes', + }, + domain: null, + container: { + urn: 'urn:li:container:348c96555971d3f5c1ffd7dd2e7446cb', + platform: { + urn: 'urn:li:dataPlatform:bigquery', + type: 'DATA_PLATFORM', + name: 'bigquery', + properties: { + type: 'RELATIONAL_DB', + displayName: 'BigQuery', + datasetNameDelimiter: '.', + logoUrl: '/assets/platforms/bigquerylogo.png', + __typename: 'DataPlatformProperties', + }, + displayName: null, + info: null, + __typename: 'DataPlatform', + }, + properties: { + name: 'jaffle_shop', + __typename: 'ContainerProperties', + }, + subTypes: { + typeNames: ['Dataset'], + __typename: 'SubTypes', + }, + deprecation: null, + __typename: 'Container', + }, + parentContainers: { + count: 2, + containers: [ + { + urn: 'urn:li:container:348c96555971d3f5c1ffd7dd2e7446cb', + platform: { + urn: 'urn:li:dataPlatform:bigquery', + type: 'DATA_PLATFORM', + name: 'bigquery', + properties: { + type: 'RELATIONAL_DB', + displayName: 'BigQuery', + datasetNameDelimiter: '.', + logoUrl: '/assets/platforms/bigquerylogo.png', + __typename: 'DataPlatformProperties', + }, + displayName: null, + info: null, + __typename: 'DataPlatform', + }, + properties: { + name: 'jaffle_shop', + __typename: 'ContainerProperties', + }, + subTypes: { + typeNames: ['Dataset'], + __typename: 'SubTypes', + }, + deprecation: null, + __typename: 'Container', + }, + { + urn: 'urn:li:container:b5e95fce839e7d78151ed7e0a7420d84', + platform: { + urn: 'urn:li:dataPlatform:bigquery', + type: 'DATA_PLATFORM', + name: 'bigquery', + properties: { + type: 'RELATIONAL_DB', + displayName: 'BigQuery', + datasetNameDelimiter: '.', + logoUrl: '/assets/platforms/bigquerylogo.png', + __typename: 'DataPlatformProperties', + }, + displayName: null, + info: null, + __typename: 'DataPlatform', + }, + properties: { + name: 'cypress_project', + __typename: 'ContainerProperties', + }, + subTypes: { + typeNames: ['Project'], + __typename: 'SubTypes', + }, + deprecation: null, + __typename: 'Container', + }, + ], + __typename: 'ParentContainersResult', + }, + deprecation: null, + siblings: { + isPrimary: false, + siblings: [ + { + urn: 'urn:li:dataset:(urn:li:dataPlatform:dbt,cypress_project.jaffle_shop.raw_orders,PROD)', + exists: true, + type: 'DATASET', + platform: { + urn: 'urn:li:dataPlatform:dbt', + type: 'DATA_PLATFORM', + name: 'dbt', + properties: { + type: 'OTHERS', + displayName: 'dbt', + datasetNameDelimiter: '.', + logoUrl: '/assets/platforms/dbtlogo.png', + __typename: 'DataPlatformProperties', + }, + displayName: null, + info: null, + __typename: 'DataPlatform', + }, + name: 'cypress_project.jaffle_shop.raw_orders', + properties: { + name: 'raw_orders', + description: '', + qualifiedName: null, + __typename: 'DatasetProperties', + }, + __typename: 'Dataset', + }, + ], + __typename: 'SiblingProperties', + }, + __typename: 'Dataset', + }, + matchedFields: [ + { + name: 'name', + value: 'raw_orders', + __typename: 'MatchedField', + }, + { + name: 'id', + value: 'cypress_project.jaffle_shop.raw_orders', + __typename: 'MatchedField', + }, + ], + insights: [], + __typename: 'SearchResult', + }, + { + entity: { + urn: 'urn:li:dataset:(urn:li:dataPlatform:dbt,cypress_project.jaffle_shop.raw_orders,PROD)', + exists: true, + type: 'DATASET', + name: 'cypress_project.jaffle_shop.raw_orders', + origin: 'PROD', + uri: null, + platform: { + urn: 'urn:li:dataPlatform:dbt', + type: 'DATA_PLATFORM', + name: 'dbt', + properties: { + type: 'OTHERS', + displayName: 'dbt', + datasetNameDelimiter: '.', + logoUrl: '/assets/platforms/dbtlogo.png', + __typename: 'DataPlatformProperties', + }, + displayName: null, + info: null, + __typename: 'DataPlatform', + }, + dataPlatformInstance: null, + editableProperties: null, + platformNativeType: null, + properties: { + name: 'raw_orders', + description: '', + qualifiedName: null, + customProperties: [ + { + key: 'catalog_version', + value: '1.0.4', + __typename: 'StringMapEntry', + }, + { + key: 'node_type', + value: 'seed', + __typename: 'StringMapEntry', + }, + { + key: 'materialization', + value: 'seed', + __typename: 'StringMapEntry', + }, + { + key: 'dbt_file_path', + value: 'data/raw_orders.csv', + __typename: 'StringMapEntry', + }, + { + key: 'catalog_schema', + value: 'https://schemas.getdbt.com/dbt/catalog/v1.json', + __typename: 'StringMapEntry', + }, + { + key: 'catalog_type', + value: 'table', + __typename: 'StringMapEntry', + }, + { + key: 'manifest_version', + value: '1.0.4', + __typename: 'StringMapEntry', + }, + { + key: 'manifest_schema', + value: 'https://schemas.getdbt.com/dbt/manifest/v4.json', + __typename: 'StringMapEntry', + }, + ], + __typename: 'DatasetProperties', + }, + ownership: null, + globalTags: null, + glossaryTerms: null, + subTypes: { + typeNames: ['seed'], + __typename: 'SubTypes', + }, + domain: null, + container: null, + parentContainers: { + count: 0, + containers: [], + __typename: 'ParentContainersResult', + }, + deprecation: null, + siblings: { + isPrimary: true, + siblings: [ + { + urn: 'urn:li:dataset:(urn:li:dataPlatform:bigquery,cypress_project.jaffle_shop.raw_orders,PROD)', + type: 'DATASET', + platform: { + urn: 'urn:li:dataPlatform:bigquery', + type: 'DATA_PLATFORM', + name: 'bigquery', + properties: { + type: 'RELATIONAL_DB', + displayName: 'BigQuery', + datasetNameDelimiter: '.', + logoUrl: '/assets/platforms/bigquerylogo.png', + __typename: 'DataPlatformProperties', + }, + displayName: null, + info: null, + __typename: 'DataPlatform', + }, + name: 'cypress_project.jaffle_shop.raw_orders', + properties: { + name: 'raw_orders', + description: null, + qualifiedName: null, + __typename: 'DatasetProperties', + }, + __typename: 'Dataset', + }, + ], + __typename: 'SiblingProperties', + }, + __typename: 'Dataset', + }, + matchedFields: [ + { + name: 'name', + value: 'raw_orders', + __typename: 'MatchedField', + }, + { + name: 'id', + value: 'cypress_project.jaffle_shop.raw_orders', + __typename: 'MatchedField', + }, + ], + insights: [], + __typename: 'SearchResult', + }, +]; + +const searchResultWithGhostSiblings = [ + { + entity: { + urn: 'urn:li:dataset:(urn:li:dataPlatform:bigquery,cypress_project.jaffle_shop.raw_orders,PROD)', + exists: true, + type: 'DATASET', + name: 'cypress_project.jaffle_shop.raw_orders', + origin: 'PROD', + uri: null, + platform: { + urn: 'urn:li:dataPlatform:bigquery', + type: 'DATA_PLATFORM', + name: 'bigquery', + properties: { + type: 'RELATIONAL_DB', + displayName: 'BigQuery', + datasetNameDelimiter: '.', + logoUrl: '/assets/platforms/bigquerylogo.png', + __typename: 'DataPlatformProperties', + }, + displayName: null, + info: null, + __typename: 'DataPlatform', + }, + dataPlatformInstance: null, + editableProperties: null, + platformNativeType: null, + properties: { + name: 'raw_orders', + description: null, + qualifiedName: null, + customProperties: [], + __typename: 'DatasetProperties', + }, + ownership: null, + globalTags: null, + glossaryTerms: null, + subTypes: { + typeNames: ['table'], + __typename: 'SubTypes', + }, + domain: null, + container: { + urn: 'urn:li:container:348c96555971d3f5c1ffd7dd2e7446cb', + platform: { + urn: 'urn:li:dataPlatform:bigquery', + type: 'DATA_PLATFORM', + name: 'bigquery', + properties: { + type: 'RELATIONAL_DB', + displayName: 'BigQuery', + datasetNameDelimiter: '.', + logoUrl: '/assets/platforms/bigquerylogo.png', + __typename: 'DataPlatformProperties', + }, + displayName: null, + info: null, + __typename: 'DataPlatform', + }, + properties: { + name: 'jaffle_shop', + __typename: 'ContainerProperties', + }, + subTypes: { + typeNames: ['Dataset'], + __typename: 'SubTypes', + }, + deprecation: null, + __typename: 'Container', + }, + parentContainers: { + count: 2, + containers: [ + { + urn: 'urn:li:container:348c96555971d3f5c1ffd7dd2e7446cb', + platform: { + urn: 'urn:li:dataPlatform:bigquery', + type: 'DATA_PLATFORM', + name: 'bigquery', + properties: { + type: 'RELATIONAL_DB', + displayName: 'BigQuery', + datasetNameDelimiter: '.', + logoUrl: '/assets/platforms/bigquerylogo.png', + __typename: 'DataPlatformProperties', + }, + displayName: null, + info: null, + __typename: 'DataPlatform', + }, + properties: { + name: 'jaffle_shop', + __typename: 'ContainerProperties', + }, + subTypes: { + typeNames: ['Dataset'], + __typename: 'SubTypes', + }, + deprecation: null, + __typename: 'Container', + }, + { + urn: 'urn:li:container:b5e95fce839e7d78151ed7e0a7420d84', + platform: { + urn: 'urn:li:dataPlatform:bigquery', + type: 'DATA_PLATFORM', + name: 'bigquery', + properties: { + type: 'RELATIONAL_DB', + displayName: 'BigQuery', + datasetNameDelimiter: '.', + logoUrl: '/assets/platforms/bigquerylogo.png', + __typename: 'DataPlatformProperties', + }, + displayName: null, + info: null, + __typename: 'DataPlatform', + }, + properties: { + name: 'cypress_project', + __typename: 'ContainerProperties', + }, + subTypes: { + typeNames: ['Project'], + __typename: 'SubTypes', + }, + deprecation: null, + __typename: 'Container', + }, + ], + __typename: 'ParentContainersResult', + }, + deprecation: null, + siblings: { + isPrimary: false, + siblings: [ + { + urn: 'urn:li:dataset:(urn:li:dataPlatform:dbt,cypress_project.jaffle_shop.raw_orders,PROD)', + exists: false, + type: 'DATASET', + }, + ], + __typename: 'SiblingProperties', + }, + __typename: 'Dataset', + }, + matchedFields: [ + { + name: 'name', + value: 'raw_orders', + __typename: 'MatchedField', + }, + { + name: 'id', + value: 'cypress_project.jaffle_shop.raw_orders', + __typename: 'MatchedField', + }, + ], + insights: [], + __typename: 'SearchResult', + }, +]; + +describe('siblingUtils', () => { + describe('combineSiblingsInSearchResults', () => { + it('combines search results to deduplicate siblings', () => { + const result = combineSiblingsInSearchResults(searchResultWithSiblings as any); + + expect(result).toHaveLength(1); + expect(result?.[0]?.matchedEntities?.[0]?.urn).toEqual( + 'urn:li:dataset:(urn:li:dataPlatform:dbt,cypress_project.jaffle_shop.raw_orders,PROD)', + ); + expect(result?.[0]?.matchedEntities?.[1]?.urn).toEqual( + 'urn:li:dataset:(urn:li:dataPlatform:bigquery,cypress_project.jaffle_shop.raw_orders,PROD)', + ); + + expect(result?.[0]?.matchedEntities).toHaveLength(2); + }); + + it('will not combine an entity with a ghost node', () => { + const result = combineSiblingsInSearchResults(searchResultWithGhostSiblings as any); + + expect(result).toHaveLength(1); + expect(result?.[0]?.matchedEntities?.[0]?.urn).toEqual( + 'urn:li:dataset:(urn:li:dataPlatform:bigquery,cypress_project.jaffle_shop.raw_orders,PROD)', + ); + expect(result?.[0]?.matchedEntities).toHaveLength(1); + }); + }); +}); diff --git a/datahub-web-react/src/app/search/utils/combineSiblingsInSearchResults.ts b/datahub-web-react/src/app/search/utils/combineSiblingsInSearchResults.ts new file mode 100644 index 00000000000000..1885c5e207287d --- /dev/null +++ b/datahub-web-react/src/app/search/utils/combineSiblingsInSearchResults.ts @@ -0,0 +1,20 @@ +import { Entity, MatchedField } from '../../../types.generated'; +import { CombinedEntityResult, combineSiblingEntities } from '../../entity/shared/siblingUtils'; + +export type CombinedSearchResult = CombinedEntityResult & { + matchedFields: Array; +}; + +export function combineSiblingsInSearchResults( + input: + | Array<{ + entity: Entity; + matchedFields: Array; + }> + | undefined, +): Array { + return combineSiblingEntities(input?.map((value) => value.entity)).map((combinedResult) => ({ + ...combinedResult, + matchedFields: [], + })); +} From 5f85017dd7f7ef73d80a65801d48676c6dfc4150 Mon Sep 17 00:00:00 2001 From: Josh Eilers Date: Thu, 10 Aug 2023 13:48:36 -0700 Subject: [PATCH 06/17] backout some todos --- datahub-web-react/src/app/search/SearchBar.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/datahub-web-react/src/app/search/SearchBar.tsx b/datahub-web-react/src/app/search/SearchBar.tsx index b07775712638ba..3e5b6aa83dee9c 100644 --- a/datahub-web-react/src/app/search/SearchBar.tsx +++ b/datahub-web-react/src/app/search/SearchBar.tsx @@ -347,8 +347,6 @@ export const SearchBar = ({ }, 0); } }} - // todo - disable - // open={isDropdownVisible || true} open={isDropdownVisible} listHeight={480} > From 91649e218fd87b55d6699a6fdc892df19a57fc1b Mon Sep 17 00:00:00 2001 From: Josh Eilers Date: Thu, 10 Aug 2023 13:56:17 -0700 Subject: [PATCH 07/17] remove unhelpfuld default --- datahub-web-react/src/app/entity/shared/siblingUtils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/datahub-web-react/src/app/entity/shared/siblingUtils.ts b/datahub-web-react/src/app/entity/shared/siblingUtils.ts index 46b4cab96e0b40..ac6a990daa25f9 100644 --- a/datahub-web-react/src/app/entity/shared/siblingUtils.ts +++ b/datahub-web-react/src/app/entity/shared/siblingUtils.ts @@ -233,7 +233,7 @@ export function combineSiblingEntities( const entity = entityReadOnly as GenericEntityProperties; const siblings = entity.siblings?.siblings ?? []; const isPrimary = entity.siblings?.isPrimary; - const siblingUrns = siblings.map((sibling) => sibling?.urn) || []; + const siblingUrns = siblings.map((sibling) => sibling?.urn); if (siblingUrns.length > 0) { combinedResult.matchedEntities = isPrimary From 6953c6350b473be1a4e2f78100ebf581cea6df79 Mon Sep 17 00:00:00 2001 From: Josh Eilers Date: Thu, 10 Aug 2023 13:57:23 -0700 Subject: [PATCH 08/17] harden options typing --- datahub-web-react/src/app/entity/shared/siblingUtils.ts | 2 +- .../src/app/search/utils/combineSiblingsInAutoComplete.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/datahub-web-react/src/app/entity/shared/siblingUtils.ts b/datahub-web-react/src/app/entity/shared/siblingUtils.ts index ac6a990daa25f9..9a651faf1bcf0e 100644 --- a/datahub-web-react/src/app/entity/shared/siblingUtils.ts +++ b/datahub-web-react/src/app/entity/shared/siblingUtils.ts @@ -208,7 +208,7 @@ export type CombinedEntityResult = { matchedEntities?: Array; }; -type CombineOptions = { +export type CombineOptions = { combine?: boolean; }; diff --git a/datahub-web-react/src/app/search/utils/combineSiblingsInAutoComplete.ts b/datahub-web-react/src/app/search/utils/combineSiblingsInAutoComplete.ts index e66eb1fcfc96e4..34fac2162a8283 100644 --- a/datahub-web-react/src/app/search/utils/combineSiblingsInAutoComplete.ts +++ b/datahub-web-react/src/app/search/utils/combineSiblingsInAutoComplete.ts @@ -1,5 +1,5 @@ import { AutoCompleteResultForEntity, EntityType } from '../../../types.generated'; -import { CombinedEntityResult, combineSiblingEntities } from '../../entity/shared/siblingUtils'; +import { CombineOptions, CombinedEntityResult, combineSiblingEntities } from '../../entity/shared/siblingUtils'; export type CombinedSuggestion = { type: EntityType; @@ -9,7 +9,7 @@ export type CombinedSuggestion = { export function combineSiblingsInAutoComplete( input: AutoCompleteResultForEntity, - { combine = true } = {}, + { combine = true }: CombineOptions = {}, ): CombinedSuggestion { const combinedSuggestion: CombinedSuggestion = { type: input.type, From f6571f0d2499c1a501c007350ae145635775b17b Mon Sep 17 00:00:00 2001 From: Josh Eilers Date: Thu, 10 Aug 2023 16:21:24 -0700 Subject: [PATCH 09/17] some refactoring to support matchedFields --- .../src/app/entity/shared/siblingUtils.ts | 59 +++++++------------ .../src/app/search/SearchBar.tsx | 2 +- .../utils/combineSiblingsInAutoComplete.ts | 30 ++++++---- .../combineSiblingsInSearchResults.test.ts | 4 ++ .../utils/combineSiblingsInSearchResults.ts | 35 +++++++---- 5 files changed, 69 insertions(+), 61 deletions(-) diff --git a/datahub-web-react/src/app/entity/shared/siblingUtils.ts b/datahub-web-react/src/app/entity/shared/siblingUtils.ts index 9a651faf1bcf0e..9f88ebb05864c5 100644 --- a/datahub-web-react/src/app/entity/shared/siblingUtils.ts +++ b/datahub-web-react/src/app/entity/shared/siblingUtils.ts @@ -203,54 +203,37 @@ export const combineEntityDataWithSiblings = (baseEntity: T): T => { return { [baseEntityKey]: combinedBaseEntity } as unknown as T; }; -export type CombinedEntityResult = { +export type CombinedEntity = { entity: Entity; matchedEntities?: Array; }; -export type CombineOptions = { - combine?: boolean; -}; - -export function combineSiblingEntities( - entities?: Array, - { combine = true }: CombineOptions = {}, -): Array { - const combinedResults: CombinedEntityResult[] = []; - const siblingsToPair: Set = new Set(); - - entities?.forEach((entityReadOnly) => { - if (!combine) { - combinedResults.push({ entity: entityReadOnly }); - return; - } - - if (siblingsToPair.has(entityReadOnly.urn)) { - return; - } +export function combineSiblingsForEntity(entity: Entity, visitedSiblingUrns: Set): CombinedEntity | null { + if (visitedSiblingUrns.has(entity.urn)) return null; - const combinedResult: CombinedEntityResult = { entity: entityReadOnly }; - const entity = entityReadOnly as GenericEntityProperties; - const siblings = entity.siblings?.siblings ?? []; - const isPrimary = entity.siblings?.isPrimary; - const siblingUrns = siblings.map((sibling) => sibling?.urn); + const combinedResult: CombinedEntity = { entity }; + const siblings = (entity as GenericEntityProperties).siblings?.siblings ?? []; + const isPrimary = (entity as GenericEntityProperties).siblings?.isPrimary; + const siblingUrns = siblings.map((sibling) => sibling?.urn); - if (siblingUrns.length > 0) { - combinedResult.matchedEntities = isPrimary - ? [stripSiblingsFromEntity(entity), ...siblings] - : [...siblings, stripSiblingsFromEntity(entity)]; + if (siblingUrns.length > 0) { + combinedResult.matchedEntities = isPrimary + ? [stripSiblingsFromEntity(entity), ...siblings] + : [...siblings, stripSiblingsFromEntity(entity)]; - combinedResult.matchedEntities = combinedResult.matchedEntities.filter( - (resultToFilter) => (resultToFilter as Dataset).exists, - ); + combinedResult.matchedEntities = combinedResult.matchedEntities.filter( + (resultToFilter) => (resultToFilter as Dataset).exists, + ); - siblingUrns.forEach((urn) => urn && siblingsToPair.add(urn)); - } + siblingUrns.forEach((urn) => urn && visitedSiblingUrns.add(urn)); + } - combinedResults.push(combinedResult); - }); + return combinedResult; +} - return combinedResults; +export function createSiblingEntityCombiner() { + const visitedSiblingUrns: Set = new Set(); + return (entity: Entity) => combineSiblingsForEntity(entity, visitedSiblingUrns); } // used to determine whether sibling entities should be shown merged or not diff --git a/datahub-web-react/src/app/search/SearchBar.tsx b/datahub-web-react/src/app/search/SearchBar.tsx index 3e5b6aa83dee9c..7dbf3c55d021d3 100644 --- a/datahub-web-react/src/app/search/SearchBar.tsx +++ b/datahub-web-react/src/app/search/SearchBar.tsx @@ -223,7 +223,7 @@ export const SearchBar = ({ const autoCompleteEntityOptions = useMemo(() => { return suggestions.map((suggestion: AutoCompleteResultForEntity) => { - const combinedSuggestion = combineSiblingsInAutoComplete(suggestion, { combine: combineSiblings }); + const combinedSuggestion = combineSiblingsInAutoComplete(suggestion, { combineSiblings }); return { label: , options: combinedSuggestion.combinedEntities.map((combinedEntity) => ({ diff --git a/datahub-web-react/src/app/search/utils/combineSiblingsInAutoComplete.ts b/datahub-web-react/src/app/search/utils/combineSiblingsInAutoComplete.ts index 34fac2162a8283..55cf4475a57648 100644 --- a/datahub-web-react/src/app/search/utils/combineSiblingsInAutoComplete.ts +++ b/datahub-web-react/src/app/search/utils/combineSiblingsInAutoComplete.ts @@ -1,21 +1,31 @@ import { AutoCompleteResultForEntity, EntityType } from '../../../types.generated'; -import { CombineOptions, CombinedEntityResult, combineSiblingEntities } from '../../entity/shared/siblingUtils'; +import { CombinedEntity, createSiblingEntityCombiner } from '../../entity/shared/siblingUtils'; export type CombinedSuggestion = { type: EntityType; - combinedEntities: Array; + combinedEntities: Array; suggestions?: AutoCompleteResultForEntity['suggestions']; }; export function combineSiblingsInAutoComplete( - input: AutoCompleteResultForEntity, - { combine = true }: CombineOptions = {}, + autoCompleteResultForEntity: AutoCompleteResultForEntity, + { combineSiblings = false } = {}, ): CombinedSuggestion { - const combinedSuggestion: CombinedSuggestion = { - type: input.type, - suggestions: input.suggestions, - combinedEntities: combineSiblingEntities(input.entities, { combine }), - }; + const combine = createSiblingEntityCombiner(); + const combinedResults: Array = []; + + autoCompleteResultForEntity.entities.forEach((entity) => { + if (!combineSiblings) { + combinedResults.push({ entity }); + return; + } + const combinedEntity = combine(entity); + if (combinedEntity) combinedResults.push(combinedEntity); + }); - return combinedSuggestion; + return { + type: autoCompleteResultForEntity.type, + suggestions: autoCompleteResultForEntity.suggestions, + combinedEntities: combinedResults, + }; } diff --git a/datahub-web-react/src/app/search/utils/combineSiblingsInSearchResults.test.ts b/datahub-web-react/src/app/search/utils/combineSiblingsInSearchResults.test.ts index 5ce62933955745..4cf61c715b0e91 100644 --- a/datahub-web-react/src/app/search/utils/combineSiblingsInSearchResults.test.ts +++ b/datahub-web-react/src/app/search/utils/combineSiblingsInSearchResults.test.ts @@ -502,6 +502,8 @@ describe('siblingUtils', () => { ); expect(result?.[0]?.matchedEntities).toHaveLength(2); + + expect(result?.[0]?.matchedFields).toHaveLength(2); }); it('will not combine an entity with a ghost node', () => { @@ -512,6 +514,8 @@ describe('siblingUtils', () => { 'urn:li:dataset:(urn:li:dataPlatform:bigquery,cypress_project.jaffle_shop.raw_orders,PROD)', ); expect(result?.[0]?.matchedEntities).toHaveLength(1); + + expect(result?.[0]?.matchedFields).toHaveLength(2); }); }); }); diff --git a/datahub-web-react/src/app/search/utils/combineSiblingsInSearchResults.ts b/datahub-web-react/src/app/search/utils/combineSiblingsInSearchResults.ts index 1885c5e207287d..4617b8a7e30372 100644 --- a/datahub-web-react/src/app/search/utils/combineSiblingsInSearchResults.ts +++ b/datahub-web-react/src/app/search/utils/combineSiblingsInSearchResults.ts @@ -1,20 +1,31 @@ import { Entity, MatchedField } from '../../../types.generated'; -import { CombinedEntityResult, combineSiblingEntities } from '../../entity/shared/siblingUtils'; +import { CombinedEntity, createSiblingEntityCombiner } from '../../entity/shared/siblingUtils'; -export type CombinedSearchResult = CombinedEntityResult & { +type UncombinedSeaerchResults = { + entity: Entity; + matchedFields: Array; +}; + +export type CombinedSearchResult = CombinedEntity & { matchedFields: Array; }; export function combineSiblingsInSearchResults( - input: - | Array<{ - entity: Entity; - matchedFields: Array; - }> - | undefined, + searchResults: Array | undefined = [], ): Array { - return combineSiblingEntities(input?.map((value) => value.entity)).map((combinedResult) => ({ - ...combinedResult, - matchedFields: [], - })); + const combine = createSiblingEntityCombiner(); + const combinedResults: Array = []; + + searchResults.forEach(({ entity, matchedFields }) => { + const combinedEntity = combine(entity); + if (combinedEntity) { + combinedResults.push({ + entity: combinedEntity.entity, + matchedEntities: combinedEntity.matchedEntities, + matchedFields, + }); + } + }); + + return combinedResults; } From 7707b54f098cd3120649bfa354f6070567b65cab Mon Sep 17 00:00:00 2001 From: Josh Eilers Date: Thu, 10 Aug 2023 16:47:47 -0700 Subject: [PATCH 10/17] clarify some logic better with a better return --- .../src/app/entity/shared/siblingUtils.ts | 23 +++++++++++++------ .../utils/combineSiblingsInAutoComplete.ts | 10 ++++---- .../utils/combineSiblingsInSearchResults.ts | 17 +++++++------- 3 files changed, 29 insertions(+), 21 deletions(-) diff --git a/datahub-web-react/src/app/entity/shared/siblingUtils.ts b/datahub-web-react/src/app/entity/shared/siblingUtils.ts index 9f88ebb05864c5..9da789cecd825f 100644 --- a/datahub-web-react/src/app/entity/shared/siblingUtils.ts +++ b/datahub-web-react/src/app/entity/shared/siblingUtils.ts @@ -208,27 +208,36 @@ export type CombinedEntity = { matchedEntities?: Array; }; -export function combineSiblingsForEntity(entity: Entity, visitedSiblingUrns: Set): CombinedEntity | null { - if (visitedSiblingUrns.has(entity.urn)) return null; - - const combinedResult: CombinedEntity = { entity }; +type CombinedEntityResult = + | { + skipped: true; + } + | { + skipped: false; + combinedEntity: CombinedEntity; + }; + +export function combineSiblingsForEntity(entity: Entity, visitedSiblingUrns: Set): CombinedEntityResult { + if (visitedSiblingUrns.has(entity.urn)) return { skipped: true }; + + const combinedEntity: CombinedEntity = { entity }; const siblings = (entity as GenericEntityProperties).siblings?.siblings ?? []; const isPrimary = (entity as GenericEntityProperties).siblings?.isPrimary; const siblingUrns = siblings.map((sibling) => sibling?.urn); if (siblingUrns.length > 0) { - combinedResult.matchedEntities = isPrimary + combinedEntity.matchedEntities = isPrimary ? [stripSiblingsFromEntity(entity), ...siblings] : [...siblings, stripSiblingsFromEntity(entity)]; - combinedResult.matchedEntities = combinedResult.matchedEntities.filter( + combinedEntity.matchedEntities = combinedEntity.matchedEntities.filter( (resultToFilter) => (resultToFilter as Dataset).exists, ); siblingUrns.forEach((urn) => urn && visitedSiblingUrns.add(urn)); } - return combinedResult; + return { combinedEntity, skipped: false }; } export function createSiblingEntityCombiner() { diff --git a/datahub-web-react/src/app/search/utils/combineSiblingsInAutoComplete.ts b/datahub-web-react/src/app/search/utils/combineSiblingsInAutoComplete.ts index 55cf4475a57648..e8e64559e67a00 100644 --- a/datahub-web-react/src/app/search/utils/combineSiblingsInAutoComplete.ts +++ b/datahub-web-react/src/app/search/utils/combineSiblingsInAutoComplete.ts @@ -12,20 +12,20 @@ export function combineSiblingsInAutoComplete( { combineSiblings = false } = {}, ): CombinedSuggestion { const combine = createSiblingEntityCombiner(); - const combinedResults: Array = []; + const combinedEntities: Array = []; autoCompleteResultForEntity.entities.forEach((entity) => { if (!combineSiblings) { - combinedResults.push({ entity }); + combinedEntities.push({ entity }); return; } - const combinedEntity = combine(entity); - if (combinedEntity) combinedResults.push(combinedEntity); + const combinedResult = combine(entity); + if (!combinedResult.skipped) combinedEntities.push(combinedResult.combinedEntity); }); return { type: autoCompleteResultForEntity.type, suggestions: autoCompleteResultForEntity.suggestions, - combinedEntities: combinedResults, + combinedEntities, }; } diff --git a/datahub-web-react/src/app/search/utils/combineSiblingsInSearchResults.ts b/datahub-web-react/src/app/search/utils/combineSiblingsInSearchResults.ts index 4617b8a7e30372..2088ae02fc7a38 100644 --- a/datahub-web-react/src/app/search/utils/combineSiblingsInSearchResults.ts +++ b/datahub-web-react/src/app/search/utils/combineSiblingsInSearchResults.ts @@ -14,18 +14,17 @@ export function combineSiblingsInSearchResults( searchResults: Array | undefined = [], ): Array { const combine = createSiblingEntityCombiner(); - const combinedResults: Array = []; + const combinedSearchResults: Array = []; - searchResults.forEach(({ entity, matchedFields }) => { - const combinedEntity = combine(entity); - if (combinedEntity) { - combinedResults.push({ - entity: combinedEntity.entity, - matchedEntities: combinedEntity.matchedEntities, - matchedFields, + searchResults.forEach((searchResult) => { + const combinedResult = combine(searchResult.entity); + if (!combinedResult.skipped) { + combinedSearchResults.push({ + ...searchResult, + ...combinedResult.combinedEntity, }); } }); - return combinedResults; + return combinedSearchResults; } From 931570a4a941be6da96175a83ab7c665c6f3ed3c Mon Sep 17 00:00:00 2001 From: Josh Eilers Date: Fri, 11 Aug 2023 08:46:59 -0700 Subject: [PATCH 11/17] move icons to top of autocomplete --- .../autoComplete/AutoCompleteEntity.tsx | 31 +++++++++++-------- .../autoComplete/AutoCompleteEntityIcon.tsx | 31 +++++-------------- .../search/autoComplete/AutoCompleteUser.tsx | 12 +------ .../search/autoComplete/ParentContainers.tsx | 1 - .../search/autoComplete/RecommendedOption.tsx | 2 +- .../search/autoComplete/styledComponents.tsx | 11 +++++++ .../utils/combineSiblingsInSearchResults.ts | 4 +-- 7 files changed, 39 insertions(+), 53 deletions(-) create mode 100644 datahub-web-react/src/app/search/autoComplete/styledComponents.tsx diff --git a/datahub-web-react/src/app/search/autoComplete/AutoCompleteEntity.tsx b/datahub-web-react/src/app/search/autoComplete/AutoCompleteEntity.tsx index 0e1ce2f80f2441..fbd69ebd73a5c8 100644 --- a/datahub-web-react/src/app/search/autoComplete/AutoCompleteEntity.tsx +++ b/datahub-web-react/src/app/search/autoComplete/AutoCompleteEntity.tsx @@ -4,10 +4,10 @@ import styled from 'styled-components/macro'; import { Entity } from '../../../types.generated'; import { useEntityRegistry } from '../../useEntityRegistry'; import { getAutoCompleteEntityText } from './utils'; -import { SuggestionText } from './AutoCompleteUser'; import ParentContainers from './ParentContainers'; import { ANTD_GRAY } from '../../entity/shared/constants'; import AutoCompleteEntityIcon from './AutoCompleteEntityIcon'; +import { SuggestionText } from './styledComponents'; const AutoCompleteEntityWrapper = styled.div` display: flex; @@ -18,7 +18,6 @@ const AutoCompleteEntityWrapper = styled.div` const IconsContainer = styled.div` display: flex; - flex-direction: column; `; const ContentWrapper = styled.div` @@ -37,6 +36,13 @@ const Subtype = styled.span` margin-right: 8px; `; +const ItemHeader = styled.div` + display: flex; + align-items: center; + margin-bottom: 3px; + gap: 4px; +`; + interface Props { query: string; entity: Entity; @@ -48,9 +54,9 @@ export default function AutoCompleteEntity({ query, entity, siblings, hasParentT const entityRegistry = useEntityRegistry(); const genericEntityProps = entityRegistry.getGenericEntityProperties(entity.type, entity); const displayName = entityRegistry.getDisplayName(entity.type, entity); - + const { matchedText, unmatchedText } = getAutoCompleteEntityText(displayName, query); + const subtype = genericEntityProps?.subTypes?.typeNames?.[0]; const entities = siblings?.length ? siblings : [entity]; - const iconScale = siblings && siblings.length > 1 ? 0.66 : 1; const parentContainers = entities @@ -61,19 +67,18 @@ export default function AutoCompleteEntity({ query, entity, siblings, hasParentT }) .find((containers) => containers.length > 0) ?? []; - const { matchedText, unmatchedText } = getAutoCompleteEntityText(displayName, query); - const subtype = genericEntityProps?.subTypes?.typeNames?.[0]; - return ( - - {entities.map((ent) => ( - - ))} - - + + + {entities.map((ent) => ( + + ))} + + + { +const AutoCompleteEntityIcon = ({ entity }: Props) => { const entityRegistry = useEntityRegistry(); - const iconFontSize = Math.floor(DEFAULT_ICON_SIZE * scale); - const previewImageSize = Math.floor(DEFAULT_PREVIEW_IMAGE_SIZE * scale); - const genericEntityProps = entityRegistry.getGenericEntityProperties(entity.type, entity); const platformLogoUrl = genericEntityProps?.platform?.properties?.logoUrl; const platformName = getPlatformName(genericEntityProps); - const icon = - (platformLogoUrl && ( - - )) || - entityRegistry.getIcon(entity.type, iconFontSize, IconStyleType.ACCENT); - - return icon; + return ( + (platformLogoUrl && ) || + entityRegistry.getIcon(entity.type, 12, IconStyleType.ACCENT) + ); }; export default AutoCompleteEntityIcon; diff --git a/datahub-web-react/src/app/search/autoComplete/AutoCompleteUser.tsx b/datahub-web-react/src/app/search/autoComplete/AutoCompleteUser.tsx index 1f88b94bb0cc7c..53b4d53ef46d4c 100644 --- a/datahub-web-react/src/app/search/autoComplete/AutoCompleteUser.tsx +++ b/datahub-web-react/src/app/search/autoComplete/AutoCompleteUser.tsx @@ -1,20 +1,10 @@ import { Typography } from 'antd'; import React from 'react'; -import styled from 'styled-components'; import { CorpUser, EntityType } from '../../../types.generated'; -import { ANTD_GRAY } from '../../entity/shared/constants'; import { CustomAvatar } from '../../shared/avatar'; import { useEntityRegistry } from '../../useEntityRegistry'; import { getAutoCompleteEntityText } from './utils'; - -export const SuggestionText = styled.div` - margin-left: 12px; - margin-top: 2px; - margin-bottom: 2px; - color: ${ANTD_GRAY[9]}; - font-size: 16px; - overflow: hidden; -`; +import { SuggestionText } from './styledComponents'; interface Props { query: string; diff --git a/datahub-web-react/src/app/search/autoComplete/ParentContainers.tsx b/datahub-web-react/src/app/search/autoComplete/ParentContainers.tsx index 77ccde06172c9e..77067d6e7057ba 100644 --- a/datahub-web-react/src/app/search/autoComplete/ParentContainers.tsx +++ b/datahub-web-react/src/app/search/autoComplete/ParentContainers.tsx @@ -13,7 +13,6 @@ const ParentContainersWrapper = styled.div` color: ${ANTD_GRAY[9]}; display: flex; align-items: center; - margin-bottom: 3px; `; const ParentContainer = styled(Typography.Text)` diff --git a/datahub-web-react/src/app/search/autoComplete/RecommendedOption.tsx b/datahub-web-react/src/app/search/autoComplete/RecommendedOption.tsx index 79743858b06d9e..f4c31b18c99b27 100644 --- a/datahub-web-react/src/app/search/autoComplete/RecommendedOption.tsx +++ b/datahub-web-react/src/app/search/autoComplete/RecommendedOption.tsx @@ -1,7 +1,7 @@ import { SearchOutlined } from '@ant-design/icons'; import React from 'react'; import styled from 'styled-components/macro'; -import { SuggestionText } from './AutoCompleteUser'; +import { SuggestionText } from './styledComponents'; const TextWrapper = styled.span``; diff --git a/datahub-web-react/src/app/search/autoComplete/styledComponents.tsx b/datahub-web-react/src/app/search/autoComplete/styledComponents.tsx new file mode 100644 index 00000000000000..9e4b084ab3889b --- /dev/null +++ b/datahub-web-react/src/app/search/autoComplete/styledComponents.tsx @@ -0,0 +1,11 @@ +import styled from 'styled-components'; +import { ANTD_GRAY } from '../../entity/shared/constants'; + +export const SuggestionText = styled.div` + margin-left: 12px; + margin-top: 2px; + margin-bottom: 2px; + color: ${ANTD_GRAY[9]}; + font-size: 16px; + overflow: hidden; +`; diff --git a/datahub-web-react/src/app/search/utils/combineSiblingsInSearchResults.ts b/datahub-web-react/src/app/search/utils/combineSiblingsInSearchResults.ts index 2088ae02fc7a38..4a5c8da6381b89 100644 --- a/datahub-web-react/src/app/search/utils/combineSiblingsInSearchResults.ts +++ b/datahub-web-react/src/app/search/utils/combineSiblingsInSearchResults.ts @@ -6,9 +6,7 @@ type UncombinedSeaerchResults = { matchedFields: Array; }; -export type CombinedSearchResult = CombinedEntity & { - matchedFields: Array; -}; +export type CombinedSearchResult = CombinedEntity & Pick; export function combineSiblingsInSearchResults( searchResults: Array | undefined = [], From 5b2395c88c4ba983757d3afe602299ea49aa569c Mon Sep 17 00:00:00 2001 From: Josh Eilers Date: Fri, 11 Aug 2023 09:27:27 -0700 Subject: [PATCH 12/17] render platform names in autocomplete --- .../autoComplete/AutoCompleteEntity.tsx | 35 +++++++++++++++++-- 1 file changed, 32 insertions(+), 3 deletions(-) diff --git a/datahub-web-react/src/app/search/autoComplete/AutoCompleteEntity.tsx b/datahub-web-react/src/app/search/autoComplete/AutoCompleteEntity.tsx index fbd69ebd73a5c8..c0a856a5e33c6d 100644 --- a/datahub-web-react/src/app/search/autoComplete/AutoCompleteEntity.tsx +++ b/datahub-web-react/src/app/search/autoComplete/AutoCompleteEntity.tsx @@ -40,7 +40,20 @@ const ItemHeader = styled.div` display: flex; align-items: center; margin-bottom: 3px; - gap: 4px; + gap: 8px; +`; + +const PlatformText = styled(Typography.Text)` + font-size: 12px; + line-height: 20px; + font-weight: 700; + color: ${ANTD_GRAY[7]}; + white-space: nowrap; +`; + +const PlatformDivider = styled.div` + border-right: 1px solid ${ANTD_GRAY[5]}; + height: 14px; `; interface Props { @@ -58,11 +71,21 @@ export default function AutoCompleteEntity({ query, entity, siblings, hasParentT const subtype = genericEntityProps?.subTypes?.typeNames?.[0]; const entities = siblings?.length ? siblings : [entity]; + const platformNames = entities + .map((ent) => { + const genericPropsForEnt = entityRegistry.getGenericEntityProperties(ent.type, ent); + const platformName = genericPropsForEnt?.platform?.name; + return platformName; + }) + .filter(Boolean); + + console.log({ entity: entity.urn, platformNames }); + const parentContainers = entities .map((ent) => { - const genericEntityPropsForEnt = entityRegistry.getGenericEntityProperties(ent.type, ent); - const entParentContainers = genericEntityPropsForEnt?.parentContainers?.containers || []; + const genericPropsForEnt = entityRegistry.getGenericEntityProperties(ent.type, ent); + const entParentContainers = genericPropsForEnt?.parentContainers?.containers || []; return [...entParentContainers].reverse(); }) .find((containers) => containers.length > 0) ?? []; @@ -77,6 +100,12 @@ export default function AutoCompleteEntity({ query, entity, siblings, hasParentT ))} + {platformNames.length > 0 && ( + <> + {platformNames.join(' & ')} + + + )} Date: Fri, 11 Aug 2023 09:29:19 -0700 Subject: [PATCH 13/17] only show divider if we are going to show containers --- .../src/app/search/autoComplete/AutoCompleteEntity.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/datahub-web-react/src/app/search/autoComplete/AutoCompleteEntity.tsx b/datahub-web-react/src/app/search/autoComplete/AutoCompleteEntity.tsx index c0a856a5e33c6d..4eac6717311d94 100644 --- a/datahub-web-react/src/app/search/autoComplete/AutoCompleteEntity.tsx +++ b/datahub-web-react/src/app/search/autoComplete/AutoCompleteEntity.tsx @@ -103,7 +103,7 @@ export default function AutoCompleteEntity({ query, entity, siblings, hasParentT {platformNames.length > 0 && ( <> {platformNames.join(' & ')} - + {!!parentContainers.length && } )} From 45b8dedd6088d1853f6bf5ca5d801c2790edb1fb Mon Sep 17 00:00:00 2001 From: Josh Eilers Date: Fri, 11 Aug 2023 10:28:31 -0700 Subject: [PATCH 14/17] updates to greyes and conditional icon rendering --- .../src/app/entity/shared/constants.ts | 1 + .../autoComplete/AutoCompleteEntity.tsx | 56 +++++++++---------- .../AutoCompletePlatformNames.tsx | 22 ++++++++ .../search/autoComplete/ParentContainers.tsx | 6 +- 4 files changed, 52 insertions(+), 33 deletions(-) create mode 100644 datahub-web-react/src/app/search/autoComplete/AutoCompletePlatformNames.tsx diff --git a/datahub-web-react/src/app/entity/shared/constants.ts b/datahub-web-react/src/app/entity/shared/constants.ts index e14affc95b6f91..ce93f219dc8941 100644 --- a/datahub-web-react/src/app/entity/shared/constants.ts +++ b/datahub-web-react/src/app/entity/shared/constants.ts @@ -25,6 +25,7 @@ export const ANTD_GRAY_V2 = { 5: '#DDE0E4', 8: '#5E666E', 10: '#1B1E22', + colorBordered: '#B2B8BD', }; export const EMPTY_MESSAGES = { diff --git a/datahub-web-react/src/app/search/autoComplete/AutoCompleteEntity.tsx b/datahub-web-react/src/app/search/autoComplete/AutoCompleteEntity.tsx index 4eac6717311d94..1e5b8382845f10 100644 --- a/datahub-web-react/src/app/search/autoComplete/AutoCompleteEntity.tsx +++ b/datahub-web-react/src/app/search/autoComplete/AutoCompleteEntity.tsx @@ -5,9 +5,10 @@ import { Entity } from '../../../types.generated'; import { useEntityRegistry } from '../../useEntityRegistry'; import { getAutoCompleteEntityText } from './utils'; import ParentContainers from './ParentContainers'; -import { ANTD_GRAY } from '../../entity/shared/constants'; +import { ANTD_GRAY_V2 } from '../../entity/shared/constants'; import AutoCompleteEntityIcon from './AutoCompleteEntityIcon'; import { SuggestionText } from './styledComponents'; +import AutoCompletePlatformNames from './AutoCompletePlatformNames'; const AutoCompleteEntityWrapper = styled.div` display: flex; @@ -27,8 +28,8 @@ const ContentWrapper = styled.div` `; const Subtype = styled.span` - color: ${ANTD_GRAY[9]}; - border: 1px solid ${ANTD_GRAY[9]}; + color: ${ANTD_GRAY_V2[8]}; + border: 1px solid ${ANTD_GRAY_V2.colorBordered}; border-radius: 16px; padding: 4px 8px; line-height: 12px; @@ -43,17 +44,9 @@ const ItemHeader = styled.div` gap: 8px; `; -const PlatformText = styled(Typography.Text)` - font-size: 12px; - line-height: 20px; - font-weight: 700; - color: ${ANTD_GRAY[7]}; - white-space: nowrap; -`; - -const PlatformDivider = styled.div` - border-right: 1px solid ${ANTD_GRAY[5]}; - height: 14px; +const Divider = styled.div` + border-right: 1px solid ${ANTD_GRAY_V2.colorBordered}; + height: 12px; `; interface Props { @@ -74,13 +67,11 @@ export default function AutoCompleteEntity({ query, entity, siblings, hasParentT const platformNames = entities .map((ent) => { const genericPropsForEnt = entityRegistry.getGenericEntityProperties(ent.type, ent); - const platformName = genericPropsForEnt?.platform?.name; + const platformName = genericPropsForEnt?.platform?.name || ''; return platformName; }) .filter(Boolean); - console.log({ entity: entity.urn, platformNames }); - const parentContainers = entities .map((ent) => { @@ -90,24 +81,27 @@ export default function AutoCompleteEntity({ query, entity, siblings, hasParentT }) .find((containers) => containers.length > 0) ?? []; + const showPlatforms = !!platformNames.length; + const showPlatformDivider = !!platformNames.length && !!parentContainers.length; + const showParentContainers = !!parentContainers.length; + const showHeader = showPlatforms || showParentContainers; + return ( - - - {entities.map((ent) => ( - - ))} - - {platformNames.length > 0 && ( - <> - {platformNames.join(' & ')} - {!!parentContainers.length && } - - )} - - + {showHeader && ( + + + {entities.map((ent) => ( + + ))} + + {showPlatforms && } + {showPlatformDivider && } + {showParentContainers && } + + )} ; +}; + +const AutoCompletePlatformNames = ({ platforms }: Props) => { + return {platforms.join(' & ')}; +}; + +export default AutoCompletePlatformNames; diff --git a/datahub-web-react/src/app/search/autoComplete/ParentContainers.tsx b/datahub-web-react/src/app/search/autoComplete/ParentContainers.tsx index 77067d6e7057ba..98a4f5aa214bb9 100644 --- a/datahub-web-react/src/app/search/autoComplete/ParentContainers.tsx +++ b/datahub-web-react/src/app/search/autoComplete/ParentContainers.tsx @@ -4,19 +4,21 @@ import React, { Fragment } from 'react'; import styled from 'styled-components/macro'; import { Container, EntityType } from '../../../types.generated'; import { useEntityRegistry } from '../../useEntityRegistry'; -import { ANTD_GRAY } from '../../entity/shared/constants'; +import { ANTD_GRAY_V2 } from '../../entity/shared/constants'; const NUM_VISIBLE_CONTAINERS = 2; const ParentContainersWrapper = styled.div` font-size: 12px; - color: ${ANTD_GRAY[9]}; + color: ${ANTD_GRAY_V2[8]}; display: flex; align-items: center; `; const ParentContainer = styled(Typography.Text)` + color: ${ANTD_GRAY_V2[8]}; margin-left: 4px; + font-weight: 500; `; export const ArrowWrapper = styled.span` From fe9c17974e9e1344995a497291c3ebfe003048f4 Mon Sep 17 00:00:00 2001 From: Josh Eilers Date: Fri, 11 Aug 2023 12:10:32 -0700 Subject: [PATCH 15/17] make platform names casing correct --- datahub-web-react/src/app/entity/shared/constants.ts | 3 ++- .../app/search/autoComplete/AutoCompleteEntity.tsx | 11 +++++++---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/datahub-web-react/src/app/entity/shared/constants.ts b/datahub-web-react/src/app/entity/shared/constants.ts index ce93f219dc8941..e92ac3aa891d0c 100644 --- a/datahub-web-react/src/app/entity/shared/constants.ts +++ b/datahub-web-react/src/app/entity/shared/constants.ts @@ -23,9 +23,10 @@ export const ANTD_GRAY = { export const ANTD_GRAY_V2 = { 2: '#F3F5F6', 5: '#DDE0E4', + 6: '#B2B8BD', 8: '#5E666E', 10: '#1B1E22', - colorBordered: '#B2B8BD', + // todo - this probably shouldn't go here }; export const EMPTY_MESSAGES = { diff --git a/datahub-web-react/src/app/search/autoComplete/AutoCompleteEntity.tsx b/datahub-web-react/src/app/search/autoComplete/AutoCompleteEntity.tsx index 1e5b8382845f10..fd64db9eec48f6 100644 --- a/datahub-web-react/src/app/search/autoComplete/AutoCompleteEntity.tsx +++ b/datahub-web-react/src/app/search/autoComplete/AutoCompleteEntity.tsx @@ -9,6 +9,7 @@ import { ANTD_GRAY_V2 } from '../../entity/shared/constants'; import AutoCompleteEntityIcon from './AutoCompleteEntityIcon'; import { SuggestionText } from './styledComponents'; import AutoCompletePlatformNames from './AutoCompletePlatformNames'; +import { capitalizeFirstLetterOnly } from '../../shared/textUtil'; const AutoCompleteEntityWrapper = styled.div` display: flex; @@ -29,7 +30,7 @@ const ContentWrapper = styled.div` const Subtype = styled.span` color: ${ANTD_GRAY_V2[8]}; - border: 1px solid ${ANTD_GRAY_V2.colorBordered}; + border: 1px solid ${ANTD_GRAY_V2[6]}; border-radius: 16px; padding: 4px 8px; line-height: 12px; @@ -45,7 +46,7 @@ const ItemHeader = styled.div` `; const Divider = styled.div` - border-right: 1px solid ${ANTD_GRAY_V2.colorBordered}; + border-right: 1px solid ${ANTD_GRAY_V2[6]}; height: 12px; `; @@ -67,8 +68,10 @@ export default function AutoCompleteEntity({ query, entity, siblings, hasParentT const platformNames = entities .map((ent) => { const genericPropsForEnt = entityRegistry.getGenericEntityProperties(ent.type, ent); - const platformName = genericPropsForEnt?.platform?.name || ''; - return platformName; + const platform = genericPropsForEnt?.platform; + if (platform?.properties?.displayName) return platform.properties.displayName; + if (platform?.name) return capitalizeFirstLetterOnly(platform.name) || ''; + return ''; }) .filter(Boolean); From 5620c5e8d7be75a0287d73469b766552ee4684d5 Mon Sep 17 00:00:00 2001 From: Josh Eilers Date: Mon, 14 Aug 2023 13:00:20 -0700 Subject: [PATCH 16/17] remove todo --- datahub-web-react/src/app/entity/shared/constants.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/datahub-web-react/src/app/entity/shared/constants.ts b/datahub-web-react/src/app/entity/shared/constants.ts index e92ac3aa891d0c..447780fb0d6410 100644 --- a/datahub-web-react/src/app/entity/shared/constants.ts +++ b/datahub-web-react/src/app/entity/shared/constants.ts @@ -26,7 +26,6 @@ export const ANTD_GRAY_V2 = { 6: '#B2B8BD', 8: '#5E666E', 10: '#1B1E22', - // todo - this probably shouldn't go here }; export const EMPTY_MESSAGES = { From 7c8d0b248ed70314950c822e78ed58f8549ad41c Mon Sep 17 00:00:00 2001 From: Josh Eilers Date: Tue, 15 Aug 2023 13:48:45 -0700 Subject: [PATCH 17/17] simplify platform rendering --- .../autoComplete/AutoCompleteEntity.tsx | 42 ++++++++----------- 1 file changed, 17 insertions(+), 25 deletions(-) diff --git a/datahub-web-react/src/app/search/autoComplete/AutoCompleteEntity.tsx b/datahub-web-react/src/app/search/autoComplete/AutoCompleteEntity.tsx index 3270f2efd0716d..60bb21713ba58e 100644 --- a/datahub-web-react/src/app/search/autoComplete/AutoCompleteEntity.tsx +++ b/datahub-web-react/src/app/search/autoComplete/AutoCompleteEntity.tsx @@ -1,7 +1,7 @@ import { Typography } from 'antd'; import React from 'react'; import styled from 'styled-components/macro'; -import { Entity } from '../../../types.generated'; +import { Entity, EntityType } from '../../../types.generated'; import { useEntityRegistry } from '../../useEntityRegistry'; import { getAutoCompleteEntityText } from './utils'; import ParentContainers from './ParentContainers'; @@ -9,7 +9,7 @@ import { ANTD_GRAY_V2 } from '../../entity/shared/constants'; import AutoCompleteEntityIcon from './AutoCompleteEntityIcon'; import { SuggestionText } from './styledComponents'; import AutoCompletePlatformNames from './AutoCompletePlatformNames'; -import { capitalizeFirstLetterOnly } from '../../shared/textUtil'; +import { getPlatformName } from '../../entity/shared/utils'; const AutoCompleteEntityWrapper = styled.div` display: flex; @@ -62,30 +62,22 @@ export default function AutoCompleteEntity({ query, entity, siblings, hasParentT const genericEntityProps = entityRegistry.getGenericEntityProperties(entity.type, entity); const displayName = entityRegistry.getDisplayName(entity.type, entity); const { matchedText, unmatchedText } = getAutoCompleteEntityText(displayName, query); - const subtype = genericEntityProps?.subTypes?.typeNames?.[0]; const entities = siblings?.length ? siblings : [entity]; + const platforms = + genericEntityProps?.siblingPlatforms + ?.map( + (platform) => + getPlatformName(entityRegistry.getGenericEntityProperties(EntityType.DataPlatform, platform)) || '', + ) + .filter(Boolean) ?? []; - const platformNames = entities - .map((ent) => { - const genericPropsForEnt = entityRegistry.getGenericEntityProperties(ent.type, ent); - const platform = genericPropsForEnt?.platform; - if (platform?.properties?.displayName) return platform.properties.displayName; - if (platform?.name) return capitalizeFirstLetterOnly(platform.name) || ''; - return ''; - }) - .filter(Boolean); - - const parentContainers = - entities - .map((ent) => { - const genericPropsForEnt = entityRegistry.getGenericEntityProperties(ent.type, ent); - const entParentContainers = genericPropsForEnt?.parentContainers?.containers || []; - return [...entParentContainers].reverse(); - }) - .find((containers) => containers.length > 0) ?? []; + const parentContainers = genericEntityProps?.parentContainers?.containers || []; + // Need to reverse parentContainers since it returns direct parent first. + const orderedParentContainers = [...parentContainers].reverse(); + const subtype = genericEntityProps?.subTypes?.typeNames?.[0]; - const showPlatforms = !!platformNames.length; - const showPlatformDivider = !!platformNames.length && !!parentContainers.length; + const showPlatforms = !!platforms.length; + const showPlatformDivider = !!platforms.length && !!parentContainers.length; const showParentContainers = !!parentContainers.length; const showHeader = showPlatforms || showParentContainers; @@ -100,9 +92,9 @@ export default function AutoCompleteEntity({ query, entity, siblings, hasParentT ))} - {showPlatforms && } + {showPlatforms && } {showPlatformDivider && } - {showParentContainers && } + {showParentContainers && } )}