+++
title = "Active Search"
weight = 117
updated = 2026-09-25

aliases = ["/docs/active-search"]

[extra]
weight = 16
nav_title = "Rails: Active Search"
+++

Here's how to get started with Bonsai and Ruby on Rails using [Active Search](https://github.com/basecamp/rails-active_search), the search framework from 37signals. Active Search gives your models one search interface that works across database full-text search and search engines like OpenSearch and Elasticsearch.

{% admonition(type="note", title="Note") %}
Active Search is alpha software and requires Rails 8.1 or later. Its API may change between releases. This guide was tested with version 0.1.0 against a Bonsai OpenSearch 3.8 cluster.
{% end %}

## Add the Gems

Active Search doesn't include a search client, so add the one that matches your cluster. For an OpenSearch cluster:

```ruby
gem "rails-active_search"
gem "opensearch-ruby"
```

Then run `bundle install`, followed by the install task, which writes `config/search.yml` and `config/search.rb`:

```bash
bin/rails active_search:install
```

{% admonition(type="warning", title="Warning") %}
If your cluster runs Elasticsearch, use `adapter: elasticsearch` and the `elasticsearch` gem instead. The official Elasticsearch Ruby client is not supported on Bonsai after version 7.13, because the 7.14 release refuses to talk to open-source versions of Elasticsearch 7.x. Pin it in your Gemfile:

```ruby
gem "elasticsearch", "7.13.3"
```
{% end %}

## Configure the Connection

You can find your Bonsai cluster URL in the [Access section of your cluster dashboard](@/docs/features/credential-management/index.md). Hard-coding your credentials is not recommended. Instead, export the URL to an environment variable called `BONSAI_URL`:

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

Heroku users will not need to do so, as their Bonsai cluster URL will already be in a Config Var of the same name.

Then point the production environment at your cluster in `config/search.yml`:

```yaml
development:
  adapter: sqlite

production:
  adapter: opensearch
  hosts:
    - <%= ENV["BONSAI_URL"] %>
```

The client reads the host, port and credentials from the URL. To confirm that your app can reach the cluster, run:

```bash
RAILS_ENV=production bin/rails active_search:health
```

## Configure Your Models

Declare an index for each model you want to search in `config/search.rb`. `text` fields are searched, and the other types are used for filtering and sorting:

```ruby
ActiveSearch.define_index(:articles) do
  text :title
  text :body
  string :status
end
```

Then add `has_search` to the model:

```ruby
class Article < ApplicationRecord
  has_search
end
```

`has_search` uses the index named after the model's table. It reindexes a record after every create and update, and removes it after destroy, through Active Job.

## Indexing Your Documents

Create the index on your cluster:

```bash
RAILS_ENV=production bin/rails active_search:index:create INDEX=articles
```

The model callbacks only index records as they change, so load your existing records with a batch. This sends them to your cluster as `_bulk` requests:

```ruby
ActiveSearch.index(:articles).batch(max_size: 500) do |batch|
  Article.find_each { |article| batch.add(article) }
end
```

To check that everything made it in, count the documents in each index:

```bash
RAILS_ENV=production bin/rails active_search:status COUNT=1
```

The document count on your [Cluster Overview](@/docs/features/cluster-overview/index.md) should match.

## Searching

Queries chain like Active Record queries, and pagination is built in:

```ruby
@page = Article.search(params[:q])
  .filter(status: "published")
  .page(params[:page], per_page: 20)

@page.results.each do |article|
  article.title
  article.hit.score
end
```

To see the request Active Search sends to your cluster, call `to_native_query`. You can paste the result into the [Console](@/docs/features/console/index.md) as a `POST` to `/articles/_search`:

```ruby
Article.search("hotwire").to_native_query
```

## Query Logs

[Query Logs](@/docs/features/query-logs/index.md) extracts the user's search terms from each request. To tell Bonsai exactly what the user typed, add it to the request with `native`:

```ruby
Article.search(params[:q]).native { |request|
  request.merge(ext: { bonsai: { user_query: params[:q] } })
}.page(params[:page])
```

## Next Steps

The [Active Search README](https://github.com/basecamp/rails-active_search#readme) covers highlighting, filters, polymorphic indexes, routing and the full list of adapters. For a walkthrough of Active Search with the Bonsai dashboard, see [Active Search, Meet Bonsai](@/blog/active-search-ruby-on-rails-bonsai/index.md) on the Bonsai blog.
