Multi Index Search
On this page
You are currently reading the documentation for InstantSearch.js V4. Read our migration guide to learn how to upgrade from V3 to V4. You can still access the V3 documentation for this page.
Overview
You might want to search across multiple indices at the same time. InstantSearch.js has a built-in solution for this use case: the index
widget. Note that widgets scoped under an index
widget target the Algolia index that the index
widget provides. index
widgets that are lower in the widget tree inherit their search parameters from preceding widgets. The index
widget is really useful whenever you want to:
- display hits from different indices
- share a single
searchBox
- build an autocomplete menu targeting multiple indices
In this guide, you’ll learn how to display results from multiple indices using a single searchBox
. Specifically, you’ll learn how to do this for an autocomplete search implementation.
The source code for both examples can be found on our GitHub repository of examples.
Search in multiple indices
In this first example, we use a single searchBox
to search into multiple indices. The first hits
widget is added at the top level, while the second one is scoped under an index
widget. The second hits
widget is restricted to display results from the Algolia index referenced by the index
widget that precedes it.
In the code below, the first hits
widget displays results from the top level instant_search
index, while the second hits
widget displays results from the instant_search_price_desc
index .
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
const search = instantsearch({
indexName: 'instant_search',
searchClient,
});
search.addWidgets([
instantsearch.widgets.searchBox({
container: '#searchbox',
}),
instantsearch.widgets.hits({
container: '#hits-instant-search',
templates: {
item:
'{{#helpers.highlight}}{ "attribute": "name" }{{/helpers.highlight}}',
},
}),
instantsearch.widgets
.index({ indexName: 'instant_search_price_desc' })
.addWidgets([
instantsearch.widgets.hits({
container: '#hits-instant-search-price-desc',
templates: {
item:
'{{#helpers.highlight}}{ "attribute": "name" }{{/helpers.highlight}}',
},
}),
]),
]);
search.start();
Any widget can be scoped under an index
. In the following example we want to display a different number of hits for our two sets of results. The instant_search
index displays 8 results and instant_search_price_desc
16 results. To restrict the number of results per page we use the configure
widget. Each widget is scoped under the level that is targeted.
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
const search = instantsearch({
indexName: 'instant_search',
searchClient,
});
search.addWidgets([
instantsearch.widgets.configure({
hitsPerPage: 8,
}),
instantsearch.widgets.searchBox({
container: '#searchbox',
}),
instantsearch.widgets.hits({
container: '#hits-instant-search',
templates: {
item:
'{{#helpers.highlight}}{ "attribute": "name" }{{/helpers.highlight}}',
},
}),
instantsearch.widgets
.index({ indexName: 'instant_search_price_desc' })
.addWidgets([
instantsearch.widgets.configure({
hitsPerPage: 16,
}),
instantsearch.widgets.hits({
container: '#hits-instant-search-price-desc',
templates: {
item:
'{{#helpers.highlight}}{ "attribute": "name" }{{/helpers.highlight}}',
},
}),
]),
]);
search.start();
You can find the complete example on our GitHub examples repository.
autocomplete
For this second use case, we are building an autocomplete that searches into multiple indices. The focus of this guide is on search into multiple indices. We do not cover the autocomplete implementation in depth because it has its own guide. The autocomplete is built with Selectize and the autocomplete
connector. This guide differs from the previous one in the way hits are appended to the autocomplete.
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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
const autocomplete = instantsearch.connectors.connectAutocomplete(
({ indices, refine, widgetParams }, isFirstRendering) => {
const { container } = widgetParams;
if (isFirstRendering) {
container.html('<select id="ais-autocomplete"></select>');
container.find('select').selectize({
options: [],
labelField: 'name',
valueField: 'name',
optgroups: indices.map((index, idx) => ({
$order: idx,
id: index.indexId,
name: index.indexId,
})),
optgroupField: 'section',
optgroupLabelField: 'name',
optgroupValueField: 'id',
highlight: false,
onType: refine,
onBlur() {
refine(this.getValue());
},
onChange: refine,
score() {
return function() {
return 1;
};
},
render: {
option: hit => `
<div class="hit">
${instantsearch.highlight({ attribute: 'name', hit })}
</div>
`,
},
});
return;
}
const [select] = container.find('select');
select.selectize.clearOptions();
indices.forEach(index => {
if (index.hits.length) {
index.hits.forEach(hit =>
select.selectize.addOption(
Object.assign({}, hit, {
section: index.indexId,
})
)
);
}
});
select.selectize.refreshOptions(select.selectize.isOpen);
}
);
Once we have our autocomplete component we can use it in our application. The component will have access to both indices: the top level one but the also the one created by the index
widget. It also leverages the configure
widget to reduce the number of hits.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
const search = instantsearch({
indexName: 'instant_search',
searchClient,
});
search.addWidgets([
instantsearch.widgets.index({ indexName: 'instant_search_price_desc' }),
instantsearch.widgets.configure({
hitsPerPage: 3,
}),
autocomplete({
container: $('#autocomplete'),
}),
]);
search.start();
You can find the complete example on GitHub.