Inject Hits from Another Algolia Index
Content injection consists of inserting data between search results. With React InstantSearch, you can build a custom widget to mix results from two or more indices into a single set of hits.
For example, if you’re building a search experience for ingredients, you might want to inject recipes using those ingredients. These recipes would live in a secondary index, and receive search parameters such as the query and facets.
Requirements
React InstantSearch exposes a connector API that lets you reuse existing logic and plug your own. With it, you can build a custom React InstantSearch widget to mix regular Algolia results with injected content. Make sure you’re familiar with this concept before continuing to the next section.
Set up indices
To find related recipes when searching for ingredients, they need to match the same queries.
For example, in your “ingredients” records, you can use a unique name to identify each ingredient. Then, you can use the same name in the list of ingredients for each recipe in the “recipes” index.
1
2
3
4
5
6
7
8
[
{
"ingredient": "sesame seeds",
"title": "Sesame",
"summary": "Sesame is a flowering plant in the genus Sesamum, also called benne..."
},
// ...
]
Make sure you declare any attribute you with to search into as searchableAttributes
.
Inject custom content between hits
To mix hits from the two indices, you first need to make sure to register them all by mounting an index-widget
widget. You can use a nested configure
widget to define what search parameters to use and what to inherit from the parent index.
In the following example, the index “recipes” always returns a single index from the first page, but receives the query, the facets, and any other parameter defined higher in the component tree.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import React from 'react';
import {
InstantSearch,
Configure,
Index,
SearchBox,
} from 'react-instantsearch-dom';
function App() {
return (
<InstantSearch searchClient={searchClient} indexName="ingredients">
<Configure hitsPerPage={8} />
<Index indexName="recipes">
<Configure hitsPerPage={1} page={0} />
</Index>
<SearchBox />
</InstantSearch>
);
}
Then, you can display results from “ingredients” and inject results from “recipes”. In the following example, there’s a slot that injects hits from the “recipes” index in position 3.
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
35
36
37
38
39
40
41
42
43
import React from 'react';
import {
InstantSearch,
Configure,
Index,
SearchBox,
} from 'react-instantsearch-dom';
import { InjectedHits } from './InjectedHits';
function App() {
return (
<InstantSearch searchClient={searchClient} indexName="ingredients">
<Configure hitsPerPage={8} />
<Index indexName="recipes">
<Configure hitsPerPage={1} page={0} />
</Index>
<SearchBox />
<InjectedHits
slots={() => [
{
injectAt: 3,
getHits: ({ resultsByIndex }) => resultsByIndex.recipes?.hits || [],
slotComponent: RecipeHit,
},
]}
hitComponent={IngredientHit}
/>
</InstantSearch>
);
}
function RecipeHit() {
return (
{/* ... */}
);
}
function IngredientHit() {
return (
{/* ... */}
);
}
Regular hits from “ingredients” use the component defined with hitComponent
, while injected hits use the slotComponent
.
Dynamic positioning with hits
Instead of passing a static position to injectAt
, you can store a dynamic position in hits, along with their data, and use it in the code.
If you inject multiple slots at the same position, they’re injected in the defined slot order.
1
2
3
4
5
6
7
8
9
[
{
"position": 3,
"title": "Butter chicken",
"ingredients": [
// ...
]
}
]
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
35
36
37
import React from 'react';
import {
InstantSearch,
Configure,
Index,
SearchBox,
} from 'react-instantsearch-dom';
import { InjectedHits } from './InjectedHits';
function App() {
return (
<InstantSearch searchClient={searchClient} indexName="ingredients">
<Configure hitsPerPage={8} />
<Index indexName="recipes">
<Configure hitsPerPage={1} page={0} />
</Index>
<SearchBox />
<InjectedHits
slots={({ resultsByIndex }) => {
const [recipe] = resultsByIndex.recipes?.hits || [];
return [
{
injectAt: recipe.position,
getHits: () => [recipe].filter(Boolean),
slotComponent: RecipeHit,
},
];
}}
hitComponent={IngredientHit}
/>
</InstantSearch>
);
}
// ...