From e5644c7f5c3450df4c78d586fc0c45ea40c4e3b2 Mon Sep 17 00:00:00 2001 From: Conrad VanLandingham Date: Tue, 9 Apr 2024 09:23:23 -0500 Subject: [PATCH] Improve search result sorting and add subtitles to results (#212) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## 🐛 Issue Since we have two indices, search results always return with web page results first, and content item results second. This makes it hard to the user to understand which results to look at, and without any other result context (like summaries) the quality of the search results feel very poor. ## ✏️ Solution 1. Combine multi-index results into a single array. This is hacky, and isn't a permanent solution. Best case - the results end up in a much better order. Worse case - they end up in a similar order to they are already. 2. Add summary/subtitles to results 3. Remove "Search history" section if you have search results As a follow up, @vinnyjth is going to document work to be done on the shovel to create a new, combined index that we can start tweaking to more dramatically improve search results. Also, we will need to pursue a minor (hopefully) refactor for search on the web. Currently, all search is done through the autocomplete API on Algolia, not the instantsearch api. This means that features like snippeting is not available, and we also can not use any of Algolia's out of the box react components. Not using algolia best practices, unfortunately. ## 🔬 To Test 1. Boot up web embeds 2. Test search ## 📸 Screenshots | | Before | After | | --- | --- | --- | | **Best Case:** "Take Heart" content item is now listed first| ![Screenshot 2024-04-09 at 9.01.24 AM.png](https://graphite-user-uploaded-assets-prod.s3.amazonaws.com/Iqv8SYVwCDX0dUbjBiDz/c8b282f9-faef-4d6e-9f31-b8de18338b4e.png) | ![Screenshot 2024-04-09 at 9.00.07 AM.png](https://graphite-user-uploaded-assets-prod.s3.amazonaws.com/Iqv8SYVwCDX0dUbjBiDz/a1b8a106-7f5c-4956-abab-847157fe0aaa.png) | | **Worse Case:** The "Campus Locations" result is the best result, but still shown last| ![Screenshot 2024-04-09 at 9.02.50 AM.png](https://graphite-user-uploaded-assets-prod.s3.amazonaws.com/Iqv8SYVwCDX0dUbjBiDz/c9c142b1-11a9-4fb1-9cb6-58c556508526.png) | ![Screenshot 2024-04-09 at 9.00.36 AM.png](https://graphite-user-uploaded-assets-prod.s3.amazonaws.com/Iqv8SYVwCDX0dUbjBiDz/7b32df41-eb3a-43e2-ab96-799c6d36955d.png) | --- .../components/Searchbar/Autocomplete.js | 2 + .../components/Searchbar/SearchResults.js | 109 ++++++++++++------ 2 files changed, 75 insertions(+), 36 deletions(-) diff --git a/packages/web-shared/components/Searchbar/Autocomplete.js b/packages/web-shared/components/Searchbar/Autocomplete.js index 8e223079..8b5edc1d 100644 --- a/packages/web-shared/components/Searchbar/Autocomplete.js +++ b/packages/web-shared/components/Searchbar/Autocomplete.js @@ -152,6 +152,7 @@ export default function Autocomplete({ facetFilters: [`church:${searchState.church}`], hitsPerPage: 4, clickAnalytics: true, + getRankingInfo: true, // highlightPreTag: '', // highlightPostTag: '', }, @@ -182,6 +183,7 @@ export default function Autocomplete({ params: { hitsPerPage: 8, clickAnalytics: true, + getRankingInfo: true, // highlightPreTag: '', // highlightPostTag: '', }, diff --git a/packages/web-shared/components/Searchbar/SearchResults.js b/packages/web-shared/components/Searchbar/SearchResults.js index b2ff779e..792517cc 100644 --- a/packages/web-shared/components/Searchbar/SearchResults.js +++ b/packages/web-shared/components/Searchbar/SearchResults.js @@ -171,6 +171,39 @@ const SearchResults = ({ autocompleteState, autocomplete }) => { // Makes SSR consistent on aria aspects const inputProps = autocomplete.getInputProps({}); + // Hack: join results from each collection back to a single array, and re-sort them by Algolia's ranking + // TODO: Remove once we have a single index for all content + const allResults = []; + autocompleteState.collections + .filter((collection) => ['pages', 'content'].includes(collection.source.sourceId)) + .forEach((collection) => { + allResults.push(...collection.items.map((item) => ({ ...item, source: collection.source }))); + }); + // Algolia adds a _rankingInfo property to each item, which we can use to sort the results + // We want to sort the results first by nbExactWords (desc), then proximityDistance (asc), then last by userScore (desc) + // This is super hacky and results are not to be guaranteed, but should improve results over displaying them in the order they come back + allResults.sort((a, b) => { + if (a._rankingInfo.nbExactWords > b._rankingInfo.nbExactWords) { + return -1; + } + if (a._rankingInfo.nbExactWords < b._rankingInfo.nbExactWords) { + return 1; + } + if (a._rankingInfo.proximityDistance < b._rankingInfo.proximityDistance) { + return -1; + } + if (a._rankingInfo.proximityDistance > b._rankingInfo.proximityDistance) { + return 1; + } + if (a._rankingInfo.userScore > b._rankingInfo.userScore) { + return -1; + } + if (a._rankingInfo.userScore < b._rankingInfo.userScore) { + return 1; + } + return 0; + }); + return ( { Trending Searches )} - {collection.source.sourceId === 'recentSearchesPlugin' && ( + {collection.source.sourceId === 'recentSearchesPlugin' && !inputProps.value && ( Search History @@ -240,7 +273,10 @@ const SearchResults = ({ autocompleteState, autocomplete }) => { /> ); - } else if (collection.source.sourceId === 'recentSearchesPlugin') { + } else if ( + collection.source.sourceId === 'recentSearchesPlugin' && + !inputProps.value + ) { return (
  • { ); } - - // Rendering of regular items - - return autocompleteState.query !== '' ? ( -
    -
      - {items.map((item) => ( - - { - if (collection.source.sourceId === 'pages') { - return handleStaticActionPress(item); - } - return handleActionPress(item); - }} - background="none" - /> - - ))} -
    -
    - ) : null; })} + { + // Rendering of regular items + autocompleteState.query !== '' ? ( +
    +
      + {allResults.map((item) => ( + + { + if (collection.source.sourceId === 'pages') { + return handleStaticActionPress(item); + } + return handleActionPress(item); + }} + background="none" + /> + + ))} +
    +
    + ) : null + } {autocompleteState.isOpen && autocompleteState.query === '' && searchState.searchFeed ? (