Few months ago, I was looking for information concerning auto-completion with ElasticSearch as a source. I found many solutions, but none of them really fit my needs. Nevertheless, it helped me building my request.
What’s the situation Doc?
I needed to be able to extract values from an indexed field in ElasticSearch according to a group of letters (user input). In fact, my indexed document contains a field named map which is an array of string. The idea is the following: if the user is looking for the value « name » for instance, let’s say he will first type « na ». So here we must be able to suggest searches to the user. Furthermore, with this 2 letters, we must proposed existing fields like « name », « native » or « nature ».
I’m using Elastical to interact with ElasticSearch.
First, I’m building a regex which will be used later in the request.
var search = req.body.searched.toLowerCase(), firstLetter = search.charAt(0), rest = req.body.searched.slice(1), reg = "^["+firstLetter+firstLetter.toUpperCase()+"]"+rest+".*";
The regex is build to match with both an upper case char or a lower case one in first position.
What about the request?
var request = { query: { query_string: { default_field: "map", default_operator: "AND", query: req.body.searched+"*" } }, facets:{ map:{ terms:{ field: "map.exact", regex: reg, size: 10 } } } }
First, I’m asking ElasticSearch to retrieve all documents which match req.body.searched+ »* » where req.body.searched contains the user input. I’ve change the default operator to « AND » rather than « OR » in order to be able to deal with fields like « Nom de la gare » or « Name of the dog ». By default, ElasticSearch uses the « OR » operator, so it will ask for « name » OR « of » OR « the » OR « dog »; which is not what I wanted.
Then, I’m using facets to retrieve values in the field map of found documents matching the given regex. I’m using map.exact for the same reason I must use the « AND » operator.
This request works great with on the tests I’ve made. Remains to be seen if it can handle big indexes.
I can now ask ElasticSearch with Elastical and build a clean response:
elastical.search(request, function (err, results, full) { var terms = []; async.forEach(full.facets.map.terms, function(data, callback) { terms.push(data.term); callback(); }, function(err) { res.send(terms); }); });