🎉 Try the public beta of the new docs site at algolia.com/doc-beta! 🎉
Guides / Building Search UI / UI & UX patterns

Structured results in React InstantSearch

This is the React InstantSearch v7 documentation. React InstantSearch v7 is the latest version of React InstantSearch and the stable version of React InstantSearch Hooks.

If you were using React InstantSearch v6, you can upgrade to v7.

If you were using React InstantSearch Hooks, you can still use the React InstantSearch v7 documentation, but you should check the upgrade guide for necessary changes.

If you want to keep using React InstantSearch v6, you can find the archived documentation.

Why using structured results?

Structured results 0

One of the most interesting recent additions to search result pages are what’s commonly called “structured results.”

Different companies use different names:

Traditional search, which consists of displaying long lists of search results once users press “Enter” on their keyboard, usually falls short of piquing their curiosity and triggering engagement. This is when new ways of searching emerge.

Instead, a successful search should attempt to communicate clearly and cleanly with a user on multiple levels when applicable, where different types of results help guide users to the information they want. Sometimes, it’s desirable to showcase a particular piece of relevant content against the rest of a user’s standard query search results. This is why many believe in the value of leveraging “structured results”.

How about your search?

Uses for structured results

You’ll want to use structured results whenever there is a singular piece of information you want to stand out from the rest for a given query.

Here are a few examples:

  • Movie titles on an entertainment website
  • User details and avatar on a customer relationship management (CRM) system
  • Sponsored brands on an ecommerce search (see example and code on GitHub).

You can see a common thread among these examples - a need to search two different types of structured data - and Algolia happens to handle these cases out of the box.

An example

First, take a look at the ecommerce example mentioned in the preceding section. You can view the code and follow along on GitHub.

Imagine a scenario where certain brands within your ecommerce site have requested that their brand will be displayed if it happens to match a user’s query. In your search, if you determine that someone is searching for a particular brand name, you can embed a section displaying the brand and its name within the main search interface.

How will the data look?

One index will be the main index to host traditional results - in your case, tech products - regardless of a match on the brand. Another index will be used to store your brands as records like so:

1
2
3
4
5
{
  "name": "Bosch",
  "url": "https://logo.clearbit.com/bosch.com",
  "objectID": "3414165441"
}

Tweaking the relevance

Now that you added a new index for structured results to use in conjunction with your main index, outline what you’d like to emphasize in your structured results:

  • Only display a single result that matches a user’s search
  • Continue to handle typos, but be more stringent
  • Ensure that you have all words matching in order (so “The Bad News” and “The Bad News Bears” would match but “Bears Bad News” would not)

Next, determine the corresponding settings that would be configured in Algolia.

In this case, you only need to care about results with a minimal number of typos, especially if you only render one result:

Structured results 2

The corresponding code for this dashboard configuration would be:

1
2
3
4
5
index.setSettings({
  typoTolerance: 'min'
}).then(() => {
  // done
});

Next, decrease your typo tolerance by increasing the minimum characters from 4 to 5 for a single typo:

Structured results 1

And the corresponding code for this dashboard configuration would be:

1
2
3
index.setSettings({ minWordSizefor1Typo: 5 }).then(() => {
  // done
});

These configuration changes make the typo tolerance more stringent. The next section addresses the last point: ensuring that all words match in order.

Displaying the data

Federated search makes it easy to perform a single query against several datasets. You can leverage it with respect to your structured brand result:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import algoliasearch from 'algoliasearch/lite';
import {
  InstantSearch,
  Index,
  Configure
} from 'react-instantsearch';

const searchClient = algoliasearch(
  YourApplicationID,
  YourSearchOnlyAPIKey
);

function App() {
  return (
    <InstantSearch indexName="mainIndex" searchClient={searchClient}>
      <Index indexName="structuredIndex">
        <Configure hitsPerPage={1} getRankingInfo={true} />
        {/* We'll add the structured results widget here */}
      </Index>
      {/* other widgets */}
    </InstantSearch>
  )
}

In this snippet, you’re taking advantage of the <Index> widget to nest multiple indices that share the same UI state (or “search state”). When a user searches in the app, it searches in both the main and the structured indices.

You can use the <Configure> widget to:

  • Stay on the first page (0 since page is zero-based) because the main index can be paginated, and the structured index would then “inherit” from its pagination state.
  • Set hitsPerPage to 1 because you only show the first result.
  • Set getRankingInfo to true so that Algolia returns metadata about the hit’s ranking.

When rendering results, take the first result with the useHits() connector and do a conditional check:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import { useHits } from 'react-instantsearch';

function StructuredResults() {
  const { hits } = useHits();
  const result = hits[0];

  if (
    result &&
    result._rankingInfo.words - 1 === result._rankingInfo.proximityDistance
  ) {
    return (
      <div>
        <img src={result.url} alt={result.name} />
        <h3>${result.name}</h3>
      </div>
    );
  }

  return null;
}

// ...

function App() {
  return (
    <InstantSearch indexName="mainIndex" searchClient={searchClient}>
      <Index indexName="structuredIndex">
        <Configure hitsPerPage={1} getRankingInfo={true} />
        <StructuredResults />
      </Index>
      {/* other widgets */}
    </InstantSearch>
  )
}

By comparing the number of exact word matches to 1 + the total distance between all the words in the match, you can make sure that all matched words are sorted. In other terms, when all matching words against a user’s query are sequential and adjacent, the proximity (or distance between search terms) is always equal to exact word matches - 1

There’s no requirement as to when to display your content, this part is up to you.

Another constraint you may impose on the rendering of the structured result could be to check that the count of exact word matches is equal to the number of words in the value of the attribute being queried. For example, you may only want to show a brand when all words of that brand have been typed. Check the getRandkingInfo documentation to learn about other useful metrics you can access in the results.

Another example, a different use case

Now imagine you’re iterating on an entertainment website that displays structured results on movies: if you can determine that someone is searching for a specific movie, you will embed a section displaying the movie details within the main search interface. That is, you’d like to communicate to the user that there might be a salient piece of content worth showcasing.

How will the data look?

Consider how you should structure your data. First, you need a main index, as with the ecommerce example. This is where people will always see traditional results even if there’s no structured data match.

Think about a major search engine when you search a new movie: you might get structured results with the cast, showtimes, and video clips at the same time. In Algolia, that would require three extra indices—one for each result type.

This example is a bit simpler, as it only has a single data type to show structured results: movies. Yet, you can imagine a circumstance where you would match on actors, directors, etc.

Here’s how a sample movie record could look like:

1
2
3
4
5
6
7
8
9
10
{
  "title": "The Shawshank Redemption",
  "year": 1994,
  "image": "https://image.tmdb.org/t/p/w154/9O7gLzmreU0nGkIB6K3BsJbzvNv.jpg",
  "color": "#8C634B",
  "score": 9.97764206054169,
  "rating": 5,
  "actors": ["Tim Robbins", "Morgan Freeman", "Bob Gunton"],
  "objectID": "439817390"
}

Records with this structure would be stored in another index. Keep in mind though that if your new index doesn’t contain any new data, it could be a replica of your primary index. (for example, searchable attributes or custom ranking order).

You can display the data and render the result exactly the same way as in the previous ecommerce example.

Conclusion

The movie and ecommerce examples you went over are just two scenarios among many that may call for structured data, all following a similar pattern of giving users the ability to search through multiple indices and showcase results accordingly.

Search paradigms continue to evolve, and the rise of “structured results” is part of an ongoing itch to enrich a user’s process of discovery while browsing content. Algolia enables users the flexibility to roll out their own flavor of “structured results” by making simple the ability to search against multiple types of content and customizing how the search is performed.

Did you find this page helpful?