+++
title = "Searchkick"
weight = 115
updated = 2023-07-26

aliases = ["/docs/searchkick", "/docs/ruby-on-rails", "/docs/managed-search/elasticsearch-and-opensearch/using-your-cluster/connecting/ruby/ruby-on-rails/searchkick/", "/docs/using-your-cluster/connecting/ruby-on-rails-searchkick/"]

[extra]
weight = 15
nav_title = "Rails: Searchkick"
+++

Getting started with Ruby on Rails and Bonsai Elasticsearch is fast and easy with Searchkick. In this guide, we will
start with a very basic Ruby on Rails application and add the bare minimum amount of code needed to support basic search
with Elasticsearch. Users looking for more details and advanced usage should consult the resources at the end of this
page.

Throughout this guide, you will see some code examples. These code examples are drawn from a very simple Ruby on Rails
application, and are designed to offer some real-world, working code that new users will find useful. The complete demo
app can be found in [this GitHub repo](https://github.com/ankane/searchkick).

{% admonition(type="warning", title="Warning") %}
SearchKick uses the official Elasticsearch Ruby client, which is not supported on Bonsai after version 7.13. This is due
to a [change](https://github.com/elastic/elasticsearch-ruby/pull/1398) introduced in the 7.14 release of the gem. This
change prevents the Ruby client from communicating with
open-sourced versions of Elasticsearch 7.x, as well as any version of OpenSearch. The table below indicates
compatibility:

| Engine Version    | Highest Compatible Gem Version |
|:------------------|:-------------------------------|
| Elasticsearch 5.x | 7.13                           |
| Elasticsearch 6.x | 7.14+ (sic)                    |
| Elasticsearch 7.x | 7.13                           |
| OpenSearch 1.x    | 7.13                           |

If you are receiving a `Elasticsearch::UnsupportedProductError`, then you'll need to ensure you're using a supported
version of the Elasticsearch Ruby client.
{% end %}

{% admonition(type="note", title="Note") %}
In this example, we are going to connect to Elasticsearch using
the [Searchkick gem](https://github.com/ankane/searchkick). There are also
the [official Elasticsearch gems for Rails](https://github.com/elastic/elasticsearch-rails), which are covered in
another set of documentation.
{% end %}

## Step 1: Spin up a Bonsai Cluster

Make sure that there is a Bonsai Elasticsearch cluster ready for your app to interact with. This needs to be set up
first so you know which version of the gems you need to install; Bonsai supports a large number of Elasticsearch
versions, and the gems need to correspond to the version of Elasticsearch you're running.

Bonsai clusters can be created in a few different ways, and the documentation for each path varies. If you need help
creating your cluster, check out the link that pertains to your situation:

* If you've signed up with us at bonsai.io, you will want to follow
  the [directions here](@/docs/get-started/quickstart/index.md).
* Heroku users should follow [these directions](@/docs/heroku/add-bonsai-to-your-heroku-app/index.md).

### The Cluster URL

When you have successfully created your cluster, it will be given a semi-random URL called the Elasticsearch Access URL.
You can find this in
the [Cluster Overview](@/docs/features/cluster-overview/index.md),
in the Access section:

{{ image(src="access-section.png", alt="Cluster Access Section",
automatically_determine_ratio=true) }}

Heroku users will also have a `BONSAI_URL` environment variable created when Bonsai is added to the application. This
variable will contain the fully-qualified URL to the cluster.

## Step 2: Confirm the Version of Elasticsearch Your Cluster is On

When you have a Bonsai Elasticsearch cluster, there are a few ways to check the version that it is running. These are
outlined below:

### Option 1: Via the Cluster Dashboard Details

The easiest is to simply get it from the Cluster Dashboard. When you view your cluster overview in Bonsai UI, you will
see some details which include the version of Elasticsearch the cluster is running:

{{ image(src="cluster-dashboard-version.png", alt="Cluster Dashboard details showing version", automatically_determine_ratio=true) }}

### Option 2: Interactive Console

You can also use the Interactive Console. In the Cluster Dashboard, click on the Console tab. It will load a default
view, which includes the version of Elasticsearch. The version of Elasticsearch is called “number” in the JSON response:

{{ image(src="interactive-console-version.png", alt="Interactive Console showing version",
automatically_determine_ratio=true) }}

### Option 3: Using a Browser or `curl`

You can copy/paste your cluster URL into a browser or into a tool like `curl`. Either way, you will get a response like
so:

```bash
curl https://abcd123:efg456@my-cluster-123456.us-west-2.bonsaisearch.net:443
{
  "name" : "ip-172-31-14-16",
  "cluster_name" : "elasticsearch",
  "cluster_uuid" : "jVJrINr5R5GVVXHGcRhMdA",
  "version" : {
    "number" : "7.2.0",
    "build_flavor" : "oss",
    "build_type" : "tar",
    "build_hash" : "508c38a",
    "build_date" : "2019-06-20T15:54:18.811730Z",
    "build_snapshot" : false,
    "lucene_version" : "8.0.0",
    "minimum_wire_compatibility_version" : "6.8.0",
    "minimum_index_compatibility_version" : "6.0.0-beta1"
  },
  "tagline" : "You Know, for Search"
}
```

The version of Elasticsearch is called “number” in the JSON response.

## Step 3: Install the Gem

To install Searchkick, you will need the searchkick gem. Add the following to your Gemfile outside of any blocks:

```ruby
gem 'searchkick'
```

This will install the gem for the latest major version of Elasticsearch. If you have an older version of Elasticsearch,
then you should follow this table:

| Elasticsearch Version | Searchkick Version |
|:----------------------|:-------------------|
| 1.x                   | -> 1.5.1           |
| 2.x                   | -> 2.5.0           |
| 5.x                   | -> 3.1.3           |
| 6.x and up            | -> 4.0 and up      |

If you need a specific version of Searchkick to accommodate your Elasticsearch cluster, you can specify it in your
Gemfile like so:

```ruby
gem 'searchkick', '3.1.3' # For Elasticsearch 5.x
```

Once the gem has been added to your Gemfile, run `bundle install`.

## Step 4: Add Searchkick to Your Models

Any model that you will want to be searchable with Elasticsearch will need to be configured to do so by adding the
`searchkick` keyword to it.

For example, our demo app has a User model that looks something like this:

```ruby

class User < ApplicationRecord
  searchkick
end
```

Adding the `searchkick` keyword makes our User model searchable with Searchkick.

Searchkick provides a number of reasonable settings out of the box, but you can also pass in a hash of settings if you
want to override the defaults. The hash keys generally correspond to the
official [Create Indices API](https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-create-index.html).
For example, this will allow you to create an index with 0 replicas:

```ruby

class User < ApplicationRecord
  searchkick settings: { number_of_replicas: 0 }
end
```

If you have questions about shards and how many is enough, check out
our [Shard Primer](@/docs/platform/shard-primer/index.md) and our documentation
on [Capacity Planning](@/docs/cluster-management/capacity-planning/index.md).

## Step 5: Create a Search Route

You will need to set up a route to handle searching. The easiest way to do this with Searchkick is to have a search
route per model. This involves updating your models' corresponding controller, and defining routes in
`config/routes.rb`. You'll also need to have some views that handle rendering the results, and a form that posts data to
the controller(s). Take a look at how we implemented this in our demo app for some examples of how this is done:

### Our Example

In our example Rails app, we have one model, `User`, with `searchkick`. It looks something like this:

```ruby

class User < ApplicationRecord
  searchkick
end
```

To implement search, we updated the file `app/controllers/users_controllers.rb` and added this code:

```ruby

class UsersController < ApplicationController
  #... a bunch of controller actions, removed for brevity

  def search
    @results = User.search(params[:q])
  end

  #... more controller actions removed for brevity
end
```

We then created a route in the `config/routes.rb` file:

```ruby
Rails.application.routes.draw do
  resources :users do
    collection do
      post :search # creates a route called users_search
    end
  end
end
```

Next, we need to have some views to render the data we get back from Elasticsearch. The `search` controller action will
be rendered by creating a file called `app/views/users/search.html.erb` and adding:

```erb
<h1>Search Results</h1>

<% if @results.present? %>
<%= render partial: 'search_result', collection: @results, as: :result %>
<% else %>
  <p>Nothing here, chief!</p>
<% end %>
```

This way if there are no results to show, we simply put a banner indicating as such. If there are results to display, we
will iterate over the collection (assigning each one to a local variable called `result`), and passing it off to a
partial. We also created a file for a partial called `app/views/users/_search_result.html.erb` and added:

```erb
<%= link_to "#{result.first_name} #{result.last_name} <#{result.email}>", user_path(result) %>
<%= result.company %>
<%= result.company_description %>
```

This partial simply renders a search result using some of the data of the matching ActiveRecord objects.

At this point, the `User` model is configured for searching in Elasticsearch, and has routes for sending a query to
Elasticsearch. The next step is to render a form so that a user can actually use this feature. This is possible with a
basic `form_with` helper.

In our demo app, we added this to the navigation bar:

```erb
<%= form_with(url: "/search", method: "post", class: 'form-inline my-2 my-lg-0', local: true) do %>
<%= text_field_tag(:q, nil, class: "form-control mr-sm-2", placeholder: "Search") %>
<%= button_tag("Search", class: "btn btn-outline-info my-2 my-sm-0", name: nil) %>
<% end %>
```

This code renders a form that looks like this:

{{ image(src="search-form-example.png", alt="Search form example", automatically_determine_ratio=true) }}

Please note that these classes use Bootstrap, which is not required for Searchkick.

We’re close to finishing up. We just need to tell the app where the Bonsai cluster is located, then push our data into
that cluster.

## Step 6: Tell Searchkick Where Your Cluster is Located

Searchkick looks for an environment variable called `ELASTICSEARCH_URL`, and if it doesn’t find it, it uses
`localhost:9200`. This is a problem because your Bonsai cluster is not running on a localhost. We need to make sure
Searchkick is
pointed to the correct URL.

Bonsai does offer a gem, [bonsai-searchkick](https://github.com/omc/bonsai-searchkick), which populates the necessary
environment variable automatically. If you're
using this gem, then all you need to do is ensure that there is an environment variable called `BONSAI_URL` set in your
application environment that points at your Bonsai cluster URL.

Heroku users will have this already, and can skip to the next step. Other users will need to make sure this environment
variable is manually set in their application environment. If you have access to the host, you can run this command in
your command line:

```sh
# Substitute with your cluster URL, obviously:
export BONSAI_URL="https://abcd123:efg456@my-cluster-123456.us-west-2.bonsaisearch.net:443"
```

### Writing an Initializer

You will only need to write an initializer if:

- You are not using the bonsai-searchkick gem for some reason, OR
- You are not able to set the `BONSAI_URL` environment variable in your application environment

If you need to do this, then you can create a file called `config/initializers/elasticsearch.rb`. Inside this file, you
will want to put something like this:

```sh
# Assuming you can set the BONSAI_URL variable:
ENV["ELASTICSEARCH_URL"] = ENV['BONSAI_URL']
```

If you’re one of the few who can’t set the `BONSAI_URL` variable, then you’ll need to do something like this:

```sh
# Use your personal URL, not this made-up one:
ENV["ELASTICSEARCH_URL"] = "https://abcd123:efg456@my-cluster-123456.us-west-2.bonsaisearch.net:443"
```

If you’re wondering why we prefer to use an environment variable instead of the URL, it’s simply a best practice. The
cluster URL is considered sensitive information, in that anyone with the fully-qualified URL is going to have full
read/write access to the cluster.

So if you have it in an initializer and check it into source control, that creates an attack vector. Many people have
been burned by committing sensitive URLs, keys, passwords, etc to git, and it’s best to avoid it.

Additionally, if you ever need to change your cluster URL, updating the initializer will require another pass through CI
and a deployment. Whereas you could otherwise just change the environment variable and restart Rails. Environment
variables are simply the better way to go.

## Step 7: Push Data into Elasticsearch

Now that the app has everything it needs to query the cluster and render the results, we need to push data into the
cluster. There are a few ways to do this.

One method is to open up a Rails console and run `.reindex`. So if you want to reindex a model called `User`, you would
run `User.index`.

Another method is to use Rake tasks from the command line. If you wanted to reindex that same `User` model, you could
run:

```sh
bundle exec rake searchkick:reindex CLASS=User
```

Alternatively, if you have multiple Searchkick-enabled models, you could run

```sh
rake searchkick:reindex:all
```

Regardless of how you do it, Searchkick will create an index named after the ActiveRecord table of the model, the
environment, and a timestamp. So reindexing the `User` model in a development environment might result in an index
called `users_development_20191029111649033`. This allows Searchkick to provide zero-downtime updates to settings and
mappings.

## Step 8: Put it All Together

At this point you should have all of the pieces you need to search your data using Searchkick. In our demo app, we have
this simple list of users:

{{ image(src="users-list.png", alt="List of users", automatically_determine_ratio=true) }}

This search box is rendered by a form that will pass the query to the `UsersController#search` action, via the route set
up in `config/routes.rb`:

{{ image(src="used-search-form.png", alt="Search form with Abbott as entry", automatically_determine_ratio=true) }}

This query will reach the `UsersController#search` action, where it will be passed to Searchkick, which queries
Elasticsearch. Elasticsearch will search the `users_development_20191029111649033` index, and return any hits to a class
variable called `@results`.

The `UsersController` will then ensure the appropriate views are rendered. Each result will be rendered by the partial
`app/views/users/_search_result.html.erb`. It looks something like this:

{{ image(src="users-search-results.png", alt="Users Search results", automatically_determine_ratio=true) }}

Congratulations! You have implemented Searchkick in Rails!

## Final Thoughts

This documentation demonstrated how to quickly get Elasticsearch added to a basic Rails application. We installed the
Searchkick gem, added it to a model, set up the search route, and created the views and partials needed to render the
results. Then we set up the connection to Elasticsearch and pushed the data into the cluster. Finally, we were able to
search that data through our app.

Hopefully this was enough to get you up and running with Searchkick. This documentation is not exhaustive, and there are
a lot of really cool features that Searchkick offers. There are other additional changes and customizations that can be
implemented to make search more accurate and resilient.

You can find information on additional subjects in the section below. And if you have any ideas or requests for
additional content, please don’t hesitate to [let us know](mailto:support@bonsai.io)!

### Additional Resources

* [Shard Primer](@/docs/platform/shard-primer/index.md)
* [Capacity Planning](@/docs/cluster-management/capacity-planning/index.md)