Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Searchkick for Smart Search Using Rails and Elasticsearch

DZone's Guide to

Searchkick for Smart Search Using Rails and Elasticsearch

Search has become a key component for any web application. Learn how to add smart search capabilities to your app using these frameworks.

· Web Dev Zone ·
Free Resource

Jumpstart your Angular applications with Indigo.Design, a unified platform for visual design, UX prototyping, code generation, and app development.

Have you ever wondered, can my web app scale by learning about user searches and personalized results? Is there a one-stop solution for an autocomplete search and analytic provision to the customer and app developer? Gladly, you landed in the right place and before jumping into Searchkick, we need to understand Elasticsearch.

What Is Elasticsearch?

According to the Elasticsearch website, "Elasticsearch is a distributed, RESTful search and analytics engine capable of solving a growing number of use cases. As the heart of the Elastic Stack, it centrally stores your data so you can discover the expected and uncover the unexpected."

In short, ElasticSearch provides:

  • Analytical logs for unstructured and semi-structured data.
  • Full-text search experience in real time.
  • Application monitoring in real-time.
  • JSON Document store.
  • Automatic JSON Document indexing.
  • Multi-tenancy support.
  • Ease in data distribution across nodes.
  • Scalability ease.

Searchkick uses Elasticsearch as a default server. So, let's set up an Elasticsearch server.

How to Setup Elasticsearch Manually

Brew the Elasticsearch flavor:

brew install elasticsearch

Start and check if the Elasticsearch server install is a success.

elasticsearch

Now that we have Elasticsearch installed, let's build a Bookstore Rails app to showcase the Searchkick demo.

1) Create a new Rails application by running the command:

rails new bookstore-demo

2) Create a Book model:

rails generate scaffold Book title:string author:string genre:string price:decimal

3) Run Database creation and migration:

rake db:create db:migrate

4) Configure root on routes.rb file:

Rails.application.routes.draw do
  root 'books#index'
  resources :books
end

Now let's understand what Searchkick is all about. Searchkick is a smart and intelligent search engine gem that drives and creates quicker search handles based on user search activity. In our example, Searchkick can handle:

  • Misspellings - Horrorr matches Horror.
  • Extra whitespaces - Auto biography matches Butobiography.
  • Stemming - diaries matches diary.
  • Special characters.
  • Reindexing with no downtime.
  • Autocomplete search.
  • Works with Mongoid and ActiveRecord.
  • Personalize search results by user.

5) Add the Searchkick gem to the bookstore-demo app's gemfile:

gem 'searchkick'

6) Bundle the app

bundle install

7) Add the Searchkick addition to any model that needs search implemented:

class Book < ApplicationRecord
  searchkick
end

8) Re-index to add data to the search index, make sure you run this command everytime Searchkick implemented model changes:

Book.reindex

9) Customize Book model to implement partial match criteria using the word_start keyword:

class Book < ApplicationRecord
  searchkick word_start: [:title, :author, :genre]

  def search_data
    {
      title: title,
      author: author,
      genre: genre
    }
  end
end

Use alternate options depending on your criteria and needs:

:word # default
:word_start
:word_middle
:word_end
:text_start
:text_middle
:text_end

All you need is a search box to search a book by implementing the view using your favorite templating language, like ERB, HAML, or Slim.

Now that we understand Searchkick pretty well, let's look at the Searchkick gem's capabilities:

1) Querying and fetch everything:

Book.search "*"

2) Partial matches:

Book.search "science fiction" # science AND fiction

If you want to search both science and fiction:

Book.search "science fiction", operator: "or" # science AND fiction

3) Exact matches:

Book.search params[:q], fields:[{genre: :exact}, :title]

4) Phrase matches:

Book.search "Religion, Spirituality & New Age", match: :phrase

5) Languages:

searchkick word_start: [:title, :author, :genre], language: "spanish"

Check the language supported listing here.

6) Model associations callback addition to reindex.

Tracking searches and conversions:

Book.search "Great expectations", track: {user_id: current_user.id}

Instant search and autocomplete:

class Book < ApplicationRecord
  searchkick match: :word_start, searchable: [:title, :author]
end



Add controller action for search criteria:

class BooksController < ApplicationController
  before_action :set_book, only: [:show, :edit, :update, :destroy]

  def searchcriteria
    render json: Book.search(params[:query], {
      fields: ["title^5", "author", "genre"],
      limit: 10,
      load: false,
      misspellings: {below: 5}
    }).map(&:title)
  end
end

Search box using JavaScript on the view page:

<input type="text" id="query" name="query" />

  $("#query").typeahead({
    name: "book",
    remote: "/books/search_criteria?query=%QUERY"
  });

9) Suggestions generator:

class Book < ApplicationRecord
  searchkick suggest: [:author, :title, :genre] # fields to generate suggestions
end

10) Highlight search result fields:

class Book < ApplicationRecord
  searchkick highlight: [:author]
end

11) Create custom and advanced mapping:

class Book < ApplicationRecord
  searchkick mappings: {
    book: {
      properties: {
        title: {type: "string", analyzer: "keyword"},
        author: {type: "string", analyzer: "keyword"}
      }
    }
  }
end

To learn more about Searchkick and its capabilities, check out the Searchkick wiki page.

Take a look at the Indigo.Design sample applications to learn more about how apps are created with design to code software.

Topics:
rails ,ruby on rails ,web dev ,elasticseach

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}