Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Combine siblings in autocomplete #8610

Merged
merged 21 commits into from
Aug 16, 2023
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

Large diffs are not rendered by default.

64 changes: 34 additions & 30 deletions datahub-web-react/src/app/entity/shared/siblingUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ import merge from 'deepmerge';
import { unionBy, keyBy, values } from 'lodash';
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks like we have a conflict from my changes to display consistent combined search results for sibling entities in this PR: https://github.com/datahub-project/datahub/pull/8602/files#diff-dbeb733a628595000d108fe2f57b5c464d52b6da55f57d57ad4bc6e246af3914

so let's make sure we keep that functionlity! the main thing I did is simply combine the siblings entity data in combineSiblingsInSearchResults and refactored a function above it to make it reusable between its two uses

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Think I got the conflict resolved 👍

import { useLocation } from 'react-router-dom';
import * as QueryString from 'query-string';
import { Dataset, Entity, MatchedField, Maybe, SiblingProperties } from '../../../types.generated';
import { Dataset, Entity, Maybe, SiblingProperties } from '../../../types.generated';
import { GenericEntityProperties } from './types';

export function stripSiblingsFromEntity(entity: any) {
return {
Expand Down Expand Up @@ -202,53 +203,56 @@ export const combineEntityDataWithSiblings = <T>(baseEntity: T): T => {
return { [baseEntityKey]: combinedBaseEntity } as unknown as T;
};

export type CombinedSearchResult = {
export type CombinedEntityResult = {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

combineSiblingEntities is now more generalized and less about search specifically so other use cases can just pass in some entities and combine siblings (as long as you provided a gql entity that actually had siblings). Should make it more reusable.

entity: Entity;
matchedFields: MatchedField[];
matchedEntities?: Entity[];
matchedEntities?: Array<Entity>;
};

export function combineSiblingsInSearchResults(
results:
| {
entity: Entity;
matchedFields: MatchedField[];
}[]
| undefined,
) {
const combinedResults: CombinedSearchResult[] | undefined = [];
const siblingsToPair: Record<string, CombinedSearchResult> = {};

// set sibling associations
results?.forEach((result) => {
if (result.entity.urn in siblingsToPair) {
// filter from repeating
// const siblingsCombinedResult = siblingsToPair[result.entity.urn];
// siblingsCombinedResult.matchedEntities?.push(result.entity);
type CombineOptions = {
combine?: boolean;
};

export function combineSiblingEntities(
entities?: Array<Entity>,
{ combine = true }: CombineOptions = {},
): Array<CombinedEntityResult> {
const combinedResults: CombinedEntityResult[] = [];
const siblingsToPair: Set<string> = new Set();

entities?.forEach((entityReadOnly) => {
if (!combine) {
combinedResults.push({ entity: entityReadOnly });
return;
}

if (siblingsToPair.has(entityReadOnly.urn)) {
return;
}

const combinedResult: CombinedSearchResult = result;
const { entity }: { entity: any } = result;
const siblingUrns = entity?.siblings?.siblings?.map((sibling) => sibling.urn) || [];
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) || [];

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[urn] = combinedResult;
});
siblingUrns.forEach((urn) => urn && siblingsToPair.add(urn));
}

combinedResults.push(combinedResult);
});

return combinedResults;
}

// used to determine whether sibling entities should be shown merged or not
export const SEPARATE_SIBLINGS_URL_PARAM = 'separate_siblings';

Expand Down
1 change: 1 addition & 0 deletions datahub-web-react/src/app/home/HomePageHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,7 @@ export const HomePageHeader = () => {
autoCompleteStyle={styles.searchBox}
entityRegistry={entityRegistry}
viewsEnabled={viewsEnabled}
combineSiblings
showQuickFilters
/>
{searchResultsToShow && searchResultsToShow.length > 0 && (
Expand Down
1 change: 0 additions & 1 deletion datahub-web-react/src/app/preview/DefaultPreviewCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -304,7 +304,6 @@ export default function DefaultPreviewCard({
/>
)}
</EntityTitleContainer>

{degree !== undefined && degree !== null && (
<Tooltip
title={`This entity is a ${getNumberWithOrdinal(degree)} degree connection to ${
Expand Down
42 changes: 24 additions & 18 deletions datahub-web-react/src/app/search/SearchBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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 './utils/combineSiblingsInAutoComplete';

const StyledAutoComplete = styled(AutoComplete)`
width: 100%;
Expand Down Expand Up @@ -88,15 +89,6 @@ const QUICK_FILTER_AUTO_COMPLETE_OPTION = {
],
};

const renderItem = (query: string, entity: Entity) => {
return {
value: entity.urn,
label: <AutoCompleteItem query={query} entity={entity} />,
type: entity.type,
style: { padding: '12px 12px 12px 16px' },
};
};

const renderRecommendedQuery = (query: string) => {
return {
value: query,
Expand All @@ -123,6 +115,7 @@ interface Props {
hideRecommendations?: boolean;
showQuickFilters?: boolean;
viewsEnabled?: boolean;
combineSiblings?: boolean;
setIsSearchBarFocused?: (isSearchBarFocused: boolean) => void;
onFocus?: () => void;
onBlur?: () => void;
Expand All @@ -149,6 +142,7 @@ export const SearchBar = ({
hideRecommendations,
showQuickFilters,
viewsEnabled = false,
combineSiblings = false,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so we default want to not combine siblings in our search bar? is there somewhere where we particularly don't want them combined?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right the idea was to make siblings an opt-in behavior, where the home/searchResults pages have that behavior but not assume that all use cases in the UI would get that behavior was my thought.

setIsSearchBarFocused,
onFocus,
onBlur,
Expand Down Expand Up @@ -227,14 +221,26 @@ export const SearchBar = ({
];
}, [showQuickFilters, suggestions.length, effectiveQuery, selectedQuickFilter, entityRegistry]);

const autoCompleteEntityOptions = useMemo(
() =>
suggestions.map((entity: AutoCompleteResultForEntity) => ({
label: <SectionHeader entityType={entity.type} />,
options: [...entity.entities.map((e: Entity) => renderItem(effectiveQuery, e))],
})),
[effectiveQuery, suggestions],
);
const autoCompleteEntityOptions = useMemo(() => {
return suggestions.map((suggestion: AutoCompleteResultForEntity) => {
const combinedSuggestion = combineSiblingsInAutoComplete(suggestion, { combine: combineSiblings });
return {
label: <SectionHeader entityType={combinedSuggestion.type} />,
options: combinedSuggestion.combinedEntities.map((combinedEntity) => ({
value: combinedEntity.entity.urn,
label: (
<AutoCompleteItem
query={effectiveQuery}
entity={combinedEntity.entity}
siblings={combineSiblings ? combinedEntity.matchedEntities : undefined}
/>
),
type: combinedEntity.entity.type,
style: { padding: '12px 12px 12px 16px' },
})),
};
});
}, [combineSiblings, effectiveQuery, suggestions]);

const previousSelectedQuickFilterValue = usePrevious(selectedQuickFilter?.value);
useEffect(() => {
Expand Down
1 change: 1 addition & 0 deletions datahub-web-react/src/app/search/SearchHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ export const SearchHeader = ({
entityRegistry={entityRegistry}
setIsSearchBarFocused={setIsSearchBarFocused}
viewsEnabled={viewsEnabled}
combineSiblings
fixAutoComplete
showQuickFilters
/>
Expand Down
3 changes: 2 additions & 1 deletion datahub-web-react/src/app/search/SearchResultList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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)`
&&& {
Expand Down
2 changes: 1 addition & 1 deletion datahub-web-react/src/app/search/SearchResults.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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;
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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`
Expand All @@ -44,30 +40,40 @@ const Subtype = styled.span`
interface Props {
query: string;
entity: Entity;
siblings?: Array<Entity>;
hasParentTooltip: boolean;
}

export default function AutoCompleteEntity({ query, entity, hasParentTooltip }: Props) {
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 && <PreviewImage preview={false} src={platformLogoUrl} alt={platformName || ''} />) ||
entityRegistry.getIcon(entity.type, 12, IconStyleType.ACCENT);

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];

return (
<AutoCompleteEntityWrapper>
<ContentWrapper>
{icon}
<IconsContainer>
{entities.map((ent) => (
<AutoCompleteEntityIcon key={ent.urn} entity={ent} scale={iconScale} />
))}
</IconsContainer>
<SuggestionText>
<ParentContainers parentContainers={orderedParentContainers} />
<ParentContainers parentContainers={parentContainers} />
<Typography.Text
ellipsis={
hasParentTooltip ? {} : { tooltip: { title: displayName, color: 'rgba(0, 0, 0, 0.9)' } }
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import React from 'react';
import styled from 'styled-components';
import { Image } from 'antd';
import { Entity } from '../../../types.generated';
import { getPlatformName } from '../../entity/shared/utils';
import { useEntityRegistry } from '../../useEntityRegistry';
import { IconStyleType } from '../../entity/Entity';

const PreviewImage = styled(Image)`
width: auto;
object-fit: contain;
background-color: transparent;
`;

type Props = {
entity: Entity;
scale: number;
};

const DEFAULT_ICON_SIZE = 12;
const DEFAULT_PREVIEW_IMAGE_SIZE = 22;

const AutoCompleteEntityIcon = ({ entity, scale }: 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 && (
<PreviewImage
preview={false}
src={platformLogoUrl}
alt={platformName || ''}
style={{
width: previewImageSize,
height: previewImageSize,
}}
/>
)) ||
entityRegistry.getIcon(entity.type, iconFontSize, IconStyleType.ACCENT);

return icon;
};

export default AutoCompleteEntityIcon;
12 changes: 10 additions & 2 deletions datahub-web-react/src/app/search/autoComplete/AutoCompleteItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,10 @@ export const SuggestionContainer = styled.div`
interface Props {
query: string;
entity: Entity;
siblings?: Array<Entity>;
}

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;
Expand All @@ -33,7 +34,14 @@ export default function AutoCompleteItem({ query, entity }: Props) {
componentToRender = <AutoCompleteTag tag={entity as Tag} />;
break;
default:
componentToRender = <AutoCompleteEntity query={query} entity={entity} hasParentTooltip={displayTooltip} />;
componentToRender = (
<AutoCompleteEntity
query={query}
entity={entity}
siblings={siblings}
hasParentTooltip={displayTooltip}
/>
);
break;
}

Expand Down
Loading