Search code examples
javascriptruby-on-railsjsontypeahead.jssearchkick

Typeahead.js - Multiple Datasets using one Remote Source


I'm currently using typeahead.js with the searchkick rails gem to search and autocomplete queries. With the necessary adjustments to my controller I am now searching for records across two models (Artists and Albums):

class SearchController < ApplicationController
  ...
  def autocomplete
    @results = Artist.search(params[:query], index_name: [Artist.searchkick_index.name, Album.searchkick_index.name], fields: [{ name: :word_start }], limit: 10)

    render json: @results
  end
end

resources :search, only: :index do
  collection do
    get :autocomplete
  end
end

Search suggestions for the two are retrieved as expected but I now need to group the two in their own templates. This has led me to looking into using Multiple Datasets.

In my js file I'm retrieving results using the remote as suggested in this post:

remote: { 
  url: "/search/autocomplete?query=%QUERY",
  wildcard: "%QUERY" 
}

Unlike what I have, the docs suggest pulling from the model's index action by using the prefetch option:

prefetch: '../data/nhl.json'

Unfortunately configuring my js in this manner gives me nothing. Presumably I'd need an albums variable but would it not just be identical to how I have my artists variable setup? How would I go about in configuring this based on the JS I have now?

$(function() {
  var artists = new Bloodhound({
    datumTokenizer: Bloodhound.tokenizers.obj.whitespace("artist"),
    queryTokenizer: Bloodhound.tokenizers.whitespace,
    remote: { 
      url: "/search/autocomplete?query=%QUERY",
      wildcard: "%QUERY" 
    }
  });

  artists.initialize();

  $(".search").typeahead(null, {
    displayKey: "name",
    source: artists.ttAdapter(),
    templates: { 
      header: "<h3>Artists</h3>",
      suggestion: function(data) {
        return "<a href=" + data.url + ">"+ data.name +"</a>";
      }
    },
    {
     // Albums would go here
    }
  });
});

Returned JSON

/* artists.json */

{
id: 1,
name: "50 Cent",
avatar: ...,
url: "/artists/1"
}

/* albums.json */

{
id: 1,
name: "Get Rich or Die Tryin'",
artwork: ...,
genre: "Hip-Hop/Rap",
release_date: ...,
url: "/albums/1"
}

Solution

  • Try using suggestion property of templates to filter displayed results using url of returned data , RegExp.prototype.test()

    $(".search").typeahead(null, {
        displayKey: "name",
        source: artists.ttAdapter(),
        templates: { 
          // header: "<h3>Artists</h3>",
          suggestion: function(data) {
            var temp;
            if (/^\/albums/.test(data.url)) {
               // albums template
               // temp = "albums template"
            } else {
               // temp = "artists template"
            }
            return temp;
          }
        }
      });
    });