[![Gem Version](https://badge.fury.io/rb/chewy.svg)](http://badge.fury.io/rb/chewy) [![GitHub Actions](https://github.com/toptal/chewy/actions/workflows/ruby.yml/badge.svg)](https://github.com/toptal/chewy/actions/workflows/ruby.yml) [![Plumber Score](https://score.getplumber.io/github.com/toptal/chewy.svg)](https://score.getplumber.io/github.com/toptal/chewy) # Chewy Chewy is an ODM (Object Document Mapper), built on top of [the official Elasticsearch client](https://github.com/elastic/elasticsearch-ruby). ## Why Chewy? In this section we'll cover why you might want to use Chewy instead of the official `elasticsearch-ruby` client gem. * Every index is observable by all the related models. Most of the indexed models are related to each other and sometimes it is necessary to denormalize this related data and put it in the same object. For example, you need to index an array of tags together with an article. Chewy allows you to specify an updateable index for every model separately - so corresponding articles will be reindexed on any tag update. * Bulk import everywhere. Chewy utilizes the bulk ES API for full reindexing or index updates. It also uses atomic updates. All the changed objects are collected inside the atomic block and the index is updated once at the end with all the collected objects. See `Chewy.strategy(:atomic)` for more details. * Powerful querying DSL. Chewy has an ActiveRecord-style query DSL. It is chainable, mergeable and lazy, so you can produce queries in the most efficient way. It also has object-oriented query and filter builders. * Support for ActiveRecord. Chewy provides out-of-the-box integration with ActiveRecord, including automatic index updates on model changes via `update_index` callbacks. ## Installation Add this line to your application's `Gemfile`: gem 'chewy' And then execute: $ bundle Or install it yourself as: $ gem install chewy ## Compatibility Chewy aims to support all Ruby and Rails versions that are currently maintained by their respective teams. When a version reaches end-of-life, we may drop support for it in a future release. ### Ruby Chewy is compatible with MRI 3.2-4.0. ### Elasticsearch compatibility matrix | Chewy version | Elasticsearch version | | ------------- | ---------------------------------- | | 8.0.x | 8.x | | 7.2.x | 7.x | | 7.1.x | 7.x | | 7.0.x | 6.8, 7.x | | 6.0.0 | 5.x, 6.x | | 5.x | 5.x, limited support for 1.x & 2.x | **Important:** Chewy doesn't follow SemVer, so you should always check the release notes before upgrading. The major version is linked to the newest supported Elasticsearch and the minor version bumps may include breaking changes. See our [migration guide](migration_guide.md) for detailed upgrade instructions between various Chewy versions. ### Active Record The following Active Record versions are supported by Chewy: - 7.2 - 8.0 ## Getting Started Chewy provides functionality for Elasticsearch index handling, documents import mappings, index update strategies and chainable query DSL. ### Minimal client setting Create `config/initializers/chewy.rb` with this line: ```ruby Chewy.settings = {host: 'localhost:9250'} ``` And run `rails g chewy:install` to generate `chewy.yml`: ```yaml # config/chewy.yml # separate environment configs test: host: 'localhost:9250' prefix: 'test' development: host: 'localhost:9200' ``` ### Elasticsearch Make sure you have Elasticsearch up and running. You can [install](https://www.elastic.co/guide/en/elasticsearch/reference/current/install-elasticsearch.html) it locally, but the easiest way is to use [Docker](https://www.docker.com/get-started): ```shell $ docker run --rm --name elasticsearch -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" -e "xpack.security.enabled=false" elasticsearch:8.15.0 ``` ### Security Please note that starting from version 8 Elasticsearch has security features enabled by default. Docker command above has it disabled for local testing convenience. If you want to enable it, omit `"xpack.security.enabled=false"` part from Docker command, and run these command after starting container (container name `es8` assumed): Reset password for `elastic` user: ``` docker container exec es8 '/usr/share/elasticsearch/bin/elasticsearch-reset-password' -u elastic ``` Extract CA certificate generated by Elasticsearch on first run: ``` docker container cp es8:/usr/share/elasticsearch/config/certs/http_ca.crt tmp/ ``` And then add them to settings: ```yaml # config/chewy.yml development: host: 'localhost:9200' user: 'elastic' password: 'SomeLongPassword' transport_options: ssl: ca_file: './tmp/http_ca.crt' ``` ### Closing connections `Chewy.client` is memoized per thread, so every thread that touches Chewy gets its own client with its own connections to Elasticsearch. Neither `elasticsearch-ruby` nor `elastic-transport` expose a public way to close those connections, so they are only released when Ruby's garbage collector reclaims the client. In long-lived, multi-threaded processes that frequently spawn and discard threads (e.g. Sidekiq, which replaces a thread on job failure), this can leak file descriptors. Use `Chewy.close_client` to close the current thread's connections and drop its client. The next `Chewy.client` call rebuilds a fresh one: ```ruby Chewy.close_client ``` ### Index Create `app/chewy/users_index.rb` with User Index: ```ruby class UsersIndex < Chewy::Index settings analysis: { analyzer: { email: { tokenizer: 'keyword', filter: ['lowercase'] } } } index_scope User field :first_name field :last_name field :email, analyzer: 'email' end ``` ### Model Add User model, table and migrate it: ```shell $ bundle exec rails g model User first_name last_name email $ bundle exec rails db:migrate ``` Add `update_index` to app/models/user.rb: ```ruby class User < ApplicationRecord update_index('users') { self } end ``` ### Example of data request 1. Once a record is created (could be done via the Rails console), it creates User index too: ``` User.create( first_name: "test1", last_name: "test1", email: 'test1@example.com', # other fields ) # UsersIndex Import (355.3ms) {:index=>1} # => # ``` 2. A query could be exposed at a given `UsersController`: ```ruby def search @users = UsersIndex.query(query_string: { fields: [:first_name, :last_name, :email, ...], query: search_params[:query], default_operator: 'and' }) render json: @users.to_json, status: :ok end private def search_params params.permit(:query, :page, :per) end ``` 3. So a request against `http://localhost:3000/users/search?query=test1@example.com` issuing a response like: ```json [ { "attributes":{ "id":"1", "first_name":"test1", "last_name":"test1", "email":"test1@example.com", ... "_score":0.9808291, "_explanation":null }, "_data":{ "_index":"users", "_id":"1", "_score":0.9808291, "_source":{ "first_name":"test1", "last_name":"test1", "email":"test1@example.com", ... } } } ] ``` ## Documentation - [Getting Started](docs/getting_started.md) — end-to-end tutorial building search for a media library - [Configuration](docs/configuration.md) — client settings, update strategies, notifications, integrations - [Indexing](docs/indexing.md) — index definition, field types, crutches, compiled compose path, index manipulation - [Import](docs/import.md) — import options, raw import, journaling - [Querying](docs/querying.md) — search requests, pagination, scopes, scroll, loading - [Rake Tasks](docs/rake_tasks.md) — all rake tasks and parallelization - [Testing](docs/testing.md) — RSpec, Minitest, DatabaseCleaner - [Troubleshooting](docs/troubleshooting.md) — pre-request filter - [Non-Rails Usage](docs/non_rails.md) — using Chewy without Rails ## FAQ **Can I use Chewy without Rails?** Yes. ActiveSupport and the elasticsearch gem are the only hard dependencies; the Rails railtie loads conditionally. See the [Non-Rails Usage](docs/non_rails.md) guide. **Does Chewy follow SemVer?** No. The major version tracks the newest supported Elasticsearch version and minor version bumps may include breaking changes. Always check the [release notes](https://github.com/toptal/chewy/releases) before upgrading. **Which versions of Ruby, Rails and Elasticsearch are supported?** See the [Compatibility](#compatibility) section above. When a Ruby or Rails version reaches end-of-life we may drop support in a future release. **What's the `UndefinedUpdateStrategy` error?** Chewy raises this when you save a model with an `update_index` callback and no strategy is active. Wrap your code in `Chewy.strategy(:atomic) { ... }` or set `Chewy.root_strategy = :bypass`. See [Troubleshooting](docs/troubleshooting.md#undefinedupdatestrategy-error) for details. **Can I use Chewy with OpenSearch?** OpenSearch is not officially supported or tested. Chewy depends on the official `elasticsearch` Ruby client and targets Elasticsearch. **How do I connect to Elastic Cloud?** Use the standard client settings with your Cloud credentials (API key or user/password). See [Configuration](docs/configuration.md#client-settings) for examples. ## Contributing See [CONTRIBUTING.md](CONTRIBUTING.md) for development setup instructions, how to run tests, and the pull request workflow. ## Copyright Copyright (c) 2013-2025 Toptal, LLC. See [LICENSE.txt](LICENSE.txt) for further details.