Install ElasticSearch :
— For Ubuntu 14.04
Install Java Default Runtime & JDK
sudo apt-get install default-jre
sudo apt-get install default-jdk
Install ElasticSearch from Debian software package
wget https://download.elasticsearch.org/elasticsearch/elasticsearch/elasticsearch-0.90.7.deb
dpkg -i elasticsearch-0.90.7.deb
Test ElasticSearch install
curl -X GET 'http://localhost:9200'
We will get a response like this:
{
"ok" : true,
"status" : 200,
"name" : "Destroyer, The",
"version" : {
"number" : "0.90.7",
"build_hash" : "36897d07dadcb70886db7f149e645ed3d44eb5f2",
"build_timestamp" : "2013-11-13T12:06:54Z",
"build_snapshot" : false,
"lucene_version" : "4.5.1"
},
"tagline" : "You Know, for Search"
}
We will be using Searchkick – a ruby gem which works as a wrapper on top of ElasticSearch and provides robust searching as well as many other facilities.
Add Searchkick in your Gemfile
... gem 'searchkick' ...
Add Searchkick in your Model
# app/models/book.rb class Book < ActiveRecord::Base searchkick end
We need to build the index for Book class so that already existing books from database are added into the index.
rake searchkick:reindex CLASS=Book
Now we can try Searchkick in our console
>> books = Book.search('rails').map(&:title)
["Rails Recipes", "Crafting Rails 4 Applications, 2nd Edition"]
Since Searchkick is working, lets integrate it in out index action of books controller
# app/controllers/books_controller.rb def index if params[:query].present? @books = Book.search(params[:query]) else @books = [] end end
Let’s add the Search form in the view
# app/views/books/index.html.erb <div class='books-search'> <h1>Search Books</h1> <%= form_tag books_path, method: :get do %> <div class='form-group'> <%= text_field_tag :query, params[:query], class: 'form-control' %> <%= submit_tag 'Search', class: 'btn btn-primary' %></div> <% end %></div>
Now search functionality will be working.
Search engines are awesome because of their autocomplete feature. Now, lets integrate the Autocomplete functionality using Twitter Typeahead JS and Bloodhound Suggestion Engine in our app.
After downloading TypeaheadJS Bundle with BloodhoundJS we will add typeahead.bundle.min.js into our assets/javascripts/ folder and require it after jquery in our application.js file.
# app/assets/javascripts/application.js //= require jquery //= require jquery_ujs //= require turbolinks //= require bootstrap-sprockets //= require typeahead.bundle.min //= require_tree .
Now we need to specify autocomplete field in our Book model
# app/models/book.rb class Book < ActiveRecord::Base searchkick word_start: [:title] end
Lets rebuild the search index
rake searchkick:reindex CLASS=Book
Let’s add autocomplete action in our BooksController and change the route file
def autocomplete results = Book.search(params[:query], autocomplete: false, limit: 10).map do |book| { title: book.title, value: book.id } end render json: results end
# config/routes.rb resources :books do collection do get :autocomplete end end
‘typeahead’ class name needs to be added in our text_field_tag
# app/views/books/index.html.erb <div class='books-search'> ... <%= text_field_tag :query, params[:query], class: 'form-control typeahead' %> ...</div>
Let’s add the bloodhound suggestion engine and initiate typeahead in a new js file and require it after typeahead.bundle.min.js in application.js
# app/assets/javascripts/books.js var ready = function() { var engine = new Bloodhound({ datumTokenizer: function(d) { return Bloodhound.tokenizers.whitespace(d.title); }, queryTokenizer: Bloodhound.tokenizers.whitespace, remote: { url: '../books/autocomplete?query=%QUERY', wildcard: '%QUERY' } }); var promise = engine.initialize(); promise .done(function() { console.log('success!'); }) .fail(function() { console.log('err!'); }); $('.typeahead').typeahead(null, { name: 'engine', displayKey: 'title', source: engine.ttAdapter() }); } $(document).ready(ready); $(document).on('page:load', ready);
# app/assets/javascripts/application.js //= require jquery //= require jquery_ujs //= require turbolinks //= require bootstrap-sprockets //= require typeahead.bundle.min //= require books //= require_tree .
Add a new file typeahead.css.scss and include it assets/stylesheets folder
.tt-dropdown-menu { width: 100%; min-width: 160px; margin-top: 2px; padding: 5px 0; background-color: #fff; border: 1px solid #ccc; border: 1px solid rgba(0, 0, 0, 0.2); *border-right-width: 2px; *border-bottom-width: 2px; -webkit-border-radius: 6px; -moz-border-radius: 6px; border-radius: 6px; -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); -moz-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); -webkit-background-clip: padding-box; -moz-background-clip: padding; background-clip: padding-box; } .tt-suggestion { display: block; padding: 3px 20px; } .tt-suggestion.tt-is-under-cursor { color: #fff; background-color: #0081c2; background-image: -moz-linear-gradient(top, #0088cc, #0077b3); background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0077b3)); background-image: -webkit-linear-gradient(top, #0088cc, #0077b3); background-image: -o-linear-gradient(top, #0088cc, #0077b3); background-image: linear-gradient(to bottom, #0088cc, #0077b3); background-repeat: repeat-x; filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc', endColorstr='#ff0077b3', GradientType=0); } .tt-suggestion.tt-is-under-cursor a { color: #fff; } .tt-suggestion p { margin: 0; } .books-search { .twitter-typeahead { width: 100%; } .btn { vertical-align: top; } }
You are done!
Here is the github repository if you missed anything compare your code with mine. 🙂
Leave a comment