chewy 5.1.0 → 7.2.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (234) hide show
  1. checksums.yaml +4 -4
  2. data/.github/CODEOWNERS +1 -0
  3. data/.github/ISSUE_TEMPLATE/bug_report.md +39 -0
  4. data/.github/ISSUE_TEMPLATE/feature_request.md +20 -0
  5. data/.github/PULL_REQUEST_TEMPLATE.md +16 -0
  6. data/.github/workflows/ruby.yml +73 -0
  7. data/.rubocop.yml +13 -8
  8. data/.rubocop_todo.yml +110 -22
  9. data/CHANGELOG.md +449 -347
  10. data/CODE_OF_CONDUCT.md +14 -0
  11. data/CONTRIBUTING.md +63 -0
  12. data/Gemfile +3 -7
  13. data/Guardfile +3 -1
  14. data/LICENSE.txt +1 -1
  15. data/README.md +423 -311
  16. data/chewy.gemspec +8 -10
  17. data/gemfiles/rails.5.2.activerecord.gemfile +9 -14
  18. data/gemfiles/rails.6.0.activerecord.gemfile +11 -0
  19. data/gemfiles/rails.6.1.activerecord.gemfile +13 -0
  20. data/gemfiles/rails.7.0.activerecord.gemfile +13 -0
  21. data/lib/chewy/config.rb +42 -60
  22. data/lib/chewy/errors.rb +4 -10
  23. data/lib/chewy/fields/base.rb +80 -20
  24. data/lib/chewy/fields/root.rb +7 -17
  25. data/lib/chewy/index/actions.rb +62 -35
  26. data/lib/chewy/{type → index}/adapter/active_record.rb +18 -4
  27. data/lib/chewy/{type → index}/adapter/base.rb +2 -3
  28. data/lib/chewy/{type → index}/adapter/object.rb +28 -32
  29. data/lib/chewy/{type → index}/adapter/orm.rb +26 -24
  30. data/lib/chewy/index/aliases.rb +14 -5
  31. data/lib/chewy/{type → index}/crutch.rb +5 -5
  32. data/lib/chewy/index/import/bulk_builder.rb +311 -0
  33. data/lib/chewy/{type → index}/import/bulk_request.rb +6 -7
  34. data/lib/chewy/{type → index}/import/journal_builder.rb +11 -12
  35. data/lib/chewy/{type → index}/import/routine.rb +17 -16
  36. data/lib/chewy/{type → index}/import.rb +51 -33
  37. data/lib/chewy/{type → index}/mapping.rb +32 -37
  38. data/lib/chewy/index/observe/active_record_methods.rb +87 -0
  39. data/lib/chewy/index/observe/callback.rb +34 -0
  40. data/lib/chewy/index/observe.rb +17 -0
  41. data/lib/chewy/index/specification.rb +1 -0
  42. data/lib/chewy/{type → index}/syncer.rb +61 -62
  43. data/lib/chewy/{type → index}/witchcraft.rb +15 -9
  44. data/lib/chewy/{type → index}/wrapper.rb +13 -3
  45. data/lib/chewy/index.rb +46 -96
  46. data/lib/chewy/journal.rb +25 -14
  47. data/lib/chewy/minitest/helpers.rb +86 -13
  48. data/lib/chewy/minitest/search_index_receiver.rb +22 -26
  49. data/lib/chewy/multi_search.rb +62 -0
  50. data/lib/chewy/railtie.rb +6 -20
  51. data/lib/chewy/rake_helper.rb +136 -108
  52. data/lib/chewy/rspec/build_query.rb +12 -0
  53. data/lib/chewy/rspec/helpers.rb +55 -0
  54. data/lib/chewy/rspec/update_index.rb +55 -44
  55. data/lib/chewy/rspec.rb +2 -0
  56. data/lib/chewy/runtime.rb +1 -1
  57. data/lib/chewy/search/loader.rb +19 -41
  58. data/lib/chewy/search/parameters/collapse.rb +16 -0
  59. data/lib/chewy/search/parameters/concerns/query_storage.rb +2 -2
  60. data/lib/chewy/search/parameters/ignore_unavailable.rb +27 -0
  61. data/lib/chewy/search/parameters/indices.rb +12 -57
  62. data/lib/chewy/search/parameters/none.rb +1 -3
  63. data/lib/chewy/search/parameters/order.rb +6 -19
  64. data/lib/chewy/search/parameters/source.rb +5 -1
  65. data/lib/chewy/search/parameters/track_total_hits.rb +16 -0
  66. data/lib/chewy/search/parameters.rb +7 -4
  67. data/lib/chewy/search/query_proxy.rb +9 -2
  68. data/lib/chewy/search/request.rb +180 -154
  69. data/lib/chewy/search/response.rb +5 -5
  70. data/lib/chewy/search/scoping.rb +7 -8
  71. data/lib/chewy/search/scrolling.rb +16 -13
  72. data/lib/chewy/search.rb +7 -22
  73. data/lib/chewy/stash.rb +19 -30
  74. data/lib/chewy/strategy/active_job.rb +2 -2
  75. data/lib/chewy/strategy/atomic_no_refresh.rb +18 -0
  76. data/lib/chewy/strategy/base.rb +10 -0
  77. data/lib/chewy/strategy/lazy_sidekiq.rb +64 -0
  78. data/lib/chewy/strategy/sidekiq.rb +3 -2
  79. data/lib/chewy/strategy.rb +5 -19
  80. data/lib/chewy/version.rb +1 -1
  81. data/lib/chewy.rb +36 -80
  82. data/lib/generators/chewy/install_generator.rb +1 -1
  83. data/lib/tasks/chewy.rake +26 -32
  84. data/migration_guide.md +56 -0
  85. data/spec/chewy/config_spec.rb +15 -61
  86. data/spec/chewy/fields/base_spec.rb +432 -145
  87. data/spec/chewy/fields/root_spec.rb +20 -28
  88. data/spec/chewy/fields/time_fields_spec.rb +5 -5
  89. data/spec/chewy/index/actions_spec.rb +388 -55
  90. data/spec/chewy/{type → index}/adapter/active_record_spec.rb +110 -44
  91. data/spec/chewy/{type → index}/adapter/object_spec.rb +21 -6
  92. data/spec/chewy/index/aliases_spec.rb +3 -3
  93. data/spec/chewy/index/import/bulk_builder_spec.rb +494 -0
  94. data/spec/chewy/{type → index}/import/bulk_request_spec.rb +5 -12
  95. data/spec/chewy/{type → index}/import/journal_builder_spec.rb +14 -22
  96. data/spec/chewy/{type → index}/import/routine_spec.rb +19 -19
  97. data/spec/chewy/{type → index}/import_spec.rb +149 -96
  98. data/spec/chewy/index/mapping_spec.rb +135 -0
  99. data/spec/chewy/index/observe/active_record_methods_spec.rb +68 -0
  100. data/spec/chewy/index/observe/callback_spec.rb +139 -0
  101. data/spec/chewy/index/observe_spec.rb +143 -0
  102. data/spec/chewy/index/settings_spec.rb +3 -1
  103. data/spec/chewy/index/specification_spec.rb +20 -30
  104. data/spec/chewy/{type → index}/syncer_spec.rb +14 -19
  105. data/spec/chewy/{type → index}/witchcraft_spec.rb +34 -21
  106. data/spec/chewy/index/wrapper_spec.rb +100 -0
  107. data/spec/chewy/index_spec.rb +69 -137
  108. data/spec/chewy/journal_spec.rb +46 -91
  109. data/spec/chewy/minitest/helpers_spec.rb +122 -14
  110. data/spec/chewy/minitest/search_index_receiver_spec.rb +24 -26
  111. data/spec/chewy/multi_search_spec.rb +84 -0
  112. data/spec/chewy/rake_helper_spec.rb +293 -101
  113. data/spec/chewy/rspec/build_query_spec.rb +34 -0
  114. data/spec/chewy/rspec/helpers_spec.rb +61 -0
  115. data/spec/chewy/rspec/update_index_spec.rb +106 -102
  116. data/spec/chewy/runtime_spec.rb +2 -2
  117. data/spec/chewy/search/loader_spec.rb +19 -53
  118. data/spec/chewy/search/pagination/kaminari_examples.rb +3 -5
  119. data/spec/chewy/search/pagination/kaminari_spec.rb +1 -1
  120. data/spec/chewy/search/parameters/collapse_spec.rb +5 -0
  121. data/spec/chewy/search/parameters/ignore_unavailable_spec.rb +67 -0
  122. data/spec/chewy/search/parameters/indices_spec.rb +26 -118
  123. data/spec/chewy/search/parameters/none_spec.rb +1 -1
  124. data/spec/chewy/search/parameters/order_spec.rb +18 -11
  125. data/spec/chewy/search/parameters/query_storage_examples.rb +67 -21
  126. data/spec/chewy/search/parameters/search_after_spec.rb +4 -1
  127. data/spec/chewy/search/parameters/source_spec.rb +8 -2
  128. data/spec/chewy/search/parameters/track_total_hits_spec.rb +5 -0
  129. data/spec/chewy/search/parameters_spec.rb +23 -7
  130. data/spec/chewy/search/query_proxy_spec.rb +68 -17
  131. data/spec/chewy/search/request_spec.rb +344 -149
  132. data/spec/chewy/search/response_spec.rb +35 -25
  133. data/spec/chewy/search/scrolling_spec.rb +28 -26
  134. data/spec/chewy/search_spec.rb +69 -59
  135. data/spec/chewy/stash_spec.rb +16 -26
  136. data/spec/chewy/strategy/active_job_spec.rb +23 -10
  137. data/spec/chewy/strategy/atomic_no_refresh_spec.rb +60 -0
  138. data/spec/chewy/strategy/atomic_spec.rb +9 -10
  139. data/spec/chewy/strategy/lazy_sidekiq_spec.rb +214 -0
  140. data/spec/chewy/strategy/sidekiq_spec.rb +14 -10
  141. data/spec/chewy/strategy_spec.rb +19 -15
  142. data/spec/chewy_spec.rb +17 -110
  143. data/spec/spec_helper.rb +6 -29
  144. data/spec/support/active_record.rb +43 -5
  145. metadata +102 -198
  146. data/.travis.yml +0 -45
  147. data/Appraisals +0 -81
  148. data/LEGACY_DSL.md +0 -497
  149. data/gemfiles/rails.4.0.activerecord.gemfile +0 -15
  150. data/gemfiles/rails.4.1.activerecord.gemfile +0 -15
  151. data/gemfiles/rails.4.2.activerecord.gemfile +0 -16
  152. data/gemfiles/rails.4.2.mongoid.5.2.gemfile +0 -16
  153. data/gemfiles/rails.5.0.activerecord.gemfile +0 -16
  154. data/gemfiles/rails.5.0.mongoid.6.1.gemfile +0 -16
  155. data/gemfiles/rails.5.1.activerecord.gemfile +0 -16
  156. data/gemfiles/rails.5.1.mongoid.6.3.gemfile +0 -16
  157. data/gemfiles/sequel.4.45.gemfile +0 -11
  158. data/lib/chewy/backports/deep_dup.rb +0 -46
  159. data/lib/chewy/backports/duplicable.rb +0 -91
  160. data/lib/chewy/query/compose.rb +0 -68
  161. data/lib/chewy/query/criteria.rb +0 -191
  162. data/lib/chewy/query/filters.rb +0 -244
  163. data/lib/chewy/query/loading.rb +0 -110
  164. data/lib/chewy/query/nodes/and.rb +0 -25
  165. data/lib/chewy/query/nodes/base.rb +0 -17
  166. data/lib/chewy/query/nodes/bool.rb +0 -34
  167. data/lib/chewy/query/nodes/equal.rb +0 -34
  168. data/lib/chewy/query/nodes/exists.rb +0 -20
  169. data/lib/chewy/query/nodes/expr.rb +0 -28
  170. data/lib/chewy/query/nodes/field.rb +0 -110
  171. data/lib/chewy/query/nodes/has_child.rb +0 -15
  172. data/lib/chewy/query/nodes/has_parent.rb +0 -15
  173. data/lib/chewy/query/nodes/has_relation.rb +0 -59
  174. data/lib/chewy/query/nodes/match_all.rb +0 -11
  175. data/lib/chewy/query/nodes/missing.rb +0 -20
  176. data/lib/chewy/query/nodes/not.rb +0 -25
  177. data/lib/chewy/query/nodes/or.rb +0 -25
  178. data/lib/chewy/query/nodes/prefix.rb +0 -19
  179. data/lib/chewy/query/nodes/query.rb +0 -20
  180. data/lib/chewy/query/nodes/range.rb +0 -63
  181. data/lib/chewy/query/nodes/raw.rb +0 -15
  182. data/lib/chewy/query/nodes/regexp.rb +0 -35
  183. data/lib/chewy/query/nodes/script.rb +0 -20
  184. data/lib/chewy/query/pagination.rb +0 -25
  185. data/lib/chewy/query.rb +0 -1142
  186. data/lib/chewy/search/pagination/will_paginate.rb +0 -43
  187. data/lib/chewy/search/parameters/types.rb +0 -20
  188. data/lib/chewy/strategy/resque.rb +0 -27
  189. data/lib/chewy/strategy/shoryuken.rb +0 -40
  190. data/lib/chewy/type/actions.rb +0 -43
  191. data/lib/chewy/type/adapter/mongoid.rb +0 -67
  192. data/lib/chewy/type/adapter/sequel.rb +0 -93
  193. data/lib/chewy/type/import/bulk_builder.rb +0 -122
  194. data/lib/chewy/type/observe.rb +0 -82
  195. data/lib/chewy/type.rb +0 -117
  196. data/lib/sequel/plugins/chewy_observe.rb +0 -63
  197. data/spec/chewy/query/criteria_spec.rb +0 -700
  198. data/spec/chewy/query/filters_spec.rb +0 -201
  199. data/spec/chewy/query/loading_spec.rb +0 -124
  200. data/spec/chewy/query/nodes/and_spec.rb +0 -12
  201. data/spec/chewy/query/nodes/bool_spec.rb +0 -14
  202. data/spec/chewy/query/nodes/equal_spec.rb +0 -32
  203. data/spec/chewy/query/nodes/exists_spec.rb +0 -18
  204. data/spec/chewy/query/nodes/has_child_spec.rb +0 -59
  205. data/spec/chewy/query/nodes/has_parent_spec.rb +0 -59
  206. data/spec/chewy/query/nodes/match_all_spec.rb +0 -11
  207. data/spec/chewy/query/nodes/missing_spec.rb +0 -16
  208. data/spec/chewy/query/nodes/not_spec.rb +0 -14
  209. data/spec/chewy/query/nodes/or_spec.rb +0 -12
  210. data/spec/chewy/query/nodes/prefix_spec.rb +0 -16
  211. data/spec/chewy/query/nodes/query_spec.rb +0 -12
  212. data/spec/chewy/query/nodes/range_spec.rb +0 -32
  213. data/spec/chewy/query/nodes/raw_spec.rb +0 -11
  214. data/spec/chewy/query/nodes/regexp_spec.rb +0 -43
  215. data/spec/chewy/query/nodes/script_spec.rb +0 -15
  216. data/spec/chewy/query/pagination/kaminari_spec.rb +0 -5
  217. data/spec/chewy/query/pagination/will_paginate_spec.rb +0 -5
  218. data/spec/chewy/query/pagination_spec.rb +0 -39
  219. data/spec/chewy/query_spec.rb +0 -637
  220. data/spec/chewy/search/pagination/will_paginate_examples.rb +0 -63
  221. data/spec/chewy/search/pagination/will_paginate_spec.rb +0 -23
  222. data/spec/chewy/search/parameters/types_spec.rb +0 -5
  223. data/spec/chewy/strategy/resque_spec.rb +0 -46
  224. data/spec/chewy/strategy/shoryuken_spec.rb +0 -66
  225. data/spec/chewy/type/actions_spec.rb +0 -50
  226. data/spec/chewy/type/adapter/mongoid_spec.rb +0 -372
  227. data/spec/chewy/type/adapter/sequel_spec.rb +0 -472
  228. data/spec/chewy/type/import/bulk_builder_spec.rb +0 -279
  229. data/spec/chewy/type/mapping_spec.rb +0 -173
  230. data/spec/chewy/type/observe_spec.rb +0 -137
  231. data/spec/chewy/type/wrapper_spec.rb +0 -98
  232. data/spec/chewy/type_spec.rb +0 -55
  233. data/spec/support/mongoid.rb +0 -93
  234. data/spec/support/sequel.rb +0 -80
data/README.md CHANGED
@@ -1,66 +1,15 @@
1
1
  [![Gem Version](https://badge.fury.io/rb/chewy.svg)](http://badge.fury.io/rb/chewy)
2
- [![Build Status](https://travis-ci.org/toptal/chewy.svg)](https://travis-ci.org/toptal/chewy)
2
+ [![GitHub Actions](https://github.com/toptal/chewy/actions/workflows/ruby.yml/badge.svg)](https://github.com/toptal/chewy/actions/workflows/ruby.yml)
3
3
  [![Code Climate](https://codeclimate.com/github/toptal/chewy.svg)](https://codeclimate.com/github/toptal/chewy)
4
4
  [![Inline docs](http://inch-ci.org/github/toptal/chewy.svg?branch=master)](http://inch-ci.org/github/toptal/chewy)
5
5
 
6
- <p align="right">Sponsored by</p>
7
- <p align="right"><a href="https://www.toptal.com/"><img src="https://www.toptal.com/assets/public/blocks/logo/big.png" alt="Toptal" width="105" height="34"></a></p>
8
-
9
6
  # Chewy
10
7
 
11
- Chewy is an ODM and wrapper for [the official Elasticsearch client](https://github.com/elastic/elasticsearch-ruby).
12
-
13
- ## Table of Contents
14
-
15
- * [Why Chewy?](#why-chewy)
16
- * [Installation](#installation)
17
- * [Usage](#usage)
18
- * [Client settings](#client-settings)
19
- * [AWS ElasticSearch configuration](#aws-elastic-search)
20
- * [Index definition](#index-definition)
21
- * [Type default import options](#type-default-import-options)
22
- * [Multi (nested) and object field types](#multi-nested-and-object-field-types)
23
- * [Parent and children types](#parent-and-children-types)
24
- * [Geo Point fields](#geo-point-fields)
25
- * [Crutches™ technology](#crutches-technology)
26
- * [Witchcraft™ technology](#witchcraft-technology)
27
- * [Raw Import](#raw-import)
28
- * [Index creation during import](#index-creation-during-import)
29
- * [Journaling](#journaling)
30
- * [Types access](#types-access)
31
- * [Index manipulation](#index-manipulation)
32
- * [Index update strategies](#index-update-strategies)
33
- * [Nesting](#nesting)
34
- * [Non-block notation](#non-block-notation)
35
- * [Designing your own strategies](#designing-your-own-strategies)
36
- * [Rails application strategies integration](#rails-application-strategies-integration)
37
- * [ActiveSupport::Notifications support](#activesupportnotifications-support)
38
- * [NewRelic integration](#newrelic-integration)
39
- * [Search requests](#search-requests)
40
- * [Composing requests](#composing-requests)
41
- * [Pagination](#pagination)
42
- * [Named scopes](#named-scopes)
43
- * [Scroll API](#scroll-api)
44
- * [Loading objects](#loading-objects)
45
- * [Legacy DSL incompatibilities](#legacy-dsl-incompatibilities)
46
- * [Rake tasks](#rake-tasks)
47
- * [chewy:reset](#chewyreset)
48
- * [chewy:upgrade](#chewyupgrade)
49
- * [chewy:update](#chewyupdate)
50
- * [chewy:sync](#chewysync)
51
- * [chewy:deploy](#chewydeploy)
52
- * [Parallelizing rake tasks](#parallelizing-rake-tasks)
53
- * [chewy:journal](#chewyjournal)
54
- * [Rspec integration](#rspec-integration)
55
- * [Minitest integration](#minitest-integration)
56
- * [TODO a.k.a coming soon](#todo-aka-coming-soon)
57
- * [Contributing](#contributing)
8
+ Chewy is an ODM (Object Document Mapper), built on top of the [the official Elasticsearch client](https://github.com/elastic/elasticsearch-ruby).
58
9
 
59
10
  ## Why Chewy?
60
11
 
61
- * Multi-model indices.
62
-
63
- Index classes are independent from ORM/ODM models. Now, implementing e.g. cross-model autocomplete is much easier. You can just define the index and work with it in an object-oriented style. You can define several types for index - one per indexed model.
12
+ In this section we'll cover why you might want to use Chewy instead of the official `elasticsearch-ruby` client gem.
64
13
 
65
14
  * Every index is observable by all the related models.
66
15
 
@@ -74,12 +23,11 @@ Chewy is an ODM and wrapper for [the official Elasticsearch client](https://gith
74
23
 
75
24
  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.
76
25
 
77
- * Support for ActiveRecord, [Mongoid](https://github.com/mongoid/mongoid) and [Sequel](https://github.com/jeremyevans/sequel).
78
-
26
+ * Support for ActiveRecord.
79
27
 
80
28
  ## Installation
81
29
 
82
- Add this line to your application's Gemfile:
30
+ Add this line to your application's `Gemfile`:
83
31
 
84
32
  gem 'chewy'
85
33
 
@@ -91,19 +39,181 @@ Or install it yourself as:
91
39
 
92
40
  $ gem install chewy
93
41
 
94
- ## Usage
42
+ ## Compatibility
95
43
 
96
- ### Client settings
44
+ ### Ruby
45
+
46
+ Chewy is compatible with MRI 2.6-3.0¹.
47
+
48
+ > ¹ Ruby 3 is only supported with Rails 6.1
49
+
50
+ ### Elasticsearch compatibility matrix
51
+
52
+ | Chewy version | Elasticsearch version |
53
+ | ------------- | ---------------------------------- |
54
+ | 7.2.x | 7.x |
55
+ | 7.1.x | 7.x |
56
+ | 7.0.x | 6.8, 7.x |
57
+ | 6.0.0 | 5.x, 6.x |
58
+ | 5.x | 5.x, limited support for 1.x & 2.x |
59
+
60
+ **Important:** Chewy doesn't follow SemVer, so you should always
61
+ check the release notes before upgrading. The major version is linked to the
62
+ newest supported Elasticsearch and the minor version bumps may include breaking changes.
63
+
64
+ See our [migration guide](migration_guide.md) for detailed upgrade instructions between
65
+ various Chewy versions.
66
+
67
+ ### Active Record
68
+
69
+ 5.2, 6.0, 6.1 Active Record versions are supported by all Chewy versions.
70
+
71
+ ## Getting Started
72
+
73
+ Chewy provides functionality for Elasticsearch index handling, documents import mappings, index update strategies and chainable query DSL.
74
+
75
+ ### Minimal client setting
76
+
77
+ Create `config/initializers/chewy.rb` with this line:
78
+
79
+ ```ruby
80
+ Chewy.settings = {host: 'localhost:9250'}
81
+ ```
82
+
83
+ And run `rails g chewy:install` to generate `chewy.yml`:
84
+
85
+ ```yaml
86
+ # config/chewy.yml
87
+ # separate environment configs
88
+ test:
89
+ host: 'localhost:9250'
90
+ prefix: 'test'
91
+ development:
92
+ host: 'localhost:9200'
93
+ ```
94
+
95
+ ### Elasticsearch
96
+
97
+ 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):
98
+
99
+ ```shell
100
+ $ docker run --rm --name elasticsearch -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" elasticsearch:7.11.1
101
+ ```
102
+
103
+ ### Index
104
+
105
+ Create `app/chewy/users_index.rb` with User Index:
106
+
107
+ ```ruby
108
+ class UsersIndex < Chewy::Index
109
+ settings analysis: {
110
+ analyzer: {
111
+ email: {
112
+ tokenizer: 'keyword',
113
+ filter: ['lowercase']
114
+ }
115
+ }
116
+ }
117
+
118
+ index_scope User
119
+ field :first_name
120
+ field :last_name
121
+ field :email, analyzer: 'email'
122
+ end
123
+ ```
124
+
125
+ ### Model
126
+
127
+ Add User model, table and migrate it:
128
+
129
+ ```shell
130
+ $ bundle exec rails g model User first_name last_name email
131
+ $ bundle exec rails db:migrate
132
+ ```
97
133
 
98
- There are two ways to configure the Chewy client: the `Chewy.settings` hash and `chewy.yml`
134
+ Add `update_index` to app/models/user.rb:
99
135
 
100
- You can create this file manually or run `rails g chewy:install`.
136
+ ```ruby
137
+ class User < ApplicationRecord
138
+ update_index('users') { self }
139
+ end
140
+ ```
141
+
142
+ ### Example of data request
143
+
144
+ 1. Once a record is created (could be done via the Rails console), it creates User index too:
145
+
146
+ ```
147
+ User.create(
148
+ first_name: "test1",
149
+ last_name: "test1",
150
+ email: 'test1@example.com',
151
+ # other fields
152
+ )
153
+ # UsersIndex Import (355.3ms) {:index=>1}
154
+ # => #<User id: 1, first_name: "test1", last_name: "test1", email: "test1@example.com", # other fields>
155
+ ```
156
+
157
+ 2. A query could be exposed at a given `UsersController`:
158
+
159
+ ```ruby
160
+ def search
161
+ @users = UsersIndex.query(query_string: { fields: [:first_name, :last_name, :email, ...], query: search_params[:query], default_operator: 'and' })
162
+ render json: @users.to_json, status: :ok
163
+ end
164
+
165
+ private
166
+
167
+ def search_params
168
+ params.permit(:query, :page, :per)
169
+ end
170
+ ```
171
+
172
+ 3. So a request against `http://localhost:3000/users/search?query=test1@example.com` issuing a response like:
173
+
174
+ ```json
175
+ [
176
+ {
177
+ "attributes":{
178
+ "id":"1",
179
+ "first_name":"test1",
180
+ "last_name":"test1",
181
+ "email":"test1@example.com",
182
+ ...
183
+ "_score":0.9808291,
184
+ "_explanation":null
185
+ },
186
+ "_data":{
187
+ "_index":"users",
188
+ "_type":"_doc",
189
+ "_id":"1",
190
+ "_score":0.9808291,
191
+ "_source":{
192
+ "first_name":"test1",
193
+ "last_name":"test1",
194
+ "email":"test1@example.com",
195
+ ...
196
+ }
197
+ }
198
+ }
199
+ ]
200
+ ```
201
+
202
+ ## Usage and configuration
203
+
204
+ ### Client settings
205
+
206
+ To configure the Chewy client you need to add `chewy.rb` file with `Chewy.settings` hash:
101
207
 
102
208
  ```ruby
103
209
  # config/initializers/chewy.rb
104
210
  Chewy.settings = {host: 'localhost:9250'} # do not use environments
105
211
  ```
106
212
 
213
+ And add `chewy.yml` configuration file.
214
+
215
+ You can create `chewy.yml` manually or run `rails g chewy:install` to generate it:
216
+
107
217
  ```yaml
108
218
  # config/chewy.yml
109
219
  # separate environment configs
@@ -129,27 +239,31 @@ Chewy.logger = Logger.new(STDOUT)
129
239
 
130
240
  See [config.rb](lib/chewy/config.rb) for more details.
131
241
 
132
- #### Aws Elastic Search
133
- If you would like to use AWS's ElasticSearch using an IAM user policy, you will need to sign your requests for the `es:*` action by injecting the appropriate headers passing a proc to `transport_options`.
242
+ #### AWS Elasticsearch
243
+
244
+ If you would like to use AWS's Elasticsearch using an IAM user policy, you will need to sign your requests for the `es:*` action by injecting the appropriate headers passing a proc to `transport_options`.
245
+ You'll need an additional gem for Faraday middleware: add `gem 'faraday_middleware-aws-sigv4'` to your Gemfile.
134
246
 
135
247
  ```ruby
136
- Chewy.settings = {
137
- host: 'http://my-es-instance-on-aws.us-east-1.es.amazonaws.com:80',
138
- transport_options: {
139
- headers: { content_type: 'application/json' },
140
- proc: -> (f) do
141
- f.request :aws_signers_v4,
142
- service_name: 'es',
143
- region: 'us-east-1',
144
- credentials: Aws::Credentials.new(
145
- ENV['AWS_ACCESS_KEY'],
146
- ENV['AWS_SECRET_ACCESS_KEY'])
147
- end
148
- }
248
+ require 'faraday_middleware/aws_sigv4'
249
+
250
+ Chewy.settings = {
251
+ host: 'http://my-es-instance-on-aws.us-east-1.es.amazonaws.com:80',
252
+ port: 80, # 443 for https host
253
+ transport_options: {
254
+ headers: { content_type: 'application/json' },
255
+ proc: -> (f) do
256
+ f.request :aws_sigv4,
257
+ service: 'es',
258
+ region: 'us-east-1',
259
+ access_key_id: ENV['AWS_ACCESS_KEY'],
260
+ secret_access_key: ENV['AWS_SECRET_ACCESS_KEY']
261
+ end
149
262
  }
150
- ```
263
+ }
264
+ ```
151
265
 
152
- ### Index definition
266
+ #### Index definition
153
267
 
154
268
  1. Create `/app/chewy/users_index.rb`
155
269
 
@@ -159,41 +273,38 @@ If you would like to use AWS's ElasticSearch using an IAM user policy, you will
159
273
  end
160
274
  ```
161
275
 
162
- 2. Add one or more types mapping
276
+ 2. Define index scope (you can omit this part if you don't need to specify a scope (i.e. use PORO objects for import) or options)
163
277
 
164
278
  ```ruby
165
279
  class UsersIndex < Chewy::Index
166
- define_type User.active # or just model instead_of scope: define_type User
280
+ index_scope User.active # or just model instead_of scope: index_scope User
167
281
  end
168
282
  ```
169
283
 
170
- Newly-defined index type class is accessible via `UsersIndex.user` or `UsersIndex::User`
171
-
172
- 3. Add some type mappings
284
+ 3. Add some mappings
173
285
 
174
286
  ```ruby
175
287
  class UsersIndex < Chewy::Index
176
- define_type User.active.includes(:country, :badges, :projects) do
177
- field :first_name, :last_name # multiple fields without additional options
178
- field :email, analyzer: 'email' # Elasticsearch-related options
179
- field :country, value: ->(user) { user.country.name } # custom value proc
180
- field :badges, value: ->(user) { user.badges.map(&:name) } # passing array values to index
181
- field :projects do # the same block syntax for multi_field, if `:type` is specified
182
- field :title
183
- field :description # default data type is `string`
184
- # additional top-level objects passed to value proc:
185
- field :categories, value: ->(project, user) { project.categories.map(&:name) if user.active? }
186
- end
187
- field :rating, type: 'integer' # custom data type
188
- field :created, type: 'date', include_in_all: false,
189
- value: ->{ created_at } # value proc for source object context
288
+ index_scope User.active.includes(:country, :badges, :projects)
289
+ field :first_name, :last_name # multiple fields without additional options
290
+ field :email, analyzer: 'email' # Elasticsearch-related options
291
+ field :country, value: ->(user) { user.country.name } # custom value proc
292
+ field :badges, value: ->(user) { user.badges.map(&:name) } # passing array values to index
293
+ field :projects do # the same block syntax for multi_field, if `:type` is specified
294
+ field :title
295
+ field :description # default data type is `text`
296
+ # additional top-level objects passed to value proc:
297
+ field :categories, value: ->(project, user) { project.categories.map(&:name) if user.active? }
190
298
  end
299
+ field :rating, type: 'integer' # custom data type
300
+ field :created, type: 'date', include_in_all: false,
301
+ value: ->{ created_at } # value proc for source object context
191
302
  end
192
303
  ```
193
304
 
194
305
  [See here for mapping definitions](https://www.elastic.co/guide/en/elasticsearch/reference/current/mapping.html).
195
306
 
196
- 4. Add some index- and type-related settings. Analyzer repositories might be used as well. See `Chewy::Index.settings` docs for details:
307
+ 4. Add some index-related settings. Analyzer repositories might be used as well. See `Chewy::Index.settings` docs for details:
197
308
 
198
309
  ```ruby
199
310
  class UsersIndex < Chewy::Index
@@ -206,23 +317,22 @@ If you would like to use AWS's ElasticSearch using an IAM user policy, you will
206
317
  }
207
318
  }
208
319
 
209
- define_type User.active.includes(:country, :badges, :projects) do
210
- root date_detection: false do
211
- template 'about_translations.*', type: 'text', analyzer: 'standard'
212
-
213
- field :first_name, :last_name
214
- field :email, analyzer: 'email'
215
- field :country, value: ->(user) { user.country.name }
216
- field :badges, value: ->(user) { user.badges.map(&:name) }
217
- field :projects do
218
- field :title
219
- field :description
220
- end
221
- field :about_translations, type: 'object' # pass object type explicitly if necessary
222
- field :rating, type: 'integer'
223
- field :created, type: 'date', include_in_all: false,
224
- value: ->{ created_at }
320
+ index_scope User.active.includes(:country, :badges, :projects)
321
+ root date_detection: false do
322
+ template 'about_translations.*', type: 'text', analyzer: 'standard'
323
+
324
+ field :first_name, :last_name
325
+ field :email, analyzer: 'email'
326
+ field :country, value: ->(user) { user.country.name }
327
+ field :badges, value: ->(user) { user.badges.map(&:name) }
328
+ field :projects do
329
+ field :title
330
+ field :description
225
331
  end
332
+ field :about_translations, type: 'object' # pass object type explicitly if necessary
333
+ field :rating, type: 'integer'
334
+ field :created, type: 'date', include_in_all: false,
335
+ value: ->{ created_at }
226
336
  end
227
337
  end
228
338
  ```
@@ -230,45 +340,38 @@ If you would like to use AWS's ElasticSearch using an IAM user policy, you will
230
340
  [See index settings here](https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-update-settings.html).
231
341
  [See root object settings here](https://www.elastic.co/guide/en/elasticsearch/reference/current/dynamic-field-mapping.html).
232
342
 
233
- See [mapping.rb](lib/chewy/type/mapping.rb) for more details.
343
+ See [mapping.rb](lib/chewy/index/mapping.rb) for more details.
234
344
 
235
345
  5. Add model-observing code
236
346
 
237
347
  ```ruby
238
348
  class User < ActiveRecord::Base
239
- update_index('users#user') { self } # specifying index, type and back-reference
349
+ update_index('users') { self } # specifying index and back-reference
240
350
  # for updating after user save or destroy
241
351
  end
242
352
 
243
353
  class Country < ActiveRecord::Base
244
354
  has_many :users
245
355
 
246
- update_index('users#user') { users } # return single object or collection
356
+ update_index('users') { users } # return single object or collection
247
357
  end
248
358
 
249
359
  class Project < ActiveRecord::Base
250
- update_index('users#user') { user if user.active? } # you can return even `nil` from the back-reference
251
- end
252
-
253
- class Badge < ActiveRecord::Base
254
- has_and_belongs_to_many :users
255
-
256
- update_index('users') { users } # if index has only one type
257
- # there is no need to specify updated type
360
+ update_index('users') { user if user.active? } # you can return even `nil` from the back-reference
258
361
  end
259
362
 
260
363
  class Book < ActiveRecord::Base
261
- update_index(->(book) {"books#book_#{book.language}"}) { self } # dynamic index and type with proc.
262
- # For book with language == "en"
263
- # this code will generate `books#book_en`
364
+ update_index(->(book) {"books_#{book.language}"}) { self } # dynamic index name with proc.
365
+ # For book with language == "en"
366
+ # this code will generate `books_en`
264
367
  end
265
368
  ```
266
369
 
267
370
  Also, you can use the second argument for method name passing:
268
371
 
269
372
  ```ruby
270
- update_index('users#user', :self)
271
- update_index('users#user', :users)
373
+ update_index('users', :self)
374
+ update_index('users', :users)
272
375
  ```
273
376
 
274
377
  In the case of a belongs_to association you may need to update both associated objects, previous and current:
@@ -277,47 +380,28 @@ If you would like to use AWS's ElasticSearch using an IAM user policy, you will
277
380
  class City < ActiveRecord::Base
278
381
  belongs_to :country
279
382
 
280
- update_index('cities#city') { self }
281
- update_index 'countries#country' do
282
- # For the latest active_record changed values are
283
- # already in `previous_changes` hash,
284
- # but for mongoid you have to use `changes` hash
383
+ update_index('cities') { self }
384
+ update_index 'countries' do
285
385
  previous_changes['country_id'] || country
286
386
  end
287
387
  end
288
388
  ```
289
389
 
290
- You can observe Sequel models in the same way as ActiveRecord:
390
+ ### Default import options
291
391
 
292
- ```ruby
293
- class User < Sequel::Model
294
- update_index('users#user') { self }
295
- end
296
- ```
297
-
298
- However, to make it work, you must load the chewy plugin into Sequel model:
299
-
300
- ```ruby
301
- Sequel::Model.plugin :chewy_observe # for all models, or...
302
- User.plugin :chewy_observe # just for User
303
- ```
304
-
305
- ### Type default import options
306
-
307
- Every type has `default_import_options` configuration to specify, suddenly, default import options:
392
+ Every index has `default_import_options` configuration to specify, suddenly, default import options:
308
393
 
309
394
  ```ruby
310
395
  class ProductsIndex < Chewy::Index
311
- define_type Post.includes(:tags) do
312
- default_import_options batch_size: 100, bulk_size: 10.megabytes, refresh: false
396
+ index_scope Post.includes(:tags)
397
+ default_import_options batch_size: 100, bulk_size: 10.megabytes, refresh: false
313
398
 
314
- field :name
315
- field :tags, value: -> { tags.map(&:name) }
316
- end
399
+ field :name
400
+ field :tags, value: -> { tags.map(&:name) }
317
401
  end
318
402
  ```
319
403
 
320
- See [import.rb](lib/chewy/type/import.rb) for available options.
404
+ See [import.rb](lib/chewy/index/import.rb) for available options.
321
405
 
322
406
  ### Multi (nested) and object field types
323
407
 
@@ -337,24 +421,12 @@ To define a multi field you have to specify any type except for `object` or `nes
337
421
  ```ruby
338
422
  field :full_name, type: 'text', value: ->{ full_name.strip } do
339
423
  field :ordered, analyzer: 'ordered'
340
- field :untouched, index: 'not_analyzed'
424
+ field :untouched, type: 'keyword'
341
425
  end
342
426
  ```
343
427
 
344
428
  The `value:` option for internal fields will no longer be effective.
345
429
 
346
- ### Parent and children types
347
-
348
- To define [parent](https://www.elastic.co/guide/en/elasticsearch/guide/current/parent-child-mapping.html) type for a given index_type, you can include root options for the type where you can specify parent_type and parent_id
349
-
350
- ```ruby
351
- define_type User.includes(:account) do
352
- root parent: 'account', parent_id: ->{ account_id } do
353
- field :created_at, type: 'date'
354
- field :task_id, type: 'integer'
355
- end
356
- end
357
- ```
358
430
  ### Geo Point fields
359
431
 
360
432
  You can use [Elasticsearch's geo mapping](https://www.elastic.co/guide/en/elasticsearch/reference/current/geo-point.html) with the `geo_point` field type, allowing you to query, filter and order by latitude and longitude. You can use the following hash format:
@@ -374,20 +446,36 @@ end
374
446
 
375
447
  See the section on *Script fields* for details on calculating distance in a search.
376
448
 
449
+ ### Join fields
450
+
451
+ You can use a [join field](https://www.elastic.co/guide/en/elasticsearch/reference/current/parent-join.html)
452
+ to implement parent-child relationships between documents.
453
+ It [replaces the old `parent_id` based parent-child mapping](https://www.elastic.co/guide/en/elasticsearch/reference/current/removal-of-types.html#parent-child-mapping-types)
454
+
455
+ To use it, you need to pass `relations` and `join` (with `type` and `id`) options:
456
+ ```ruby
457
+ field :hierarchy_link, type: :join, relations: {question: %i[answer comment], answer: :vote, vote: :subvote}, join: {type: :comment_type, id: :commented_id}
458
+ ```
459
+ assuming you have `comment_type` and `commented_id` fields in your model.
460
+
461
+ Note that when you reindex a parent, it's children and grandchildren will be reindexed as well.
462
+ This may require additional queries to the primary database and to elastisearch.
463
+
464
+ Also note that the join field doesn't support crutches (it should be a field directly defined on the model).
465
+
377
466
  ### Crutches™ technology
378
467
 
379
468
  Assume you are defining your index like this (product has_many categories through product_categories):
380
469
 
381
470
  ```ruby
382
471
  class ProductsIndex < Chewy::Index
383
- define_type Product.includes(:categories) do
384
- field :name
385
- field :category_names, value: ->(product) { product.categories.map(&:name) } # or shorter just -> { categories.map(&:name) }
386
- end
472
+ index_scope Product.includes(:categories)
473
+ field :name
474
+ field :category_names, value: ->(product) { product.categories.map(&:name) } # or shorter just -> { categories.map(&:name) }
387
475
  end
388
476
  ```
389
477
 
390
- Then the Chewy reindexing flow will look like the following pseudo-code (even in Mongoid):
478
+ Then the Chewy reindexing flow will look like the following pseudo-code:
391
479
 
392
480
  ```ruby
393
481
  Product.includes(:categories).find_in_batches(1000) do |batch|
@@ -399,26 +487,23 @@ Product.includes(:categories).find_in_batches(1000) do |batch|
399
487
  end
400
488
  ```
401
489
 
402
- But in Rails 4.1 and 4.2 you may face a problem with slow associations (take a look at https://github.com/rails/rails/pull/19423). Also, there might be really complicated cases when associations are not applicable.
403
-
404
- Then you can replace Rails associations with Chewy Crutches™ technology:
490
+ If you meet complicated cases when associations are not applicable you can replace Rails associations with Chewy Crutches™ technology:
405
491
 
406
492
  ```ruby
407
493
  class ProductsIndex < Chewy::Index
408
- define_type Product do
409
- crutch :categories do |collection| # collection here is a current batch of products
410
- # data is fetched with a lightweight query without objects initialization
411
- data = ProductCategory.joins(:category).where(product_id: collection.map(&:id)).pluck(:product_id, 'categories.name')
412
- # then we have to convert fetched data to appropriate format
413
- # this will return our data in structure like:
414
- # {123 => ['sweets', 'juices'], 456 => ['meat']}
415
- data.each.with_object({}) { |(id, name), result| (result[id] ||= []).push(name) }
416
- end
417
-
418
- field :name
419
- # simply use crutch-fetched data as a value:
420
- field :category_names, value: ->(product, crutches) { crutches.categories[product.id] }
494
+ index_scope Product
495
+ crutch :categories do |collection| # collection here is a current batch of products
496
+ # data is fetched with a lightweight query without objects initialization
497
+ data = ProductCategory.joins(:category).where(product_id: collection.map(&:id)).pluck(:product_id, 'categories.name')
498
+ # then we have to convert fetched data to appropriate format
499
+ # this will return our data in structure like:
500
+ # {123 => ['sweets', 'juices'], 456 => ['meat']}
501
+ data.each.with_object({}) { |(id, name), result| (result[id] ||= []).push(name) }
421
502
  end
503
+
504
+ field :name
505
+ # simply use crutch-fetched data as a value:
506
+ field :category_names, value: ->(product, crutches) { crutches.categories[product.id] }
422
507
  end
423
508
  ```
424
509
 
@@ -440,22 +525,21 @@ So Chewy Crutches™ technology is able to increase your indexing performance in
440
525
 
441
526
  ### Witchcraft™ technology
442
527
 
443
- One more experimental technology to increase import performance. As far as you know, chewy defines value proc for every imported field in mapping, so at the import time each of this procs is executed on imported object to extract result document to import. It would be great for performance to use one huge whole-document-returning proc instead. So basically the idea or Witchcraft™ technology is to compile a single document-returning proc from the type definition.
528
+ One more experimental technology to increase import performance. As far as you know, chewy defines value proc for every imported field in mapping, so at the import time each of this procs is executed on imported object to extract result document to import. It would be great for performance to use one huge whole-document-returning proc instead. So basically the idea or Witchcraft™ technology is to compile a single document-returning proc from the index definition.
444
529
 
445
530
  ```ruby
446
- define_type Product do
447
- witchcraft!
448
-
449
- field :title
450
- field :tags, value: -> { tags.map(&:name) }
451
- field :categories do
452
- field :name, value: -> (product, category) { category.name }
453
- field :type, value: -> (product, category, crutch) { crutch.types[category.name] }
454
- end
531
+ index_scope Product
532
+ witchcraft!
533
+
534
+ field :title
535
+ field :tags, value: -> { tags.map(&:name) }
536
+ field :categories do
537
+ field :name, value: -> (product, category) { category.name }
538
+ field :type, value: -> (product, category, crutch) { crutch.types[category.name] }
455
539
  end
456
540
  ```
457
541
 
458
- The type definition above will be compiled to something close to:
542
+ The index definition above will be compiled to something close to:
459
543
 
460
544
  ```ruby
461
545
  -> (object, crutches) do
@@ -485,7 +569,7 @@ Obviously not every type of definition might be compiled. There are some restric
485
569
  end
486
570
  ```
487
571
 
488
- However, it is quite possible that your type definition will be supported by Witchcraft™ technology out of the box in the most of the cases.
572
+ However, it is quite possible that your index definition will be supported by Witchcraft™ technology out of the box in the most of the cases.
489
573
 
490
574
  ### Raw Import
491
575
 
@@ -512,13 +596,12 @@ class LightweightProduct
512
596
  end
513
597
  end
514
598
 
515
- define_type Product do
516
- default_import_options raw_import: ->(hash) {
517
- LightweightProduct.new(hash)
518
- }
599
+ index_scope Product
600
+ default_import_options raw_import: ->(hash) {
601
+ LightweightProduct.new(hash)
602
+ }
519
603
 
520
- field :created_at, 'datetime'
521
- end
604
+ field :created_at, 'datetime'
522
605
  ```
523
606
 
524
607
  Also, you can pass `:raw_import` option to the `import` method explicitly.
@@ -529,6 +612,24 @@ By default, when you perform import Chewy checks whether an index exists and cre
529
612
  You can turn off this feature to decrease Elasticsearch hits count.
530
613
  To do so you need to set `skip_index_creation_on_import` parameter to `false` in your `config/chewy.yml`
531
614
 
615
+ ### Skip record fields during import
616
+
617
+ You can use `ignore_blank: true` to skip fields that return `true` for the `.blank?` method:
618
+
619
+ ```ruby
620
+ index_scope Country
621
+ field :id
622
+ field :cities, ignore_blank: true do
623
+ field :id
624
+ field :name
625
+ field :surname, ignore_blank: true
626
+ field :description
627
+ end
628
+ ```
629
+
630
+ #### Default values for different types
631
+
632
+ By default `ignore_blank` is false on every type except `geo_point`.
532
633
 
533
634
  ### Journaling
534
635
 
@@ -542,7 +643,6 @@ Common journal record looks like this:
542
643
  "action": "index",
543
644
  "object_id": [1, 2, 3],
544
645
  "index_name": "...",
545
- "type_name": "...",
546
646
  "created_at": "<timestamp>"
547
647
  }
548
648
  ```
@@ -568,9 +668,8 @@ Or as a default import option for an index:
568
668
 
569
669
  ```ruby
570
670
  class CityIndex
571
- define_type City do
572
- default_import_options journal: true
573
- end
671
+ index_scope City
672
+ default_import_options journal: true
574
673
  end
575
674
  ```
576
675
 
@@ -578,18 +677,7 @@ You may be wondering why do you need it? The answer is simple: not to lose the d
578
677
 
579
678
  Imagine that you reset your index in a zero-downtime manner (to separate index), and at the meantime somebody keeps updating the data frequently (to old index). So all these actions will be written to the journal index and you'll be able to apply them after index reset using the `Chewy::Journal` interface.
580
679
 
581
- ### Types access
582
-
583
- You can access index-defined types with the following API:
584
-
585
- ```ruby
586
- UsersIndex::User # => UsersIndex::User
587
- UsersIndex.type_hash['user'] # => UsersIndex::User
588
- UsersIndex.type('user') # => UsersIndex::User
589
- UsersIndex.type('foo') # => raises error UndefinedType("Unknown type in UsersIndex: foo")
590
- UsersIndex.types # => [UsersIndex::User]
591
- UsersIndex.type_names # => ['user']
592
- ```
680
+ When enabled, journal can grow to enormous size, consider setting up cron job that would clean it occasionally using [`chewy:journal:clean` rake task](#chewyjournal).
593
681
 
594
682
  ### Index manipulation
595
683
 
@@ -603,25 +691,22 @@ UsersIndex.create! # use bang or non-bang methods
603
691
  UsersIndex.purge
604
692
  UsersIndex.purge! # deletes then creates index
605
693
 
606
- UsersIndex::User.import # import with 0 arguments process all the data specified in type definition
607
- # literally, User.active.includes(:country, :badges, :projects).find_in_batches
608
- UsersIndex::User.import User.where('rating > 100') # or import specified users scope
609
- UsersIndex::User.import User.where('rating > 100').to_a # or import specified users array
610
- UsersIndex::User.import [1, 2, 42] # pass even ids for import, it will be handled in the most effective way
611
- UsersIndex::User.import User.where('rating > 100'), update_fields: [:email] # if update fields are specified - it will update their values only with the `update` bulk action.
694
+ UsersIndex.import # import with 0 arguments process all the data specified in index_scope definition
695
+ UsersIndex.import User.where('rating > 100') # or import specified users scope
696
+ UsersIndex.import User.where('rating > 100').to_a # or import specified users array
697
+ UsersIndex.import [1, 2, 42] # pass even ids for import, it will be handled in the most effective way
698
+ UsersIndex.import User.where('rating > 100'), update_fields: [:email] # if update fields are specified - it will update their values only with the `update` bulk action
699
+ UsersIndex.import! # raises an exception in case of any import errors
612
700
 
613
- UsersIndex.import # import every defined type
614
- UsersIndex.import user: User.where('rating > 100') # import only active users to `user` type.
615
- # Other index types, if exists, will be imported with default scope from the type definition.
616
701
  UsersIndex.reset! # purges index and imports default data for all types
617
702
  ```
618
703
 
619
- If the passed user is `#destroyed?`, or satisfies a `delete_if` type option, or the specified id does not exist in the database, import will perform delete from index action for this object.
704
+ If the passed user is `#destroyed?`, or satisfies a `delete_if` index_scope option, or the specified id does not exist in the database, import will perform delete from index action for this object.
620
705
 
621
706
  ```ruby
622
- define_type User, delete_if: :deleted_at
623
- define_type User, delete_if: -> { deleted_at }
624
- define_type User, delete_if: ->(user) { user.deleted_at }
707
+ index_scope User, delete_if: :deleted_at
708
+ index_scope User, delete_if: -> { deleted_at }
709
+ index_scope User, delete_if: ->(user) { user.deleted_at }
625
710
  ```
626
711
 
627
712
  See [actions.rb](lib/chewy/index/actions.rb) for more details.
@@ -632,13 +717,12 @@ Assume you've got the following code:
632
717
 
633
718
  ```ruby
634
719
  class City < ActiveRecord::Base
635
- update_index 'cities#city', :self
720
+ update_index 'cities', :self
636
721
  end
637
722
 
638
723
  class CitiesIndex < Chewy::Index
639
- define_type City do
640
- field :name
641
- end
724
+ index_scope City
725
+ field :name
642
726
  end
643
727
  ```
644
728
 
@@ -658,26 +742,38 @@ end
658
742
 
659
743
  Using this strategy delays the index update request until the end of the block. Updated records are aggregated and the index update happens with the bulk API. So this strategy is highly optimized.
660
744
 
661
- #### `:resque`
745
+ #### `:sidekiq`
662
746
 
663
- This does the same thing as `:atomic`, but asynchronously using resque. The default queue name is `chewy`. Patch `Chewy::Strategy::Resque::Worker` for index updates improving.
747
+ This does the same thing as `:atomic`, but asynchronously using sidekiq. Patch `Chewy::Strategy::Sidekiq::Worker` for index updates improving.
664
748
 
665
749
  ```ruby
666
- Chewy.strategy(:resque) do
750
+ Chewy.strategy(:sidekiq) do
667
751
  City.popular.map(&:do_some_update_action!)
668
752
  end
669
753
  ```
670
754
 
671
- #### `:sidekiq`
755
+ The default queue name is `chewy`, you can customize it in settings: `sidekiq.queue_name`
756
+ ```
757
+ Chewy.settings[:sidekiq] = {queue: :low}
758
+ ```
672
759
 
673
- This does the same thing as `:atomic`, but asynchronously using sidekiq. Patch `Chewy::Strategy::Sidekiq::Worker` for index updates improving.
760
+ #### `:lazy_sidekiq`
761
+
762
+ This does the same thing as `:sidekiq`, but with lazy evaluation. Beware it does not allow you to use any non-persistent record state for indices and conditions because record will be re-fetched from database asynchronously using sidekiq. However for destroying records strategy will fallback to `:sidekiq` because it's not possible to re-fetch deleted records from database.
763
+
764
+ The purpose of this strategy is to improve the response time of the code that should update indexes, as it does not only defer actual ES calls to a background job but `update_index` callbacks evaluation (for created and updated objects) too. Similar to `:sidekiq`, index update is asynchronous so this strategy cannot be used when data and index synchronization is required.
674
765
 
675
766
  ```ruby
676
- Chewy.strategy(:sidekiq) do
767
+ Chewy.strategy(:lazy_sidekiq) do
677
768
  City.popular.map(&:do_some_update_action!)
678
769
  end
679
770
  ```
680
771
 
772
+ The default queue name is `chewy`, you can customize it in settings: `sidekiq.queue_name`
773
+ ```
774
+ Chewy.settings[:sidekiq] = {queue: :low}
775
+ ```
776
+
681
777
  #### `:active_job`
682
778
 
683
779
  This does the same thing as `:atomic`, but using ActiveJob. This will inherit the ActiveJob configuration settings including the `active_job.queue_adapter` setting for the environment. Patch `Chewy::Strategy::ActiveJob::Worker` for index updates improving.
@@ -688,14 +784,9 @@ Chewy.strategy(:active_job) do
688
784
  end
689
785
  ```
690
786
 
691
- #### `:shoryuken`
692
-
693
- This does the same thing as `:atomic`, but asynchronously using shoryuken. Patch `Chewy::Strategy::Shoryuken::Worker` for index updates improving.
694
-
695
- ```ruby
696
- Chewy.strategy(:shoryuken) do
697
- City.popular.map(&:do_some_update_action!)
698
- end
787
+ The default queue name is `chewy`, you can customize it in settings: `active_job.queue_name`
788
+ ```
789
+ Chewy.settings[:active_job] = {queue: :low}
699
790
  ```
700
791
 
701
792
  #### `:urgent`
@@ -773,6 +864,12 @@ RSpec.configure do |config|
773
864
  end
774
865
  ```
775
866
 
867
+ ### Elasticsearch client options
868
+
869
+ All connection options, except the `:prefix`, are passed to the `Elasticseach::Client.new` ([chewy/lib/chewy.rb](https://github.com/toptal/chewy/blob/f5bad9f83c21416ac10590f6f34009c645062e89/lib/chewy.rb#L153-L160)):
870
+
871
+ Here's the relevant Elasticsearch documentation on the subject: https://rubydoc.info/gems/elasticsearch-transport#setting-hosts
872
+
776
873
  ### `ActiveSupport::Notifications` support
777
874
 
778
875
  Chewy has notifying the following events:
@@ -784,7 +881,7 @@ Chewy has notifying the following events:
784
881
 
785
882
  #### `import_objects.chewy` payload
786
883
 
787
- * `payload[:type]`: currently imported type
884
+ * `payload[:index]`: currently imported index name
788
885
  * `payload[:import]`: imports stats, total imported and deleted objects count:
789
886
 
790
887
  ```ruby
@@ -884,45 +981,46 @@ ActiveSupport::Notifications.subscribe(/.chewy$/, ChewySubscriber.new)
884
981
 
885
982
  ### Search requests
886
983
 
887
- Long story short: there is a new DSL that supports ES2 and ES5, the previous DSL version (which supports ES1 and ES2) documentation was moved to [LEGACY_DSL.md](LEGACY_DSL.md).
888
-
889
- If you want to use the old DSL - simply do `Chewy.search_class = Chewy::Query` somewhere before indices are initialized.
890
-
891
- The new DSL is enabled by default, here is a quick introduction.
984
+ Quick introduction.
892
985
 
893
986
  #### Composing requests
894
987
 
895
- The request DSL have the same chainable nature as AR or Mongoid ones. The main class is `Chewy::Search::Request`. It is possible to perform requests on behalf of indices or types:
988
+ The request DSL have the same chainable nature as AR. The main class is `Chewy::Search::Request`.
896
989
 
897
990
  ```ruby
898
- PlaceIndex.query(match: {name: 'London'}) # returns documents of any type
899
- PlaceIndex::City.query(match: {name: 'London'}) # returns cities only.
991
+ CitiesIndex.query(match: {name: 'London'})
900
992
  ```
901
993
 
902
- Main methods of the request DSL are: `query`, `filter` and `post_filter`, it is possible to pass pure query hashes or use `elasticsearch-dsl`. Also, there is an additional
994
+ Main methods of the request DSL are: `query`, `filter` and `post_filter`, it is possible to pass pure query hashes or use `elasticsearch-dsl`.
903
995
 
904
996
  ```ruby
905
- PlaceIndex
997
+ CitiesIndex
906
998
  .filter(term: {name: 'Bangkok'})
907
- .query { match name: 'London' }
999
+ .query(match: {name: 'London'})
908
1000
  .query.not(range: {population: {gt: 1_000_000}})
909
1001
  ```
910
1002
 
911
- See https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl.html and https://github.com/elastic/elasticsearch-ruby/tree/master/elasticsearch-dsl for more details.
1003
+ You can query a set of indexes at once:
1004
+
1005
+ ```ruby
1006
+ CitiesIndex.indices(CountriesIndex).query(match: {name: 'Some'})
1007
+ ```
1008
+
1009
+ See https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl.html and https://github.com/elastic/elasticsearch-dsl-ruby for more details.
912
1010
 
913
1011
  An important part of requests manipulation is merging. There are 4 methods to perform it: `merge`, `and`, `or`, `not`. See [Chewy::Search::QueryProxy](lib/chewy/search/query_proxy.rb) for details. Also, `only` and `except` methods help to remove unneeded parts of the request.
914
1012
 
915
1013
  Every other request part is covered by a bunch of additional methods, see [Chewy::Search::Request](lib/chewy/search/request.rb) for details:
916
1014
 
917
1015
  ```ruby
918
- PlaceIndex.limit(10).offset(30).order(:name, {population: {order: :desc}})
1016
+ CitiesIndex.limit(10).offset(30).order(:name, {population: {order: :desc}})
919
1017
  ```
920
1018
 
921
1019
  Request DSL also provides additional scope actions, like `delete_all`, `exists?`, `count`, `pluck`, etc.
922
1020
 
923
1021
  #### Pagination
924
1022
 
925
- The request DSL supports pagination with `Kaminari` and `WillPaginate`. An appropriate extension is enabled on initializtion if any of libraries is available. See [Chewy::Search](lib/chewy/search.rb) and [Chewy::Search::Pagination](lib/chewy/search/pagination/) namespace for details.
1023
+ The request DSL supports pagination with `Kaminari`. An extension is enabled on initializtion if `Kaminari` is available. See [Chewy::Search](lib/chewy/search.rb) and [Chewy::Search::Pagination::Kaminari](lib/chewy/search/pagination/kaminari.rb) for details.
926
1024
 
927
1025
  #### Named scopes
928
1026
 
@@ -941,8 +1039,8 @@ See [Chewy::Search::Scrolling](lib/chewy/search/scrolling.rb) for details.
941
1039
  It is possible to load ORM/ODM source objects with the `objects` method. To provide additional loading options use `load` method:
942
1040
 
943
1041
  ```ruby
944
- PlacesIndex.load(scope: -> { active }).to_a # to_a returns `Chewy::Type` wrappers.
945
- PlacesIndex.load(scope: -> { active }).objects # An array of AR source objects.
1042
+ CitiesIndex.load(scope: -> { active }).to_a # to_a returns `Chewy::Index` wrappers.
1043
+ CitiesIndex.load(scope: -> { active }).objects # An array of AR source objects.
946
1044
  ```
947
1045
 
948
1046
  See [Chewy::Search::Loader](lib/chewy/search/loader.rb) for more details.
@@ -950,23 +1048,12 @@ See [Chewy::Search::Loader](lib/chewy/search/loader.rb) for more details.
950
1048
  In case when it is necessary to iterate through both of the wrappers and objects simultaneously, `object_hash` method helps a lot:
951
1049
 
952
1050
  ```ruby
953
- scope = PlacesIndex.load(scope: -> { active })
1051
+ scope = CitiesIndex.load(scope: -> { active })
954
1052
  scope.each do |wrapper|
955
1053
  scope.object_hash[wrapper]
956
1054
  end
957
1055
  ```
958
1056
 
959
- #### Legacy DSL incompatibilities
960
-
961
- * Filters advanced block DSL is not supported anymore, `elasticsearch-dsl` is used instead.
962
- * Things like `query_mode` and `filter_mode` are in past, use advanced DSL to achieve similar behavior. See [Chewy::Search::QueryProxy](lib/chewy/search/query_proxy.rb) for details.
963
- * `preload` method is no more, the collection returned by scope doesn't depend on loading options, scope always returns `Chewy::Type` wrappers. To get ORM/ODM objects, use `#objects` method.
964
- * Some of the methods have changed their purpose: `only` was used to filter fields before, now it filters the scope. To filter fields use `source` or `stored_fields`.
965
- * `types!` method is no more, use `except(:types).types(...)`
966
- * Named aggregations are not supported, use named scopes instead.
967
- * A lot of query-level methods were not ported: everything that is related to boost and scoring. Use `query` manipulation to provide them.
968
- * `Chewy::Type#_object` returns nil always. Use `Chewy::Search::Response#object_hash` instead.
969
-
970
1057
  ### Rake tasks
971
1058
 
972
1059
  For a Rails application, some index-maintaining rake tasks are defined.
@@ -978,8 +1065,8 @@ Performs zero-downtime reindexing as described [here](https://www.elastic.co/blo
978
1065
  ```bash
979
1066
  rake chewy:reset # resets all the existing indices
980
1067
  rake chewy:reset[users] # resets UsersIndex only
981
- rake chewy:reset[users,places] # resets UsersIndex and PlacesIndex
982
- rake chewy:reset[-users,places] # resets every index in the application except specified ones
1068
+ rake chewy:reset[users,cities] # resets UsersIndex and CitiesIndex
1069
+ rake chewy:reset[-users,cities] # resets every index in the application except specified ones
983
1070
  ```
984
1071
 
985
1072
  #### `chewy:upgrade`
@@ -994,43 +1081,41 @@ See [Chewy::Stash::Specification](lib/chewy/stash.rb) and [Chewy::Index::Specifi
994
1081
  ```bash
995
1082
  rake chewy:upgrade # upgrades all the existing indices
996
1083
  rake chewy:upgrade[users] # upgrades UsersIndex only
997
- rake chewy:upgrade[users,places] # upgrades UsersIndex and PlacesIndex
998
- rake chewy:upgrade[-users,places] # upgrades every index in the application except specified ones
1084
+ rake chewy:upgrade[users,cities] # upgrades UsersIndex and CitiesIndex
1085
+ rake chewy:upgrade[-users,cities] # upgrades every index in the application except specified ones
999
1086
  ```
1000
1087
 
1001
1088
  #### `chewy:update`
1002
1089
 
1003
1090
  It doesn't create indexes, it simply imports everything to the existing ones and fails if the index was not created before.
1004
1091
 
1005
- Unlike `reset` or `upgrade` tasks, it is possible to pass type references to update the particular type. In index name is passed without the type specified, it will update all the types defined for this index.
1006
-
1007
1092
  ```bash
1008
1093
  rake chewy:update # updates all the existing indices
1009
1094
  rake chewy:update[users] # updates UsersIndex only
1010
- rake chewy:update[users,places#city] # updates the whole UsersIndex and PlacesIndex::City type
1011
- rake chewy:update[-users,places#city] # updates every index in the application except every type defined in UsersIndex and the rest of the types defined in PlacesIndex
1095
+ rake chewy:update[users,cities] # updates UsersIndex and CitiesIndex
1096
+ rake chewy:update[-users,cities] # updates every index in the application except UsersIndex and CitiesIndex
1012
1097
  ```
1013
1098
 
1014
1099
  #### `chewy:sync`
1015
1100
 
1016
- Provides a way to synchronize outdated indexes with the source quickly and without doing a full reset.
1101
+ Provides a way to synchronize outdated indexes with the source quickly and without doing a full reset. By default field `updated_at` is used to find outdated records, but this could be customized by `outdated_sync_field` as described at [Chewy::Index::Syncer](lib/chewy/index/syncer.rb).
1017
1102
 
1018
- Arguments are similar to the ones taken by `chewy:update` task. It is possible to specify a particular type or a whole index.
1103
+ Arguments are similar to the ones taken by `chewy:update` task.
1019
1104
 
1020
- See [Chewy::Type::Syncer](lib/chewy/type/syncer.rb) for more details.
1105
+ See [Chewy::Index::Syncer](lib/chewy/index/syncer.rb) for more details.
1021
1106
 
1022
1107
  ```bash
1023
1108
  rake chewy:sync # synchronizes all the existing indices
1024
1109
  rake chewy:sync[users] # synchronizes UsersIndex only
1025
- rake chewy:sync[users,places#city] # synchronizes the whole UsersIndex and PlacesIndex::City type
1026
- rake chewy:sync[-users,places#city] # synchronizes every index in the application except every type defined in UsersIndex and the rest of the types defined in PlacesIndex
1110
+ rake chewy:sync[users,cities] # synchronizes UsersIndex and CitiesIndex
1111
+ rake chewy:sync[-users,cities] # synchronizes every index in the application except except UsersIndex and CitiesIndex
1027
1112
  ```
1028
1113
 
1029
1114
  #### `chewy:deploy`
1030
1115
 
1031
1116
  This rake task is especially useful during the production deploy. It is a combination of `chewy:upgrade` and `chewy:sync` and the latter is called only for the indexes that were not reset during the first stage.
1032
1117
 
1033
- It is not possible to specify any particular types/indexes for this task as it doesn't make much sense.
1118
+ It is not possible to specify any particular indexes for this task as it doesn't make much sense.
1034
1119
 
1035
1120
  Right now the approach is that if some data had been updated, but index definition was not changed (no changes satisfying the synchronization algorithm were done), it would be much faster to perform manual partial index update inside data migrations or even manually after the deploy.
1036
1121
 
@@ -1047,23 +1132,43 @@ If the number of processes is not specified explicitly - `parallel` gem tries to
1047
1132
  ```bash
1048
1133
  rake chewy:parallel:reset
1049
1134
  rake chewy:parallel:upgrade[4]
1050
- rake chewy:parallel:update[4,places#city]
1135
+ rake chewy:parallel:update[4,cities]
1051
1136
  rake chewy:parallel:sync[4,-users]
1052
1137
  rake chewy:parallel:deploy[4] # performs parallel upgrade and parallel sync afterwards
1053
1138
  ```
1054
1139
 
1055
1140
  #### `chewy:journal`
1056
1141
 
1057
- This namespace contains two tasks for the journal manipulations: `chewy:journal:apply` and `chewy:journal:clean`. Both are taking time as the first argument (optional for clean) and a list of indexes/types exactly as the tasks above. Time can be in any format parsable by ActiveSupport.
1142
+ This namespace contains two tasks for the journal manipulations: `chewy:journal:apply` and `chewy:journal:clean`. Both are taking time as the first argument (optional for clean) and a list of indexes exactly as the tasks above. Time can be in any format parsable by ActiveSupport.
1058
1143
 
1059
1144
  ```bash
1060
1145
  rake chewy:journal:apply["$(date -v-1H -u +%FT%TZ)"] # apply journaled changes for the past hour
1061
1146
  rake chewy:journal:apply["$(date -v-1H -u +%FT%TZ)",users] # apply journaled changes for the past hour on UsersIndex only
1062
1147
  ```
1063
1148
 
1064
- ### Rspec integration
1149
+ When the size of the journal becomes very large, the classical way of deletion would be obstructive and resource consuming. Fortunately, Chewy internally uses [delete-by-query](https://www.elastic.co/guide/en/elasticsearch/reference/7.17/docs-delete-by-query.html#docs-delete-by-query-task-api) ES function which supports async execution with batching and [throttling](https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-delete-by-query.html#docs-delete-by-query-throttle).
1150
+
1151
+ The available options, which can be set by ENV variables, are listed below:
1152
+ * `WAIT_FOR_COMPLETION` - a boolean flag. It controls async execution. It waits by default. When set to `false` (`0`, `f`, `false` or `off` in any case spelling is accepted as `false`), Elasticsearch performs some preflight checks, launches the request, and returns a task reference you can use to cancel the task or get its status.
1153
+ * `REQUESTS_PER_SECOND` - float. The throttle for this request in sub-requests per second. No throttling is enforced by default.
1154
+ * `SCROLL_SIZE` - integer. The number of documents to be deleted in single sub-request. The default batch size is 1000.
1155
+
1156
+ ```bash
1157
+ rake chewy:journal:clean WAIT_FOR_COMPLETION=false REQUESTS_PER_SECOND=10 SCROLL_SIZE=5000
1158
+ ```
1065
1159
 
1066
- Just add `require 'chewy/rspec'` to your spec_helper.rb and you will get additional features: See [update_index.rb](lib/chewy/rspec/update_index.rb) for more details.
1160
+ ### RSpec integration
1161
+
1162
+ Just add `require 'chewy/rspec'` to your spec_helper.rb and you will get additional features:
1163
+
1164
+ [update_index](lib/chewy/rspec/update_index.rb) helper
1165
+ `mock_elasticsearch_response` helper to mock elasticsearch response
1166
+ `mock_elasticsearch_response_sources` helper to mock elasticsearch response sources
1167
+ `build_query` matcher to compare request and expected query (returns `true`/`false`)
1168
+
1169
+ To use `mock_elasticsearch_response` and `mock_elasticsearch_response_sources` helpers add `include Chewy::Rspec::Helpers` to your tests.
1170
+
1171
+ See [chewy/rspec/](lib/chewy/rspec/) for more details.
1067
1172
 
1068
1173
  ### Minitest integration
1069
1174
 
@@ -1073,6 +1178,14 @@ Since you can set `:bypass` strategy for test suites and manually handle import
1073
1178
 
1074
1179
  But if you require chewy to index/update model regularly in your test suite then you can specify `:urgent` strategy for documents indexing. Add `Chewy.strategy(:urgent)` to test_helper.rb.
1075
1180
 
1181
+ Also, you can use additional helpers:
1182
+
1183
+ `mock_elasticsearch_response` to mock elasticsearch response
1184
+ `mock_elasticsearch_response_sources` to mock elasticsearch response sources
1185
+ `assert_elasticsearch_query` to compare request and expected query (returns `true`/`false`)
1186
+
1187
+ See [chewy/minitest/](lib/chewy/minitest/) for more details.
1188
+
1076
1189
  ### DatabaseCleaner
1077
1190
 
1078
1191
  If you use `DatabaseCleaner` in your tests with [the `transaction` strategy](https://github.com/DatabaseCleaner/database_cleaner#how-to-use), you may run into the problem that `ActiveRecord`'s models are not indexed automatically on save despite the fact that you set the callbacks to do this with the `update_index` method. The issue arises because `chewy` indices data on `after_commit` run as default, but all `after_commit` callbacks are not run with the `DatabaseCleaner`'s' `transaction` strategy. You can solve this issue by changing the `Chewy.use_after_commit_callbacks` option. Just add the following initializer in your Rails application:
@@ -1082,12 +1195,6 @@ If you use `DatabaseCleaner` in your tests with [the `transaction` strategy](htt
1082
1195
  Chewy.use_after_commit_callbacks = !Rails.env.test?
1083
1196
  ```
1084
1197
 
1085
- ## TODO a.k.a coming soon:
1086
-
1087
- * Typecasting support
1088
- * update_all support
1089
- * Maybe, closer ORM/ODM integration, creating index classes implicitly
1090
-
1091
1198
  ## Contributing
1092
1199
 
1093
1200
  1. Fork it (http://github.com/toptal/chewy/fork)
@@ -1097,9 +1204,14 @@ Chewy.use_after_commit_callbacks = !Rails.env.test?
1097
1204
  5. Push to the branch (`git push origin my-new-feature`)
1098
1205
  6. Create new Pull Request
1099
1206
 
1100
- Use the following Rake tasks to control the Elasticsearch cluster while developing.
1207
+ Use the following Rake tasks to control the Elasticsearch cluster while developing, if you prefer native Elasticsearch installation over the dockerized one:
1101
1208
 
1102
1209
  ```bash
1103
1210
  rake elasticsearch:start # start Elasticsearch cluster on 9250 port for tests
1104
1211
  rake elasticsearch:stop # stop Elasticsearch
1105
1212
  ```
1213
+
1214
+ ## Copyright
1215
+
1216
+ Copyright (c) 2013-2021 Toptal, LLC. See [LICENSE.txt](LICENSE.txt) for
1217
+ further details.