Building Query Suggestions UI
On this page
Overview
In order to help users with their search, Algolia provides a feature called Query Suggestions. This feature creates an index with the best queries done by the users. You can then use this index to propose suggestions to your users as they are typing into the ais-search-box
. Once you’ve configured the generation of the Query Suggestions index, you need to query this index as well. You can use multi-index search for that.
In this guide we will cover the use case where a search box displays a list of suggestions along with the associated categories. Once the user select a suggestion both the query and the category will be applied.
To build our list of suggestions we leverage the component ais-autocomplete
. We won’t cover in too much details how to integrate an autocomplete with Vue InstantSearch. The autocomplete guide has already a dedicated section on that topic.
Refine your results with the suggestions
The first step of this guide is to setup our custom autocomplete component. To implement it we use the library Vue Autosuggest which provides a component to create an autocomplete menu. Once we have it, we have to wrap the component with the ais-autocomplete
connector. You can find more information in the guide on 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
<template>
<ais-instant-search
:search-client="searchClient"
index-name="instant_search_demo_query_suggestions"
>
<ais-configure :hitsPerPage="5" />
<ais-autocomplete>
<template slot-scope="{ currentRefinement, indices, refine }">
<vue-autosuggest
:suggestions="indicesToSuggestions(indices)"
@selected="onSelect"
:input-props="{
style: 'width: 100%',
onInputChange: refine,
placeholder: 'Search here…',
}"
>
<template slot-scope="{ suggestion }">
<ais-highlight attribute="query" :hit="suggestion.item" />
</template>
</vue-autosuggest>
</template>
</ais-autocomplete>
</ais-instant-search>
</template>
<script>
import { VueAutosuggest } from 'vue-autosuggest';
export default {
components: { VueAutosuggest },
methods: {
onSelect(selected) {
if (selected) {
console.log('selected');
}
},
indicesToSuggestions(indices) {
return indices.map(({ hits }) => ({
data: hits.map(hit => {
return {
...hit,
// this is for Vue AutoSuggest
name: hit.query,
};
}),
}));
},
},
};
</script>
Now that we have our autocomplete component, we can create our multi-index search experience. In this case, the ais-autocomplete
widget read from two indices. The first one is instant_search_demo_query_suggestions
and the second is instant_search
. The first index widget is implied by the creation of the ais-instant-search
widget. You can find more information in the autocomplete guide.
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
61
62
63
64
65
66
67
68
69
70
71
72
<template>
<div>
<ais-instant-search
:search-client="searchClient"
index-name="instant_search_demo_query_suggestions"
>
<ais-configure :hitsPerPage="5" />
<ais-index index-name="instant_search" />
<ais-autocomplete>
<template slot-scope="{ currentRefinement, indices, refine }">
<vue-autosuggest
:suggestions="indicesToSuggestions(indices)"
@selected="onSelect"
:input-props="{
style: 'width: 100%',
onInputChange: refine,
placeholder: 'Search here…',
}"
>
<div slot-scope="{ suggestion }">
<ais-highlight
:hit="suggestion.item"
attribute="query"
v-if="suggestion.item.query"
/>
<ais-highlight
:hit="suggestion.item"
attribute="name"
v-else
/>
<strong v-if="suggestion.item.price">
$ {{ suggestion.item.price }}
</strong>
<img
:src="suggestion.item.image"
style="height: 50px;"
v-if="suggestion.item.image"
/>
</div>
</vue-autosuggest>
</template>
</ais-autocomplete>
</ais-instant-search>
</div>
</template>
<script>
import { VueAutosuggest } from 'vue-autosuggest';
export default {
components: { VueAutosuggest },
methods: {
onSelect(selected) {
if (selected) {
const { query } = selected.item;
this.query = query;
}
},
indicesToSuggestions(indices) {
return indices.map(({ hits }) => ({
data: hits.map(hit => {
return {
...hit,
// this is for Vue Autosuggest
name: hit.query,
};
}),
}));
},
},
};
</script>
That’s it! We have setup our autocomplete multi-index search experience. You should be able to select a suggestion from the autocomplete and use this suggestion to search into the main index.
Use the related categories in the autocomplete
A common pattern with an autocomplete of suggestions is to display the relevant categories along with the suggestions. Then when a user select a suggestion both the suggestion and the associated category are used to refine the search. For this example the relevant categories are stored on the suggestions records. We have to update our render function to display the categories with the suggestions. For simplicity and brevity of the code we assume that all suggestions have categories, but this is not the case in the actual dataset. Take a look at the complete example to see the actual implementation.
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
<template>
<!-- ... -->
<template slot-scope="{ suggestion }">
<span>
<ais-highlight attribute="query" :hit="suggestion.item" />
<span>
<em>
in
<span v-if="suggestion.item.category">
{{ suggestion.item.category.value }}
</span>
<span v-else> All categories </span>
</em>
</span>
</span>
</template>
<!-- ... -->
</template>
<script>
export default {
methods:
indicesToSuggestions(indices) {
return indices.map(({ hits }) => ({
data: hits.map(hit => {
// categories is an array, but we only want the first element
// hit.instant_search is undefined if there are no matched categories
const [category] =
(hit.instant_search &&
hit.instant_search.facets.exact_matches.categories) ||
[];
return {
...hit,
category,
name: hit.query,
};
}),
}));
},
},
};
</script>
Now that we are able to display the categories, we can use them to refine the main search. We use the ais-configure
widget with disjunctiveFacets
and disjunctiveFacetsRefinement
. These two parameter are the same ones as internally used in a refinement list.
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
<template>
<!-- ... -->
<ais-instant-search
:search-client="searchClient"
index-name="instant_search"
>
<!-- optionally to display the selected category -->
<ais-current-refinements />
<ais-configure
:query="query"
:disjunctiveFacets="['categories']"
:disjunctiveFacetsRefinements="disjunctiveFacetsRefinements"
/>
<!-- ... -->
</ais-instant-search>
</template>
<script>
export default {
data() {
return {
query: '',
disjunctiveFacetsRefinements: undefined,
};
},
methods: {
onSelect(selected) {
if (selected) {
const { query, category } = selected.item;
this.query = query;
this.disjunctiveFacetsRefinements = category && {
categories: [category.value],
};
}
},
},
};
</script>
That’s it! Now when a suggestion is selected both the query and the category are applied to the main search.