Full-Text Search with Elasticsearch in Rails

Drashti Rathod
Simform Engineering
5 min readSep 13, 2023

A comprehensive guide to Full-Text Search with Elasticsearch in Ruby on Rails.

Effective search functionality is essential to many web apps today.

Traditional database searches often fall short when it comes to handling complex queries and delivering quick results. Elasticsearch, on the contrary, is a powerful, open-source search engine that can greatly enhance the search capabilities of your Rails application.

In this article, we’ll explore how to implement full-text search with Elasticsearch in your Ruby on Rails application, step by step.

What is Elasticsearch?

Elasticsearch is an open-source, distributed search and analytics engine built upon the Apache Lucene library. It is designed to handle large volumes of data and provide lightning-fast search capabilities.

In Elasticsearch, documents are organized into indices, which are queried for data.

An Index in Elasticsearch is a logical container that holds related documents, akin to a database table. Each index has a unique name and stores documents with similar structures.

Documents, on the other hand, are individual data units within an index. These documents contain the actual information you want to search for and are represented as JSON objects. They consist of various fields that store specific data. Elasticsearch searches and analyzes these documents to provide fast and accurate search results.

Why use Elasticsearch with Rails?

Elasticsearch is a powerful and popular open-source search and analytics engine that is often used in conjunction with Ruby on Rails (Rails) applications for several reasons:

  1. Fast and Accurate Search: Elasticsearch excels at quickly searching through large datasets and returning relevant results based on textual relevance, even with complex queries.
  2. Scalability: Elasticsearch’s distributed nature allows it to handle increasing amounts of data with ease, making it suitable for applications with growing search requirements.
  3. Advance Querying: Elasticsearch supports various types of queries, including full-text, boolean, phrase, wildcard, and fuzzy searches, to build advanced search functionality.
  4. Highlighting: Elasticsearch allows you to highlight search terms in the search results, making it easier for users to identify where their query matches.
  5. Language-Aware: Elasticsearch supports language-specific analyzers, stemming, and tokenization. This improves search accuracy for different languages.

Now, let’s implement full-text search using Elasticsearch in your Rails app

Implementing Full-Text Search With Elasticsearch in Rails

To integrate Elasticsearch into your Ruby on Rails application, follow these steps:

For this tutorial, we’ll assume you have a basic understanding of Rails and have a Rails application set up.

Step 1: Install Elasticsearch

Begin by installing Elasticsearch on your machine. You can download and install it from the official website or use a package manager.

Step 2: Add Elasticsearch Gem

In your Rails application’s Gemfile, add the ‘elasticsearch-model’ and ‘elasticsearch-rails’ gem for Elasticsearch integration.

gem 'elasticsearch-model'
gem 'elasticsearch-rails'

Run ‘bundle install’ to install the gem.

Step 3: Configure Elasticsearch Connection

Create a new file config/initializers/elasticsearch.rb and add the following code to configure the Elasticsearch connection:

Elasticsearch::Model.client = Elasticsearch::Client.new(url: ENV['ELASTICSEARCH_URL'] || 'http://localhost:9200')

This assumes your Elasticsearch server runs at the default http://localhost:9200 URL. You can customize it by setting the ELASTICSEARCH_URL environment variable.

Next, to make our model indexable by Elasticsearch, we need to do two things. First, we need to prepare a mapping (which is essentially telling Elasticsearch about our data structure), and second, we should construct a search request. Our gem can do both, so let’s see how to use it.

Step 4: Create the Searchable Module

Organize Elasticsearch-related code in a separate module. Create a concern in app/models/concerns/searchable.rb:

module Searchable
extend ActiveSupport::Concern

included do
include Elasticsearch::Model
include Elasticsearch::Model::Callbacks

mapping do
# mapping definition goes here
end

def self.search(query)
# build and run search
end
end
end

Here, Elasticsearch::Model adds some functionality for ES interaction. The Elasticsearch::Model::Callbacks module ensures automatic updates of the data in Elasticsearch.

The mapping block is where we put Elasticsearch index mapping, which defines the fields stored in Elasticsearch and their data types.

Additionally, we have a search method for creating full-text search queries.

Step 5: Define Mappings

Specify field mappings in the Searchable module. This informs Elasticsearch about data structure:

module Searchable
extend ActiveSupport::Concern

included do
include Elasticsearch::Model
include Elasticsearch::Model::Callbacks

mappings do
indexes :title, type: 'text'
indexes :content, type: 'text'
end

def self.search(query)
# build and run search
end
end
end

In our Searchable module, the mapping block specifies that the title and content fields should be indexed as text fields. This explicit definition guides Elasticsearch in processing and searching these fields effectively.

Step 6: Search Data

Utilize the search method provided by the elasticsearch-model gem within your Searchable concern:

module Searchable
extend ActiveSupport::Concern

included do
include Elasticsearch::Model
include Elasticsearch::Model::Callbacks

mappings do
indexes :title, type: 'text'
indexes :content, type: 'text'
end

def self.search(query)
params = {
query: {
multi_match: {
query: query,
fields: ['title', 'content'],
fuzziness: "AUTO"
}
}
}

self.__elasticsearch__.search(params).records.to_a
end
end
end

This method enables full-text search functionality by constructing an Elasticsearch search query using the multi_match query type based on a provided query parameter. This query scans specified fields (title and content) with the option for automatic fuzziness (spelling corrections).

The multi_match query in Elasticsearch is a versatile tool for performing full-text search across multiple fields with a single query. It's particularly useful when you want to search for a given text across various fields and find documents that match the query in any of those fields.

The line self.__elasticsearch__.search(params).records.to_a in the search method initiates a search operation with Elasticsearch using the provided search parameters (‘params’), which typically include the search query and any additional options.

Step 7: Configure Model

In your Rails model (e.g., Article), include the Searchable module you created. This integration will provide your model with Elasticsearch capabilities and full-text search functionality.

class Article < ApplicationRecord
include Searchable

# other model code ...
end

Step 8: Setup Controller and View

  1. In your controller (e.g., ArticlesController), define the search query to use Elasticsearch's query_string query. This query enables full-text searches across multiple fields.
class ArticlesController < ApplicationController

def index
@articles = params[:q].present? ? Article.search(params[:q]) : Article.all
end

# ...
end

2. In your view (e.g., index.html.erb), create a search form, and display search results using an iteration loop.

<p style="color: green"><%= notice %></p>

<h1>Articles</h1>

<%= form_tag articles_path, method: :get, role: 'search' do %>
<%= text_field_tag :q, params[:q], placeholder: 'Search Article ...' %>
<%= submit_tag 'Search' %>
<% end %>

<div id="articles">
<% @articles.each do |article| %>
<%= render article %>
<p>
<%= link_to "Show this article", article %>
</p>
<% end %>
</div>

<%= link_to "New article", new_article_path %>

Step 9: Test and Debug

  1. Ensure your Elasticsearch server is running.
  2. Test the full-text search functionality by entering different terms in the search box.
  3. Debug the Elasticsearch queries by adding debugging output to your controller.

Conclusion

By moving our Elasticsearch code into a separate module named Searchable, we've achieved code organization and a streamlined Elasticsearch integration process.

The search query, constructed using the multi_match query type, empowers our application with comprehensive full-text search capabilities.

What’s next?

Follow the Simform Engineering blogs for more such insights and latest trends in the development ecosystem.

Connect with us: Twitter | LinkedIn

--

--