InstantSearch / InstantSearch.js / V3 / Guides

Multi Index Search | InstantSearch.js V3 (Deprecated)

This version of InstantSearch.js has been deprecated in favor of the latest version of InstantSearch.js.

It might happen that you want to search across multiple indices at the same time. InstantSearch.js has a built-in solution for searching into multiple indices with an autocomplete component. Another common use case is to display hits from multiple indices at the same time. There isn’t a proper API for this use case but you can synchronize two InstantSearch.js instance to have the same behavior.

This guide shows how to share a single searchBox to display multiple hits from different indices. It also takes a look at how to create an autocomplete that targets multiple indices. The source code of both examples can be found on GitHub.

Search in multiple indices

The first use case shares a single searchBox to search into multiple indices. To implement this behavior you can use two InstantSearch.js instances. Each of them target a specific index, the first one is instant_search and the second is instant_search_price_desc.

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
const subIndex = instantsearch({
  indexName: 'instant_search_price_desc',
  searchClient,
});

subIndex.addWidgets([
  instantsearch.widgets.hits({
    container: '#hits-instant-search-price-desc',
    templates: {
      item: '{{#helpers.highlight}}{ "attribute": "name" }{{/helpers.highlight}}',
    },
  })
]);

const mainIndex = instantsearch({
  indexName: 'instant_search',
  searchClient,
});

mainIndex.addWidgets([
  instantsearch.widgets.searchBox({
    container: '#searchbox',
  }),

  instantsearch.widgets.hits({
    container: '#hits-instant-search',
    templates: {
      item: '{{#helpers.highlight}}{ "attribute": "name" }{{/helpers.highlight}}',
    },
  })
]);

subIndex.start();
mainIndex.start();

Now that the two InstantSearch.js instances are set up, you need to sync them. Right now when a user types inside the searchBox only the first index is updated. The goal is to be able to search inside both indices at the same time. To do so, you can use the searchFunction option of InstantSearch.js. You can use this hook to apply the query of the first index to the second one.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const subIndex = instantsearch({
  indexName: 'instant_search_price_desc',
  searchClient,
});

const mainIndex = instantsearch({
  indexName: 'instant_search',
  searchFunction(helper) {
    subIndex.helper.setQuery(helper.state.query).search();
    helper.search();
  },
  searchClient,
});

// ...

Now the two InstantSearch.js instances are in sync. Note that since it’s two dedicated instances you can apply different parameters and widgets to the search. The following example displays a different number of results in two results sets. The instant_search index will display 8 results and instant_search_price_desc 16 results. To restrict the number of results per page you can use the configure widget.

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
const subIndex = instantsearch({
  indexName: 'instant_search_price_desc',
  searchClient,
});

subIndex.addWidgets([
  instantsearch.widgets.configure({
    hitsPerPage: 16,
  })
]);

// ...

const mainIndex = instantsearch({
  indexName: 'instant_search',
  searchClient,
  searchFunction(helper) {
    subIndex.helper.setQuery(helper.state.query).search();
    helper.search();
  },
});

mainIndex.addWidgets([
  instantsearch.widgets.configure({
    hitsPerPage: 8,
  })
]);

// ...

You can find the complete example on GitHub.

Autocomplete

This second use case builds an autocomplete that searches into multiple indices. The focus of this guide is on search into multiple indices. It doesn’t cover the autocomplete implementation in depth because it has its own guide. The autocomplete is built with Selectize and the autocomplete connector. The only difference with the previous guide is how to append the hits 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
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: [],

        optgroups: indices.map((index, idx) => ({
          $order: idx,
          id: index.index,
          name: index.index,
        })),
        labelField: 'name',
        valueField: 'name',
        searchField: 'name',
        optgroupField: 'section',
        optgroupLabelField: 'name',
        optgroupValueField: 'id',
        highlight: false,
        onType: refine,
        onChange: refine,
        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.results) {
        index.results.hits.forEach(hit =>
          select.selectize.addOption(
            Object.assign({}, hit, {
              section: index.index,
            })
          )
        );
      }
    });
    select.selectize.refreshOptions(select.selectize.isOpen);
  }
);

Once you create the autocomplete component you can use it in your application. The component will have access to both indices. It will also leverage the configure to reduce the number of hits.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
const search = instantsearch({
  indexName: 'instant_search',
  searchClient,
});

search.addWidgets([
  instantsearch.widgets.configure({
    hitsPerPage: 3,
  }),

  autocomplete({
    container: $('#autocomplete'),
    indices: [
      {
        value: 'instant_search_price_desc',
        label: 'instant_search_price_desc',
      },
    ],
  })
]);

search.start();

You can find the complete example on GitHub.

Did you find this page helpful?