SkillAgentSearch skills...

Searchkick

Intelligent search made easy

Install / Use

/learn @ankane/Searchkick
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

Searchkick

:rocket: Intelligent search made easy

Searchkick learns what your users are looking for. As more people search, it gets smarter and the results get better. It’s friendly for developers - and magical for your users.

Searchkick handles:

  • stemming - tomatoes matches tomato
  • special characters - jalapeno matches jalapeño
  • extra whitespace - dishwasher matches dish washer
  • misspellings - zuchini matches zucchini
  • custom synonyms - pop matches soda

Plus:

  • query like SQL - no need to learn a new query language
  • reindex without downtime
  • easily personalize results for each user
  • autocomplete
  • “Did you mean” suggestions
  • supports many languages
  • works with Active Record and Mongoid

Check out Searchjoy for analytics and Autosuggest for query suggestions

:tangerine: Battle-tested at Instacart

Build Status

Contents

Searchkick 6.0 was recently released! See how to upgrade

Getting Started

Install Elasticsearch or OpenSearch. For Homebrew, use:

brew install opensearch
brew services start opensearch

Add these lines to your application’s Gemfile:

gem "searchkick"

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

The latest version works with Elasticsearch 8 and 9 and OpenSearch 2 and 3. For Elasticsearch 7 and OpenSearch 1, use version 5.5.2 and this readme.

Add searchkick to models you want to search.

class Product < ApplicationRecord
  searchkick
end

Add data to the search index.

Product.reindex

And to query, use:

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

Searchkick supports the complete Elasticsearch Search API and OpenSearch Search API. As your search becomes more advanced, we recommend you use the search server DSL for maximum flexibility.

Querying

Query like SQL

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

Search specific fields

fields(:name, :brand)

Where

where(store_id: 1, expires_at: Time.now..)

These types of filters are supported

Order

order(_score: :desc) # most relevant first - default

All of these sort options are supported

Limit / offset

limit(20).offset(40)

Select

select(:name)

These source filtering options are supported

Results

Searches return a Searchkick::Relation object. This responds like an array to most methods.

results = Product.search("milk")
results.size
results.any?
results.each { |result| ... }

By default, ids are fetched from the search server and records are fetched from your database. To fetch everything from the search server, use:

Product.search("apples").load(false)

Get total results

results.total_count

Get the time the search took (in milliseconds)

results.took

Get the full response from the search server

results.response

Note: By default, Elasticsearch and OpenSearch limit paging to the first 10,000 results for performance. This applies to the total count as well.

Filtering

Equal

where(store_id: 1)

Not equal

where.not(store_id: 2)

Greater than (gt), less than (lt), greater than or equal (gte), less than or equal (lte)

where(expires_at: {gt: Time.now})

Range

where(orders_count: 1..10)

In

where(aisle_id: [25, 30])

Not in

where.not(aisle_id: [25, 30])

Contains all

where(user_ids: {all: [1, 3]})

Like

where(category: {like: "%frozen%"})

Case-insensitive like

where(category: {ilike: "%frozen%"})

Regular expression

where(category: /frozen .+/)

Prefix

where(category: {prefix: "frozen"})

Exists

where(store_id: {exists: true})

Combine filters with OR

where(_or: [{in_stock: true}, {backordered: true}])

Boosting

Boost important fields

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

Boost by the value of a field (field must be numeric)

boost_by(:orders_count) # give popular documents a little boost
boost_by(orders_count: {factor: 10}) # default factor is 1

Boost matching documents

boost_where(user_id: 1)
boost_where(user_id: {value: 1, factor: 100}) # default factor is 1000
boost_where(user_id: [{value: 1, factor: 100}, {value: 2, factor: 200}])

Boost by recency

boost_by_recency(created_at: {scale: "7d", decay: 0.5})

You can also boost by:

Get Everything

Use a * for the query.

Product.search("*")

Pagination

Plays nicely with kaminari and will_paginate.

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

View with kaminari

<%= paginate @products %>

View with will_paginate

<%= will_paginate @products %>

Partial Matches

By default, results must match all words in the query.

Product.search("fresh honey") # fresh AND honey

To change this, use:

Product.search("fresh honey").operator("or") # fresh OR honey

By default, results must match the entire word - back will not match backpack. You can change this behavior with:

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

And to search (after you reindex):

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

Available options are:

Option | Matches | Example --- | --- | --- :word | entire word | apple matches apple :word_start | start of word | app matches apple :word_middle | any part of word | ppl matches apple :word_end | end of word | ple matches apple :text_start | start of text | gre matches green apple, app does not match :text_middle | any part of text | een app matches green apple :text_end | end of text | ple matches green apple, een does not match

The default is :word. The most matches will happen with :word_middle.

To specify different matching for different fields, use:

Product.search(query).fields({name: :word_start}, {brand: :word_middle})

Exact Matches

To match a field exactly (case-sensitive), use:

Product.search(query).fields({name: :exact})

Phrase Matches

To only match the exact order, use:

Product.search("fresh honey").match(:phrase)

Stemming and Language

Searchkick stems words by default for better matching. apple and apples both stem to appl, so searches for either term will have the same matches.

Searchkick defaults to English for stemming. To change this, use:

class Product < ApplicationRecord
  searchkick language: "german"
end

See the list of languages. A few languages require plugins:

You can also use a Hunspell dictionary for stemming.

class Product < ApplicationRecord
  searchkick stemmer: {type: "hunspell", locale: "en_US"}
end

Disable stemming with:

class Image < ApplicationRecord
  searchkick stem: false
end

Exclude certain words from stemming with:

class Image < ApplicationRecord
  searchkick stem_exclusion: ["apples"]
end

Or change how words are stemmed:

class Image < ApplicationRecord
  searchkick stemmer_override: ["apples => other"]
end

Synonyms

class Product < ApplicationRecord
  searchkick search_synonyms: [["pop", "soda"], ["burger", "hamburger"]]
end

Call Product.reindex after changing synonyms. Synonyms are applied at search time before stemming, and can be a single word or multiple words.

For d

Related Skills

View on GitHub
GitHub Stars6.7k
CategoryDevelopment
Updated2d ago
Forks766

Languages

Ruby

Security Score

100/100

Audited on Mar 22, 2026

No findings