So you know what Elasticsearch is and wonder, how you can improve your website search with it?

Let update our search from the previous article to have an autocomplete feature.

What is Autocomplete?

Autocomplete is the nice to have feature for your search forms, which allow you to guide a user with suggestions while he tries to write search query on your website.

N-Grams

In order to use autocomplete we will send user input to our backend as they type it in the input field. That input usually includes phrases in the various combinations.

For the best possible result here we need to use n-grams to generate effective indexes on words combinations in the original texts.

We will create an extra EdgeNgramField field on the search index to store that data based on the Question question_text field:

class QuestionIndex(indexes.SearchIndex, indexes.Indexable):

...

text_auto = indexes.EdgeNgramField(model_attr='question_text')

...

As we have changed our index schema we need to rebuild index:

./manage.py rebuild_index

Now we can do a query using Haystack autocomplete() method:

SearchQuerySet().autocomplete(text_auto='old')

Also, let’s change Haystack settings a bit so we have any updates in our models being sent to Elasticsearch immediately as they appeared. So if you add, update or delete Questions they will send to the Elasticsearch immediately:

# Update Search index in realtime (using models.db.signals)

HAYSTACK_SIGNAL_PROCESSOR = 'haystack.signals.RealtimeSignalProcessor'

That is basically all we need to do on the Elasticsearch/Haystack side.

Autocomplete View

In order to handle autocomplete requests lets create an endpoint /autocomplete?q=<> `, which will respond with the JSON list of the questions matched user input.

So if user type query elastic djan it will respond with the JSON list of the appropriate questions list. For ex.:

{

"results":

[

"Django Elasticsearch",

"Poll App with Elastcisearch and Django"

]

The view is pretty simple, it takes q GET query, makes an autocomplete search and returns a string representation of the 5 most relevant questions as a JSON response:

def autocomplete(request):

max_items = 5

q = request.GET.get('q')

if q:

sqs = SearchQuerySet().autocomplete(text_auto=q)

results = [str(result.object) for result in sqs[:max_items]]

else:

results = []



return JsonResponse({

'results': results

})

Let’s add that view to urlpatterns in the urls.py file:

urlpatterns = [

...

path('autocomplete/', views.autocomplete, name='autocomplete'),

]

That’s it on the backend side.

Handle Autocomplete on the Frontend

There plenty of JS helpers do that. We will reuse jQuery Autocomplete from a Haystack example:

<form action="{% url 'haystack_search' %}" class="autocomplete">

<label for="q">Search</label>

<input id="q" name="q" type="text" autocomplete="off">

<input type="submit" value="Search!">

</form>





<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>

<script src="{% static 'polls/js/autocomplete.js' %}"></script>



<script type="text/javascript">

$(function () {

window.autocomplete = new Autocomplete({

form_selector: '.autocomplete',

url: '{% url 'polls:autocomplete' %}'

});

window.autocomplete.setup()

});

</script>

What we do here? We have a form with the input field which is a source for our autocomplete query.

As soon as the user typed query string longer than 3 symbols we send a request to our /autocomplete endpoint in order to find something related.

If we found something — we will display it under the input field which can be chosen to do a search query.

That is it.