Search code examples
elasticsearchbootstrap-typeaheadtypeahead.jstypeahead

Typeahead/Rails: Not working


I am using Elasticsearch & Typeahead in my Rails application to perform autocomplete. I got the idea from here

https://shellycloud.com/blog/2013/10/adding-search-and-autocomplete-to-a-rails-app-with-elasticsearch

I have elasticsearch autocomplete configured correctly, because it works when I access it directly via the browser. However, when I try to use typeahead to call display data from the autocomplete query, it doesn't even trigger in my debugger. Here is my form & javascript where typeahead is called

Form

<script>
  $('#autcomplete_search').typeahead({
    highlight: true
  },
  {
    name: 'apple_game',
    remote: "/search/autocomplete?query=%QUERY"
  });
</script>

<h1>Keyword</h1>
<form action="/search/keyword">
  <div>
    <%= text_field_tag :query, params[:query], class: "form-control", id: "autcomplete_search" %>
    <br/>
    <br/>
  </div>
  <div>
    <input type="submit">/</input>
  </div>
</form>

Controller

  def autocomplete
    es = ESClient.get_client
    games = es.suggest index: 'games',
      body: { 
        apple_game: {
          text: params[:keyword],
          completion: {
            field: "title"}
        }
      }
    render json: games
  end

Sample browser result from controller method

{
    "_shards": {
        "total": 5,
        "successful": 5,
        "failed": 0
    },
    "apple_game": [
        {
            "text": "ma",
            "offset": 0,
            "length": 2,
            "options": [
                {
                    "text": "Macabre Mysteries: Curse of the Nightingale Collector's Edition HD",
                    "score": 1
                },
                {
                    "text": "Mad Cop - Police Car Race and Drift (Ads Free)",
                    "score": 1
                },
                {
                    "text": "Mad Freebording (Snowboarding)",
                    "score": 1
                },
                {
                    "text": "Mad Merx: Nemesis",
                    "score": 1
                },
                {
                    "text": "Mad River Whitewater Kayak Rush",
                    "score": 1
                }
            ]
        }
    ]
}

EDIT I also noticed the following error in console whenever typeahead runs

Uncaught Error: missing source 

Solution

  • Ok, I think you have two problems.

    Problem 1:

    You look to me like you are using pre 10.0 typeahead API. To use remote, you have to use Bloodhound or something like it to fetch your results.

    I implemented this recently, and here is a working example:

    var $vartypeahead = $(yourjqueryelement);
    var engine = new Bloodhound({
      name: 'typeaheads',
      remote: {"url":'/search/typeahead?q=%QUERY'},
      datumTokenizer: function(d) { return d;},
      queryTokenizer: function(d) { return d;}
    });
    engine.initialize();
    
    $vartypeahead.typeahead({
              "minLength": 2,
              "highlight": true
            },
            {
              "source": engine.ttAdapter()
              });
    

    I did have to modify the above slightly from what I've done; I use backbone on the frontend and splice the above into it (I have a PR at the typeahead project for that)

    Problem #2

    As far as ES goes, I'm not sure you have your mappings right, usually your mapping for a typeahead project is going to look something like this:

    {
      "settings": {
        "analysis": {
          "filter": {
            "autocomplete_ngram": {
              "max_gram": 24,
              "min_gram": 2,
              "type": "edge_ngram"
            }
          },
          "analyzer": {
            "autocomplete_index": {
              "filter": [
                "lowercase",
                "autocomplete_ngram"
              ],
              "tokenizer": "keyword"
            },
            "autocomplete_search": {
              "filter": [
                "lowercase"
              ],
              "tokenizer": "keyword"
            }
          }
        },
        "index": {
          "number_of_shards": 20,
          "number_of_replicas": 1
        }
      },
      "mappings": {
        "yourtype": {
          "properties": {
            "title": {
              "type": "multi_field",
              "fields": {
                "title_edgengram": {
                  "type": "string",
                  "index": "analyzed",
                  "index_analyzer": "autocomplete_index",
                  "search_analyzer": "autocomplete_search"
                },
                "title": {
                  "type": "string",
                  "index": "not_analyzed"
                }
              }
            }
          }
        }
      }
    }