CLI tool
ankane/searchkick avatar
ankane/searchkick

Searchkick: Intelligent Search for Ruby on Rails

Intelligent search made easy

6,719 stars762 forksRubyMIT

At a glance

What is it?
Searchkick is a Ruby gem that wraps Elasticsearch and OpenSearch with an ActiveRecord-style query interface, adding stemming, typo correction, synonyms, and personalization to Rails and Mongoid applications without requiring engineers to learn the underlying JSON query language.
Who is it for?
Searchkick suits Rails teams who want to add Elasticsearch or OpenSearch to an existing Active Record or Mongoid application without learning the underlying query DSL. The SQL-style API covers most common search requirements: stemming, typos, synonyms, boosting, pagination, and autocomplete.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 12 days ago.
What is it written in?
Mainly Ruby, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 25, 2026, and from our analysis. They are not legal advice.

Editorial analysis

What Problem Searchkick Solves for Rails Developers

Full-text search looks simple until you implement it well. A SQL LIKE query handles basic substring matching but falls apart with typos, word stems, and relevance ranking. Adding Elasticsearch or OpenSearch to a Rails application solves those problems, but those search engines use a JSON query language with dozens of options, and learning it takes time most product teams do not have.

Searchkick fills that gap. It wraps Elasticsearch and OpenSearch behind a Ruby API that mirrors the style of Active Record, so developers who can write where clauses and method chains can write search queries without reading Elasticsearch documentation first. The gem works with both Active Record and Mongoid. It targets Rails developers who want a production-ready search feature, including typo correction, synonym matching, and autocomplete, without maintaining a separate search configuration layer in raw JSON.

The README notes the gem is battle-tested at Instacart, which gives some indication of the scale it has handled in production. Companion gems Searchjoy and Autosuggest extend it with analytics and query suggestion features respectively.

How Searchkick Indexes Records and Returns Results

When you call `Product.reindex`, Searchkick reads every record from your database and sends the data to an Elasticsearch or OpenSearch index in batches. Each record becomes a document in that index. Subsequent calls to `Product.search("apples")` send a query to the search server, which returns matching document IDs ranked by relevance. Searchkick then fetches the full Active Record objects from your database using those IDs.

That two-step process means your application data stays in the database while the search server handles ranking and text analysis. If you want to skip the database lookup and fetch everything from the search server, you can call `Product.search("apples").load(false)`.

Text analysis runs at both index time and query time. Stemming reduces "tomatoes" and "tomato" to the same root so a search for one returns results for the other. A custom synonyms list lets you map "pop" to "soda". Misspelling correction handles "zuchini" when the user meant "zucchini". Special characters are normalized so "jalapeno" matches "jalapeño". These transformations are configured through options on the model class. The gem supports Elasticsearch 8 and 9 and OpenSearch 2 and 3; for Elasticsearch 7 and OpenSearch 1, the README points to version 5.5.2 of the gem.

Installing Searchkick and Running a First Search

You need a running Elasticsearch or OpenSearch server before installing the gem. On macOS with Homebrew, you can start OpenSearch with:

sh
brew install opensearch
brew services start opensearch

Add the gem and your chosen client to your Gemfile:

ruby
gem "searchkick"

gem "elasticsearch"   # select one
gem "opensearch-ruby" # select one

Then add the `searchkick` declaration to any model you want to search:

ruby
class Product < ApplicationRecord
  searchkick
end

Build the initial search index:

ruby
Product.reindex

That command reads all Product records and sends them to the search server. After reindexing, run a search:

ruby
products = Product.search("apples")
products.each do |product|
  puts product.name
end

The `Product.search` call returns a `Searchkick::Relation` object, which responds like an array. You can chain SQL-style filters onto the search call:

ruby
Product.search("apples").where(in_stock: true).limit(10).offset(50)

For pagination, the gem works with kaminari and will_paginate:

ruby
@products = Product.search("milk").page(params[:page]).per_page(20)

Note that Elasticsearch and OpenSearch both cap paging at the first 10,000 results by default. Queries that need results beyond that require using the search-after API configured through the raw DSL.

Filtering, Boosting, and Result Ordering

Searchkick supports a range of filter operations: equal, not equal, greater than or less than with gt/lt/gte/lte, range, in, not in, contains all, like, case-insensitive like, regular expression, prefix, and exists checks. Boolean OR logic across multiple filter groups uses the `_or` key. These cover common product catalog or content search requirements without writing JSON queries.

Relevance can be tuned through boosting. The `boost_by` option increases the score of documents with a higher numeric field value:

ruby
boost_by(:orders_count)

Documents can also be boosted by field value match, which is useful for personalizing results to a logged-in user, or by recency using a decay function. The field importance weighting option lets you prioritize one field over another:

ruby
fields("title^10", "description")

By default, results are sorted by score descending. You can override sort with the `order` option, and you can search all records by passing `Product.search("*")`.

Partial Matching and Autocomplete Configuration

By default, Searchkick requires a full-word match: "back" does not match "backpack". To enable prefix matching, declare `word_start` on the model class:

ruby
class Product < ApplicationRecord
  searchkick word_start: [:name]
end

After reindexing, query with the `match` option:

ruby
Product.search("back").fields(:name).match(:word_start)

Four match modes are available: `:word` for full words, `:word_start` for prefix matches, `:word_middle` for any substring, and `:word_end` for suffix matches. These are configured per field at the model level and applied per query.

Autocomplete and "Did you mean" suggestions are both supported, though the README points to the full documentation for their configuration. Custom synonyms let you specify that certain terms are equivalent, which is configured at the model level rather than in the Elasticsearch settings directly.

When Searchkick Is the Wrong Choice

Searchkick requires a separate Elasticsearch or OpenSearch process. That adds infrastructure cost, a new service to monitor and update, and an operational dependency that a simple Rails application may not need. If your application already runs PostgreSQL, the pg_search gem provides full-text search directly against the database using PostgreSQL's built-in tsvector and tsquery types. No additional server is needed. The trade-off is that PostgreSQL full-text search has fewer text analysis options: it supports stemming and stop words, but not the same degree of relevance learning or synonym management that Elasticsearch provides.

Searchkick also inherits the 10,000-result pagination limit from Elasticsearch and OpenSearch. This limit applies to the total count as well as to the returned records. For applications that need to browse or export very large result sets, this is a real constraint that requires working around at the Elasticsearch level.

Reindexing large tables can take significant time. Searchkick documents a zero-downtime reindex approach, but running it correctly adds operational steps that simpler solutions skip.

Maintenance History and License

Searchkick is published under the MIT license, which permits commercial use, modification, and distribution without restriction. The repository is maintained by Andrew Kane. The last push was on 2026-09-17.

Searchkick 6 was recently released; the README includes upgrade instructions for teams moving from version 5. Version 5.5.2 remains available for applications still on Elasticsearch 7 or OpenSearch 1. The gem tracks new major versions of both search engines as they are released.

The repository has no GitHub releases; version history is tracked through the gem release process rather than GitHub's release interface. The changelog is available in CHANGELOG.md at the top level of the repository. The repository structure includes lib/, test/, and benchmark/ directories, along with an examples/ folder containing hybrid.rb and semantic.rb for more advanced usage patterns.

Editorial conclusion

Searchkick suits Rails teams who want to add Elasticsearch or OpenSearch to an existing Active Record or Mongoid application without learning the underlying query DSL. The SQL-style API covers most common search requirements: stemming, typos, synonyms, boosting, pagination, and autocomplete. It is the wrong choice if your infrastructure budget does not accommodate a separate search server or if your data is already in PostgreSQL and the simpler pg_search covers your requirements. Before adopting it, confirm that your Elasticsearch or OpenSearch version matches the current requirements: Elasticsearch 8 or 9, or OpenSearch 2 or 3, for the current major version of the gem.

Frequently asked questions

Does Searchkick update the search index automatically when records change?

The README documents callback options that can be enabled to trigger incremental reindexing on create, update, or destroy events. By default, you run Product.reindex to rebuild the entire index. The choice between callbacks and full reindex depends on the size of your dataset and how frequently records change.

Can Searchkick search across multiple models at the same time?

The README does not document a cross-model search built into the gem. Each model maintains its own index. Searching across models in a single query would require combining results in application code or using the Elasticsearch cross-index query API directly through the raw DSL.

Does Searchkick support reindexing without taking the search offline?

The README documents an approach for reindexing without downtime. Searchkick builds a new index and then swaps the alias once the index is ready, so searches continue against the old index until the swap completes.

Official sources

  1. ankane/searchkick on GitHub
  2. Issues
  3. License: MIT
  4. README
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/ankane-searchkick.svg)](https://hysenlabs.com/projects/ankane-searchkick)