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

feat(ui/graphql) Add ability to sort search results from search results page #8595

Conversation

chriscollins3456
Copy link
Collaborator

This PR updates our searchAcrossEntities endpoint to accept a new sortInput field that allows users to pass in a way to sort their search results. This endpoint will accept ASCENDING or DESCENDING on any field on an entity (so long as it's stored in ES on the entity index). Right now we have it only built out to accept a singular sorting option, but this sortInput could be extended later to accept a list of fields or something even more complicated.

This adds a dropdown in search results to choose from 4 sorting options we provide in the UI

  1. Default recommended/relevance (what it always has been)
  2. sort descending by name
  3. sort ascending by name
  4. descending by last updated in source system

Adding a new sorting option is very easy, just 2 lines of code change - check out my final commit for an example.

Screenshots:
image

image

sort name ascending
image

sort name descending
image

last modified in source platform
image

Checklist

  • The PR conforms to DataHub's Contributing Guideline (particularly Commit Message Format)
  • Links to related issues (if applicable)
  • Tests for the changes have been added/updated (if applicable)
  • Docs related to the changes have been added/updated (if applicable). If a new feature has been added a Usage Guide has been added for the same.
  • For any breaking change/potential downtime/deprecation/big changes an entry has been made in Updating DataHub

@github-actions github-actions bot added product PR or Issue related to the DataHub UI/UX devops PR or Issue related to DataHub backend & deployment labels Aug 8, 2023
@chriscollins3456 chriscollins3456 changed the title feat(ui) Add ability to sort search results from search results page feat(ui/graphql) Add ability to sort search results from search results page Aug 8, 2023
@@ -456,7 +456,7 @@ public SearchResult searchAcrossEntities(@Nonnull List<String> entities, @Nonnul
@Nonnull
public SearchResult searchAcrossEntities(@Nonnull List<String> entities, @Nonnull String input,
@Nullable Filter filter, int start, int count, @Nullable SearchFlags searchFlags,
@Nonnull final Authentication authentication, @Nullable List<String> facets)
@Nullable SortCriterion sortCriterion, @Nonnull final Authentication authentication, @Nullable List<String> facets)
throws RemoteInvocationException {

final EntitiesDoSearchAcrossEntitiesRequestBuilder requestBuilder =
Copy link
Contributor

Choose a reason for hiding this comment

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

I think the EntitiesDoSearchAcrossEntitiesRequestBuilder actually has a parameter for the sort criterion: Do you want to add it here as well?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

nice good call! adding now

const { filterMode, filterModeRef, setFilterMode } = useFilterMode(filters, unionType);
const { selectedSortOption } = useSearchContext();
Copy link
Contributor

Choose a reason for hiding this comment

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

This looks like a great place for a useSelectedSortOption custom hook

import { DEFAULT_SORT_OPTION, SORT_OPTIONS } from './constants';

export default function SearchContextProvider({ children }: { children: React.ReactNode }) {
const [selectedSortOption, setSelectedSortOption] = useState(DEFAULT_SORT_OPTION);
Copy link
Contributor

Choose a reason for hiding this comment

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

Not sure if I'm missing something, but why do we need another state value here? Couldn't we just map what we need from params into another var and pass that into the context? so updateSelectedSortOption would mutate the url and then params would swap the value for a fresh context object.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

yeah tbh this is because I built it without storing in the URLfirst to start and then added storing in the URL later - but now with fresh eyes I totally agree. I like having this context as it's easier to grab values and set values and we can extend it later for other global search state stuff.

so I think i'll just remove this local state and pass in selectedSortOption as the value from URL params and the setter function to update url params.

Comment on lines 9 to 18
export const SORT_OPTIONS = {
[RECOMMENDED]: { label: 'Recommended', field: RECOMMENDED, sortOrder: SortOrder.Descending },
[`${NAME_FIELD}_${SortOrder.Ascending}`]: { label: 'A to Z', field: NAME_FIELD, sortOrder: SortOrder.Ascending },
[`${NAME_FIELD}_${SortOrder.Descending}`]: { label: 'Z to A', field: NAME_FIELD, sortOrder: SortOrder.Descending },
[`${LAST_OPERATION_TIME_FIELD}_${SortOrder.Descending}`]: {
label: 'Last Modified in Platform',
field: LAST_OPERATION_TIME_FIELD,
sortOrder: SortOrder.Descending,
},
};
Copy link
Contributor

Choose a reason for hiding this comment

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

Soooooo good


export function useSearchContext() {
return useContext(SearchContext);
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Could throw a few other custom hooks in here for selecting off of this context

Comment on lines 1 to 8
import { RouteComponentProps } from 'react-router';

export function updateUrlParam(history: RouteComponentProps['history'], key: string, value: string) {
const url = new URL(window.location.href);
const { searchParams } = url;
searchParams.set(key, value);
history.replace(url.search);
}
Copy link
Contributor

Choose a reason for hiding this comment

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

This feels even more generic than search and could go in a higher-level utils file somewhere.

Copy link
Contributor

Choose a reason for hiding this comment

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

I'm curious how the browse sidebar behaves when we mutate url params without adjusting a filter.... should be fine but I know we got a useEffect in there that kicks off some stuff when params change.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

yeah I could see that and may move this. But I tested the browse sidebar with this functionality and things look solid. it's not a URL param they're listening to so things look unaffected

@@ -13,7 +14,9 @@ export default function AppProviders({ children }: Props) {
<AppConfigProvider>
<UserContextProvider>
<EducationStepsProvider>
<QuickFiltersProvider>{children}</QuickFiltersProvider>
<QuickFiltersProvider>
<SearchContextProvider>{children}</SearchContextProvider>
Copy link
Contributor

Choose a reason for hiding this comment

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

So happy we have this now.

Copy link
Contributor

Choose a reason for hiding this comment

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

This is probably where our subscription state could go in the future if we ever wanted to hoist it up.


export function useSearchContext() {
const context = useContext(SearchContext);
if (context === null) throw new Error(`${useSearchContext.name} must be used under a SearchContextProvider`);
Copy link
Contributor

Choose a reason for hiding this comment

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

For this guard to work you would need to initialize createContext with null I think. Making the type up there SearchContextType | null. Not a big deal since we're not building a library here, just a wire-up for our state.

`;

export default function SearchSortSelect() {
const { selectedSortOption, setSelectedSortOption } = useSearchContext();
Copy link
Contributor

Choose a reason for hiding this comment

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

Could be another opportunity for some custom hooks, non blocking feedback here of course!

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

what would the benefit of using two different custom hooks here? at the end of the day I'm just trying to get these values from our context

Copy link
Contributor

Choose a reason for hiding this comment

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

In this example, not much. But as contexts grow and become more complex, having less usage of the global context and more specific selection of the values becomes cleaner imo.

@chriscollins3456 chriscollins3456 merged commit 265e2a4 into datahub-project:master Aug 9, 2023
35 of 36 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
devops PR or Issue related to DataHub backend & deployment product PR or Issue related to the DataHub UI/UX
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants