chewy 0.9.0 → 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (265) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.rubocop.yml +24 -2
  4. data/.rubocop_todo.yml +2 -2
  5. data/.travis.yml +38 -21
  6. data/.yardopts +5 -0
  7. data/Appraisals +55 -27
  8. data/CHANGELOG.md +57 -12
  9. data/Gemfile +14 -10
  10. data/LEGACY_DSL.md +497 -0
  11. data/README.md +249 -515
  12. data/chewy.gemspec +5 -4
  13. data/gemfiles/rails.4.0.activerecord.gemfile +14 -0
  14. data/gemfiles/rails.4.1.activerecord.gemfile +14 -0
  15. data/gemfiles/rails.4.2.activerecord.gemfile +8 -10
  16. data/gemfiles/rails.4.2.mongoid.5.1.gemfile +9 -10
  17. data/gemfiles/rails.5.0.activerecord.gemfile +8 -10
  18. data/gemfiles/rails.5.0.mongoid.6.0.gemfile +15 -0
  19. data/gemfiles/rails.5.1.activerecord.gemfile +15 -0
  20. data/gemfiles/rails.5.1.mongoid.6.1.gemfile +15 -0
  21. data/gemfiles/sequel.4.45.gemfile +11 -0
  22. data/lib/chewy.rb +77 -43
  23. data/lib/chewy/config.rb +44 -7
  24. data/lib/chewy/errors.rb +2 -2
  25. data/lib/chewy/fields/base.rb +39 -32
  26. data/lib/chewy/fields/root.rb +33 -7
  27. data/lib/chewy/index.rb +237 -149
  28. data/lib/chewy/index/actions.rb +85 -28
  29. data/lib/chewy/index/aliases.rb +2 -1
  30. data/lib/chewy/index/settings.rb +9 -5
  31. data/lib/chewy/index/specification.rb +58 -0
  32. data/lib/chewy/journal.rb +40 -92
  33. data/lib/chewy/query.rb +43 -27
  34. data/lib/chewy/query/compose.rb +13 -13
  35. data/lib/chewy/query/criteria.rb +13 -13
  36. data/lib/chewy/query/filters.rb +1 -1
  37. data/lib/chewy/query/loading.rb +1 -1
  38. data/lib/chewy/query/nodes/and.rb +2 -2
  39. data/lib/chewy/query/nodes/bool.rb +1 -1
  40. data/lib/chewy/query/nodes/equal.rb +2 -2
  41. data/lib/chewy/query/nodes/exists.rb +1 -1
  42. data/lib/chewy/query/nodes/has_relation.rb +2 -2
  43. data/lib/chewy/query/nodes/match_all.rb +1 -1
  44. data/lib/chewy/query/nodes/missing.rb +1 -1
  45. data/lib/chewy/query/nodes/not.rb +2 -2
  46. data/lib/chewy/query/nodes/or.rb +2 -2
  47. data/lib/chewy/query/nodes/prefix.rb +1 -1
  48. data/lib/chewy/query/nodes/query.rb +2 -2
  49. data/lib/chewy/query/nodes/range.rb +4 -4
  50. data/lib/chewy/query/nodes/regexp.rb +4 -4
  51. data/lib/chewy/query/nodes/script.rb +3 -3
  52. data/lib/chewy/query/pagination.rb +10 -1
  53. data/lib/chewy/railtie.rb +1 -0
  54. data/lib/chewy/rake_helper.rb +265 -48
  55. data/lib/chewy/rspec/update_index.rb +30 -22
  56. data/lib/chewy/search.rb +78 -21
  57. data/lib/chewy/search/loader.rb +83 -0
  58. data/lib/chewy/{query → search}/pagination/kaminari.rb +13 -5
  59. data/lib/chewy/search/pagination/will_paginate.rb +41 -0
  60. data/lib/chewy/search/parameters.rb +150 -0
  61. data/lib/chewy/search/parameters/aggs.rb +16 -0
  62. data/lib/chewy/search/parameters/concerns/bool_storage.rb +24 -0
  63. data/lib/chewy/search/parameters/concerns/hash_storage.rb +23 -0
  64. data/lib/chewy/search/parameters/concerns/integer_storage.rb +14 -0
  65. data/lib/chewy/search/parameters/concerns/query_storage.rb +237 -0
  66. data/lib/chewy/search/parameters/concerns/string_array_storage.rb +23 -0
  67. data/lib/chewy/search/parameters/concerns/string_storage.rb +14 -0
  68. data/lib/chewy/search/parameters/docvalue_fields.rb +12 -0
  69. data/lib/chewy/search/parameters/explain.rb +16 -0
  70. data/lib/chewy/search/parameters/filter.rb +47 -0
  71. data/lib/chewy/search/parameters/highlight.rb +16 -0
  72. data/lib/chewy/search/parameters/indices_boost.rb +52 -0
  73. data/lib/chewy/search/parameters/limit.rb +17 -0
  74. data/lib/chewy/search/parameters/load.rb +32 -0
  75. data/lib/chewy/search/parameters/min_score.rb +16 -0
  76. data/lib/chewy/search/parameters/none.rb +27 -0
  77. data/lib/chewy/search/parameters/offset.rb +17 -0
  78. data/lib/chewy/search/parameters/order.rb +64 -0
  79. data/lib/chewy/search/parameters/post_filter.rb +19 -0
  80. data/lib/chewy/search/parameters/preference.rb +16 -0
  81. data/lib/chewy/search/parameters/profile.rb +16 -0
  82. data/lib/chewy/search/parameters/query.rb +19 -0
  83. data/lib/chewy/search/parameters/request_cache.rb +27 -0
  84. data/lib/chewy/search/parameters/rescore.rb +29 -0
  85. data/lib/chewy/search/parameters/script_fields.rb +16 -0
  86. data/lib/chewy/search/parameters/search_after.rb +20 -0
  87. data/lib/chewy/search/parameters/search_type.rb +16 -0
  88. data/lib/chewy/search/parameters/source.rb +73 -0
  89. data/lib/chewy/search/parameters/storage.rb +95 -0
  90. data/lib/chewy/search/parameters/stored_fields.rb +63 -0
  91. data/lib/chewy/search/parameters/suggest.rb +16 -0
  92. data/lib/chewy/search/parameters/terminate_after.rb +16 -0
  93. data/lib/chewy/search/parameters/timeout.rb +16 -0
  94. data/lib/chewy/search/parameters/track_scores.rb +16 -0
  95. data/lib/chewy/search/parameters/types.rb +20 -0
  96. data/lib/chewy/search/parameters/version.rb +16 -0
  97. data/lib/chewy/search/query_proxy.rb +257 -0
  98. data/lib/chewy/search/request.rb +1021 -0
  99. data/lib/chewy/search/response.rb +119 -0
  100. data/lib/chewy/search/scoping.rb +50 -0
  101. data/lib/chewy/search/scrolling.rb +136 -0
  102. data/lib/chewy/stash.rb +70 -0
  103. data/lib/chewy/strategy.rb +10 -3
  104. data/lib/chewy/strategy/active_job.rb +1 -0
  105. data/lib/chewy/strategy/atomic.rb +1 -3
  106. data/lib/chewy/strategy/bypass.rb +1 -1
  107. data/lib/chewy/strategy/resque.rb +1 -0
  108. data/lib/chewy/strategy/shoryuken.rb +40 -0
  109. data/lib/chewy/strategy/sidekiq.rb +13 -3
  110. data/lib/chewy/type.rb +29 -7
  111. data/lib/chewy/type/actions.rb +26 -2
  112. data/lib/chewy/type/adapter/active_record.rb +44 -29
  113. data/lib/chewy/type/adapter/base.rb +27 -7
  114. data/lib/chewy/type/adapter/mongoid.rb +18 -7
  115. data/lib/chewy/type/adapter/object.rb +187 -26
  116. data/lib/chewy/type/adapter/orm.rb +59 -32
  117. data/lib/chewy/type/adapter/sequel.rb +32 -16
  118. data/lib/chewy/type/import.rb +145 -191
  119. data/lib/chewy/type/import/bulk_builder.rb +122 -0
  120. data/lib/chewy/type/import/bulk_request.rb +76 -0
  121. data/lib/chewy/type/import/journal_builder.rb +45 -0
  122. data/lib/chewy/type/import/routine.rb +138 -0
  123. data/lib/chewy/type/mapping.rb +11 -1
  124. data/lib/chewy/type/observe.rb +1 -1
  125. data/lib/chewy/type/syncer.rb +220 -0
  126. data/lib/chewy/type/witchcraft.rb +27 -13
  127. data/lib/chewy/type/wrapper.rb +28 -2
  128. data/lib/chewy/version.rb +1 -1
  129. data/lib/tasks/chewy.rake +84 -26
  130. data/spec/chewy/config_spec.rb +82 -1
  131. data/spec/chewy/fields/base_spec.rb +147 -112
  132. data/spec/chewy/fields/root_spec.rb +75 -18
  133. data/spec/chewy/fields/time_fields_spec.rb +2 -3
  134. data/spec/chewy/index/actions_spec.rb +180 -50
  135. data/spec/chewy/index/aliases_spec.rb +2 -2
  136. data/spec/chewy/index/settings_spec.rb +67 -38
  137. data/spec/chewy/index/specification_spec.rb +160 -0
  138. data/spec/chewy/index_spec.rb +57 -66
  139. data/spec/chewy/journal_spec.rb +149 -54
  140. data/spec/chewy/minitest/helpers_spec.rb +4 -4
  141. data/spec/chewy/minitest/search_index_receiver_spec.rb +1 -1
  142. data/spec/chewy/query/criteria_spec.rb +179 -179
  143. data/spec/chewy/query/filters_spec.rb +15 -15
  144. data/spec/chewy/query/loading_spec.rb +22 -20
  145. data/spec/chewy/query/nodes/and_spec.rb +2 -2
  146. data/spec/chewy/query/nodes/bool_spec.rb +4 -4
  147. data/spec/chewy/query/nodes/equal_spec.rb +19 -19
  148. data/spec/chewy/query/nodes/exists_spec.rb +6 -6
  149. data/spec/chewy/query/nodes/has_child_spec.rb +19 -19
  150. data/spec/chewy/query/nodes/has_parent_spec.rb +19 -19
  151. data/spec/chewy/query/nodes/missing_spec.rb +5 -5
  152. data/spec/chewy/query/nodes/not_spec.rb +3 -2
  153. data/spec/chewy/query/nodes/or_spec.rb +2 -2
  154. data/spec/chewy/query/nodes/prefix_spec.rb +5 -5
  155. data/spec/chewy/query/nodes/query_spec.rb +2 -2
  156. data/spec/chewy/query/nodes/range_spec.rb +18 -18
  157. data/spec/chewy/query/nodes/raw_spec.rb +1 -1
  158. data/spec/chewy/query/nodes/regexp_spec.rb +14 -14
  159. data/spec/chewy/query/nodes/script_spec.rb +4 -4
  160. data/spec/chewy/query/pagination/kaminari_spec.rb +3 -55
  161. data/spec/chewy/query/pagination/will_paginate_spec.rb +5 -0
  162. data/spec/chewy/query/pagination_spec.rb +25 -21
  163. data/spec/chewy/query_spec.rb +501 -560
  164. data/spec/chewy/rake_helper_spec.rb +368 -0
  165. data/spec/chewy/repository_spec.rb +4 -4
  166. data/spec/chewy/rspec/update_index_spec.rb +89 -56
  167. data/spec/chewy/runtime_spec.rb +2 -2
  168. data/spec/chewy/search/loader_spec.rb +117 -0
  169. data/spec/chewy/search/pagination/kaminari_examples.rb +71 -0
  170. data/spec/chewy/search/pagination/kaminari_spec.rb +17 -0
  171. data/spec/chewy/search/pagination/will_paginate_examples.rb +63 -0
  172. data/spec/chewy/search/pagination/will_paginate_spec.rb +17 -0
  173. data/spec/chewy/search/parameters/aggs_spec.rb +5 -0
  174. data/spec/chewy/search/parameters/bool_storage_examples.rb +53 -0
  175. data/spec/chewy/search/parameters/docvalue_fields_spec.rb +5 -0
  176. data/spec/chewy/search/parameters/explain_spec.rb +5 -0
  177. data/spec/chewy/search/parameters/filter_spec.rb +5 -0
  178. data/spec/chewy/search/parameters/hash_storage_examples.rb +59 -0
  179. data/spec/chewy/search/parameters/highlight_spec.rb +5 -0
  180. data/spec/chewy/search/parameters/indices_boost_spec.rb +83 -0
  181. data/spec/chewy/search/parameters/integer_storage_examples.rb +32 -0
  182. data/spec/chewy/search/parameters/limit_spec.rb +5 -0
  183. data/spec/chewy/search/parameters/load_spec.rb +60 -0
  184. data/spec/chewy/search/parameters/min_score_spec.rb +32 -0
  185. data/spec/chewy/search/parameters/none_spec.rb +5 -0
  186. data/spec/chewy/search/parameters/offset_spec.rb +5 -0
  187. data/spec/chewy/search/parameters/order_spec.rb +65 -0
  188. data/spec/chewy/search/parameters/post_filter_spec.rb +5 -0
  189. data/spec/chewy/search/parameters/preference_spec.rb +5 -0
  190. data/spec/chewy/search/parameters/profile_spec.rb +5 -0
  191. data/spec/chewy/search/parameters/query_spec.rb +5 -0
  192. data/spec/chewy/search/parameters/query_storage_examples.rb +388 -0
  193. data/spec/chewy/search/parameters/request_cache_spec.rb +67 -0
  194. data/spec/chewy/search/parameters/rescore_spec.rb +62 -0
  195. data/spec/chewy/search/parameters/script_fields_spec.rb +5 -0
  196. data/spec/chewy/search/parameters/search_after_spec.rb +32 -0
  197. data/spec/chewy/search/parameters/search_type_spec.rb +5 -0
  198. data/spec/chewy/search/parameters/source_spec.rb +156 -0
  199. data/spec/chewy/search/parameters/storage_spec.rb +60 -0
  200. data/spec/chewy/search/parameters/stored_fields_spec.rb +126 -0
  201. data/spec/chewy/search/parameters/string_array_storage_examples.rb +63 -0
  202. data/spec/chewy/search/parameters/string_storage_examples.rb +32 -0
  203. data/spec/chewy/search/parameters/suggest_spec.rb +5 -0
  204. data/spec/chewy/search/parameters/terminate_after_spec.rb +5 -0
  205. data/spec/chewy/search/parameters/timeout_spec.rb +5 -0
  206. data/spec/chewy/search/parameters/track_scores_spec.rb +5 -0
  207. data/spec/chewy/search/parameters/types_spec.rb +5 -0
  208. data/spec/chewy/search/parameters/version_spec.rb +5 -0
  209. data/spec/chewy/search/parameters_spec.rb +130 -0
  210. data/spec/chewy/search/query_proxy_spec.rb +68 -0
  211. data/spec/chewy/search/request_spec.rb +669 -0
  212. data/spec/chewy/search/response_spec.rb +192 -0
  213. data/spec/chewy/search/scrolling_spec.rb +169 -0
  214. data/spec/chewy/search_spec.rb +13 -6
  215. data/spec/chewy/stash_spec.rb +95 -0
  216. data/spec/chewy/strategy/active_job_spec.rb +6 -0
  217. data/spec/chewy/strategy/resque_spec.rb +6 -0
  218. data/spec/chewy/strategy/shoryuken_spec.rb +64 -0
  219. data/spec/chewy/strategy/sidekiq_spec.rb +8 -0
  220. data/spec/chewy/strategy_spec.rb +6 -6
  221. data/spec/chewy/type/actions_spec.rb +29 -10
  222. data/spec/chewy/type/adapter/active_record_spec.rb +203 -91
  223. data/spec/chewy/type/adapter/mongoid_spec.rb +112 -54
  224. data/spec/chewy/type/adapter/object_spec.rb +101 -28
  225. data/spec/chewy/type/adapter/sequel_spec.rb +149 -82
  226. data/spec/chewy/type/import/bulk_builder_spec.rb +279 -0
  227. data/spec/chewy/type/import/bulk_request_spec.rb +102 -0
  228. data/spec/chewy/type/import/journal_builder_spec.rb +95 -0
  229. data/spec/chewy/type/import/routine_spec.rb +110 -0
  230. data/spec/chewy/type/import_spec.rb +350 -271
  231. data/spec/chewy/type/mapping_spec.rb +54 -18
  232. data/spec/chewy/type/observe_spec.rb +5 -1
  233. data/spec/chewy/type/syncer_spec.rb +123 -0
  234. data/spec/chewy/type/witchcraft_spec.rb +45 -29
  235. data/spec/chewy/type/wrapper_spec.rb +63 -23
  236. data/spec/chewy/type_spec.rb +28 -7
  237. data/spec/chewy_spec.rb +75 -7
  238. data/spec/spec_helper.rb +5 -2
  239. data/spec/support/active_record.rb +5 -1
  240. data/spec/support/class_helpers.rb +0 -14
  241. data/spec/support/mongoid.rb +15 -3
  242. data/spec/support/sequel.rb +6 -1
  243. metadata +198 -37
  244. data/gemfiles/rails.3.2.activerecord.gemfile +0 -16
  245. data/gemfiles/rails.3.2.activerecord.kaminari.gemfile +0 -15
  246. data/gemfiles/rails.3.2.activerecord.will_paginate.gemfile +0 -15
  247. data/gemfiles/rails.4.2.activerecord.kaminari.gemfile +0 -16
  248. data/gemfiles/rails.4.2.activerecord.will_paginate.gemfile +0 -16
  249. data/gemfiles/rails.4.2.mongoid.4.0.gemfile +0 -16
  250. data/gemfiles/rails.4.2.mongoid.4.0.kaminari.gemfile +0 -15
  251. data/gemfiles/rails.4.2.mongoid.4.0.will_paginate.gemfile +0 -15
  252. data/gemfiles/rails.4.2.mongoid.5.1.kaminari.gemfile +0 -15
  253. data/gemfiles/rails.4.2.mongoid.5.1.will_paginate.gemfile +0 -15
  254. data/gemfiles/rails.5.0.activerecord.kaminari.gemfile +0 -16
  255. data/gemfiles/rails.5.0.activerecord.will_paginate.gemfile +0 -16
  256. data/gemfiles/sequel.4.38.gemfile +0 -14
  257. data/lib/chewy/journal/apply.rb +0 -31
  258. data/lib/chewy/journal/clean.rb +0 -24
  259. data/lib/chewy/journal/entry.rb +0 -83
  260. data/lib/chewy/journal/query.rb +0 -87
  261. data/lib/chewy/query/pagination/will_paginate.rb +0 -27
  262. data/lib/chewy/query/scoping.rb +0 -20
  263. data/spec/chewy/journal/apply_spec.rb +0 -120
  264. data/spec/chewy/journal/entry_spec.rb +0 -237
  265. data/spec/chewy/query/pagination/will_paginage_spec.rb +0 -59
data/README.md CHANGED
@@ -12,39 +12,49 @@ Chewy is an ODM and wrapper for [the official Elasticsearch client](https://gith
12
12
 
13
13
  ## Table of Contents
14
14
 
15
- * [Why Chewy?] (#why-chewy)
16
- * [Usage] (#usage)
17
- * [Client settings] (#client-settings)
18
- * [Index definition] (#index-definition)
19
- * [Type default import options] (#type-default-import-options)
20
- * [Multi (nested) and object field types] (#multi-nested-and-object-field-types)
21
- * [Geo Point fields] (#geo-point-fields)
22
- * [Crutches™ technology] (#crutches-technology)
23
- * [Witchcraft™ technology] (#witchcraft-technology)
24
- * [Raw Import] (#raw-import)
25
- * [Journaling] (#journaling)
26
- * [Types access] (#types-access)
27
- * [Index manipulation] (#index-manipulation)
28
- * [Index update strategies] (#index-update-strategies)
29
- * [Nesting] (#nesting)
30
- * [Non-block notation] (#non-block-notation)
31
- * [Designing your own strategies] (#designing-your-own-strategies)
32
- * [Rails application strategies integration] (#rails-application-strategies-integration)
33
- * [Index querying] (#index-querying)
34
- * [Additional query action.] (#additional-query-action)
35
- * [Filters query DSL] (#filters-query-dsl)
36
- * [Faceting] (#faceting)
37
- * [Aggregations] (#aggregations)
38
- * [Script fields] (#script-fields)
39
- * [Script scoring] (#script-scoring)
40
- * [Boost Factor] (#boost-factor)
41
- * [Objects loading] (#objects-loading)
42
- * [NewRelic integration] (#newrelic-integration)
43
- * [Rake tasks] (#rake-tasks)
44
- * [Rspec integration] (#rspec-integration)
45
- * [Minitest integration] (#minitest-integration)
46
- * [TODO a.k.a coming soon:] (#todo-aka-coming-soon)
47
- * [Contributing] (#contributing)
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](#activesupport-notifications-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)
48
58
 
49
59
  ## Why Chewy?
50
60
 
@@ -119,6 +129,26 @@ Chewy.logger = Logger.new(STDOUT)
119
129
 
120
130
  See [config.rb](lib/chewy/config.rb) for more details.
121
131
 
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`.
134
+
135
+ ```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
+ }
149
+ }
150
+ ```
151
+
122
152
  ### Index definition
123
153
 
124
154
  1. Create `/app/chewy/users_index.rb`
@@ -311,8 +341,20 @@ field :full_name, type: 'string', value: ->{ full_name.strip } do
311
341
  end
312
342
  ```
313
343
 
314
- The `value:` option for internal fields would no longer be effective.
344
+ The `value:` option for internal fields will no longer be effective.
345
+
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
315
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
+ ```
316
358
  ### Geo Point fields
317
359
 
318
360
  You can use [Elasticsearch's geo mapping](https://www.elastic.co/guide/en/elasticsearch/reference/current/mapping-geo-point-type.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:
@@ -345,7 +387,7 @@ class ProductsIndex < Chewy::Index
345
387
  end
346
388
  ```
347
389
 
348
- Then the Chewy reindexing flow would look like the following pseudo-code (even in Mongoid):
390
+ Then the Chewy reindexing flow will look like the following pseudo-code (even in Mongoid):
349
391
 
350
392
  ```ruby
351
393
  Product.includes(:categories).find_in_batches(1000) do |batch|
@@ -380,7 +422,7 @@ class ProductsIndex < Chewy::Index
380
422
  end
381
423
  ```
382
424
 
383
- An example flow would look like this:
425
+ An example flow will look like this:
384
426
 
385
427
  ```ruby
386
428
  Product.includes(:categories).find_in_batches(1000) do |batch|
@@ -481,12 +523,18 @@ end
481
523
 
482
524
  Also, you can pass `:raw_import` option to the `import` method explicitly.
483
525
 
526
+ ### Index creation during import
527
+
528
+ By default, when you perform import Chewy checks whether an index exists and creates it if it's absent.
529
+ You can turn off this feature to decrease Elasticsearch hits count.
530
+ To do so you need to set `skip_index_creation_on_import` parameter to `false` in your `config/chewy.yml`
531
+
484
532
 
485
533
  ### Journaling
486
534
 
487
535
  You can record all actions that were made to the separate journal index in ElasticSearch.
488
- When you create/update/destroy your records, it will be saved in this special index.
489
- If you make something with a batch of records (e.g. during index reset) it will be saved as a one record, including primary keys of each document that was affected.
536
+ When you create/update/destroy your documents, it will be saved in this special index.
537
+ If you make something with a batch of documents (e.g. during index reset) it will be saved as a one record, including primary keys of each document that was affected.
490
538
  Common journal record looks like this:
491
539
 
492
540
  ```json
@@ -506,7 +554,7 @@ Also, you can specify journal index name. For example:
506
554
  ```yaml
507
555
  # config/chewy.yml
508
556
  production:
509
- journal: true,
557
+ journal: true
510
558
  journal_name: my_super_journal
511
559
  ```
512
560
 
@@ -526,12 +574,9 @@ class CityIndex
526
574
  end
527
575
  ```
528
576
 
529
- You may be wondering why do you need it? The answer is simple: Not to lose the data.
530
- Imagine that:
531
- You reset your index in Zero Downtime manner (to separate index), and 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 with `Chewy::Journal::Apply.since(1.hour.ago.to_i)`.
577
+ You may be wondering why do you need it? The answer is simple: not to lose the data.
532
578
 
533
- For index reset journaling is turned off even if you set `journal: true` in `config/chewy.yml` or in `default_import_options`.
534
- You can change it only if you pass `journal: true` parameter explicitly to `#import`.
579
+ 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.
535
580
 
536
581
  ### Types access
537
582
 
@@ -563,6 +608,7 @@ UsersIndex::User.import # import with 0 arguments process all the data specified
563
608
  UsersIndex::User.import User.where('rating > 100') # or import specified users scope
564
609
  UsersIndex::User.import User.where('rating > 100').to_a # or import specified users array
565
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.
566
612
 
567
613
  UsersIndex.import # import every defined type
568
614
  UsersIndex.import user: User.where('rating > 100') # import only active users to `user` type.
@@ -642,6 +688,16 @@ Chewy.strategy(:active_job) do
642
688
  end
643
689
  ```
644
690
 
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
699
+ ```
700
+
645
701
  #### `:urgent`
646
702
 
647
703
  The following strategy is convenient if you are going to update documents in your index one by one.
@@ -652,7 +708,7 @@ Chewy.strategy(:urgent) do
652
708
  end
653
709
  ```
654
710
 
655
- This code would perform `City.popular.count` requests for ES documents update.
711
+ This code will perform `City.popular.count` requests for ES documents update.
656
712
 
657
713
  It is convenient for use in e.g. the Rails console with non-block notation:
658
714
 
@@ -717,569 +773,244 @@ RSpec.configure do |config|
717
773
  end
718
774
  ```
719
775
 
720
- ### Index querying
721
-
722
- ```ruby
723
- scope = UsersIndex.query(term: {name: 'foo'})
724
- .filter(range: {rating: {gte: 100}})
725
- .order(created: :desc)
726
- .limit(20).offset(100)
727
-
728
- scope.to_a # => will produce array of UserIndex::User or other types instances
729
- scope.map { |user| user.email }
730
- scope.total_count # => will return total objects count
731
-
732
- scope.per(10).page(3) # supports kaminari pagination
733
- scope.explain.map { |user| user._explanation }
734
- scope.only(:id, :email) # returns ids and emails only
735
-
736
- scope.merge(other_scope) # queries could be merged
737
- ```
776
+ ### `ActiveSupport::Notifications` support
738
777
 
739
- Also, queries can be performed on a type individually:
778
+ Chewy has notifying the following events:
740
779
 
741
- ```ruby
742
- UsersIndex::User.filter(term: {name: 'foo'}) # will return UserIndex::User collection only
743
- ```
780
+ #### `search_query.chewy` payload
744
781
 
745
- If you are performing more than one `filter` or `query` in the chain, all the filters and queries will be concatenated in the way specified by
746
- `filter_mode` and `query_mode` respectively.
782
+ * `payload[:index]`: requested index class
783
+ * `payload[:request]`: request hash
747
784
 
748
- The default `filter_mode` is `:and` and the default `query_mode` is `bool`.
785
+ #### `import_objects.chewy` payload
749
786
 
750
- Available filter modes are: `:and`, `:or`, `:must`, `:should` and any minimum_should_match-acceptable value
787
+ * `payload[:type]`: currently imported type
788
+ * `payload[:import]`: imports stats, total imported and deleted objects count:
751
789
 
752
- Available query modes are: `:must`, `:should`, `:dis_max`, any minimum_should_match-acceptable value or float value for dis_max query with tie_breaker specified.
790
+ ```ruby
791
+ {index: 30, delete: 5}
792
+ ```
753
793
 
754
- ```ruby
755
- UsersIndex::User.filter{ name == 'Fred' }.filter{ age < 42 } # will be wrapped with `and` filter
756
- UsersIndex::User.filter{ name == 'Fred' }.filter{ age < 42 }.filter_mode(:should) # will be wrapped with bool `should` filter
757
- UsersIndex::User.filter{ name == 'Fred' }.filter{ age < 42 }.filter_mode('75%') # will be wrapped with bool `should` filter with `minimum_should_match: '75%'`
758
- ```
794
+ * `payload[:errors]`: might not exists. Contains grouped errors with objects ids list:
759
795
 
760
- See [query.rb](lib/chewy/query.rb) for more details.
796
+ ```ruby
797
+ {index: {
798
+ 'error 1 text' => ['1', '2', '3'],
799
+ 'error 2 text' => ['4']
800
+ }, delete: {
801
+ 'delete error text' => ['10', '12']
802
+ }}
803
+ ```
761
804
 
762
- ### Additional query action.
805
+ ### NewRelic integration
763
806
 
764
- You may also perform additional actions on the query scope, such as deleting of all the scope documents:
807
+ To integrate with NewRelic you may use the following example source (config/initializers/chewy.rb):
765
808
 
766
809
  ```ruby
767
- UsersIndex.delete_all
768
- UsersIndex::User.delete_all
769
- UsersIndex.filter{ age < 42 }.delete_all
770
- UsersIndex::User.filter{ age < 42 }.delete_all
771
- ```
810
+ ActiveSupport::Notifications.subscribe('import_objects.chewy') do |name, start, finish, id, payload|
811
+ metric_name = "Database/ElasticSearch/import"
812
+ duration = (finish - start).to_f
813
+ logged = "#{payload[:type]} #{payload[:import].to_a.map{ |i| i.join(':') }.join(', ')}"
772
814
 
773
- ### Filters query DSL
815
+ self.class.trace_execution_scoped([metric_name]) do
816
+ NewRelic::Agent.instance.transaction_sampler.notice_sql(logged, nil, duration)
817
+ NewRelic::Agent.instance.sql_sampler.notice_sql(logged, metric_name, nil, duration)
818
+ NewRelic::Agent.record_metric(metric_name, duration)
819
+ end
820
+ end
774
821
 
775
- There is a test version of the filter-creating DSL:
822
+ ActiveSupport::Notifications.subscribe('search_query.chewy') do |name, start, finish, id, payload|
823
+ metric_name = "Database/ElasticSearch/search"
824
+ duration = (finish - start).to_f
825
+ logged = "#{payload[:type].presence || payload[:index]} #{payload[:request]}"
776
826
 
777
- ```ruby
778
- UsersIndex.filter{ name == 'Fred' } # will produce `term` filter.
779
- UsersIndex.filter{ age <= 42 } # will produce `range` filter.
827
+ self.class.trace_execution_scoped([metric_name]) do
828
+ NewRelic::Agent.instance.transaction_sampler.notice_sql(logged, nil, duration)
829
+ NewRelic::Agent.instance.sql_sampler.notice_sql(logged, metric_name, nil, duration)
830
+ NewRelic::Agent.record_metric(metric_name, duration)
831
+ end
832
+ end
780
833
  ```
781
834
 
782
- The basis of the DSL is the expression. There are 2 types of expressions:
835
+ ### Search requests
783
836
 
784
- * Simple function
837
+ 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).
785
838
 
786
- ```ruby
787
- UsersIndex.filter{ s('doc["num"] > 1') } # script expression
788
- UsersIndex.filter{ q(query_string: {query: 'lazy fox'}) } # query expression
789
- ```
839
+ If you want to use it - simply do `Chewy.search_class = Chewy::Query` somewhere before indices are initialized.
790
840
 
791
- * Field-dependent composite expression
792
- Consists of the field name (with or without dot notation), a value, and an action operator between them. The field name might take additional options for passing to the resulting expression.
841
+ The new DSL is enabled by default, here is a quick introduction.
793
842
 
794
- ```ruby
795
- UsersIndex.filter{ name == 'Name' } # simple field term filter
796
- UsersIndex.filter{ name(:bool) == ['Name1', 'Name2'] } # terms query with `execution: :bool` option passed
797
- UsersIndex.filter{ answers.title =~ /regexp/ } # regexp filter for `answers.title` field
798
- ```
843
+ #### Composing requests
799
844
 
800
- You can combine expressions as you wish with the help of combination operators.
845
+ 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:
801
846
 
802
847
  ```ruby
803
- UsersIndex.filter{ (name == 'Name') & (email == 'Email') } # combination produces `and` filter
804
- UsersIndex.filter{
805
- must(
806
- should(name =~ 'Fr').should_not(name == 'Fred') & (age == 42), email =~ /gmail\.com/
807
- ) | ((roles.admin == true) & name?)
808
- } # many of the combination possibilities
848
+ PlaceIndex.query(match: {name: 'London'}) # returns documents of any type
849
+ PlaceIndex::City.query(match: {name: 'London'}) # returns cities only.
809
850
  ```
810
851
 
811
- There is also a special syntax for cache enabling:
852
+ 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
812
853
 
813
854
  ```ruby
814
- UsersIndex.filter{ ~name == 'Name' } # you can apply tilde to the field name
815
- UsersIndex.filter{ ~(name == 'Name') } # or to the whole expression
816
-
817
- # if you are applying cache to the one part of range filter
818
- # the whole filter will be cached:
819
- UsersIndex.filter{ ~(age > 42) & (age <= 50) }
820
-
821
- # You can pass cache options as a field option also.
822
- UsersIndex.filter{ name(cache: true) == 'Name' }
823
- UsersIndex.filter{ name(cache: false) == 'Name' }
824
-
825
- # With regexp filter you can pass _cache_key
826
- UsersIndex.filter{ name(cache: 'name_regexp') =~ /Name/ }
827
- # Or not
828
- UsersIndex.filter{ name(cache: true) =~ /Name/ }
855
+ PlaceIndex
856
+ .filter(term: {name: 'Bangkok'})
857
+ .query { match name: 'London' }
858
+ .query.not(range: {population: {gt: 1_000_000}})
829
859
  ```
830
860
 
831
- Compliance cheatsheet for filters and DSL expressions:
832
-
833
- * Term filter
834
-
835
- ```json
836
- {"term": {"name": "Fred"}}
837
- {"not": {"term": {"name": "Johny"}}}
838
- ```
839
-
840
- ```ruby
841
- UsersIndex.filter{ name == 'Fred' }
842
- UsersIndex.filter{ name != 'Johny' }
843
- ```
844
-
845
- * Terms filter
846
-
847
- ```json
848
- {"terms": {"name": ["Fred", "Johny"]}}
849
- {"not": {"terms": {"name": ["Fred", "Johny"]}}}
850
-
851
- {"terms": {"name": ["Fred", "Johny"], "execution": "or"}}
852
-
853
- {"terms": {"name": ["Fred", "Johny"], "execution": "and"}}
854
-
855
- {"terms": {"name": ["Fred", "Johny"], "execution": "bool"}}
856
-
857
- {"terms": {"name": ["Fred", "Johny"], "execution": "fielddata"}}
858
- ```
859
-
860
- ```ruby
861
- UsersIndex.filter{ name == ['Fred', 'Johny'] }
862
- UsersIndex.filter{ name != ['Fred', 'Johny'] }
863
-
864
- UsersIndex.filter{ name(:|) == ['Fred', 'Johny'] }
865
- UsersIndex.filter{ name(:or) == ['Fred', 'Johny'] }
866
- UsersIndex.filter{ name(execution: :or) == ['Fred', 'Johny'] }
867
-
868
- UsersIndex.filter{ name(:&) == ['Fred', 'Johny'] }
869
- UsersIndex.filter{ name(:and) == ['Fred', 'Johny'] }
870
- UsersIndex.filter{ name(execution: :and) == ['Fred', 'Johny'] }
871
-
872
- UsersIndex.filter{ name(:b) == ['Fred', 'Johny'] }
873
- UsersIndex.filter{ name(:bool) == ['Fred', 'Johny'] }
874
- UsersIndex.filter{ name(execution: :bool) == ['Fred', 'Johny'] }
875
-
876
- UsersIndex.filter{ name(:f) == ['Fred', 'Johny'] }
877
- UsersIndex.filter{ name(:fielddata) == ['Fred', 'Johny'] }
878
- UsersIndex.filter{ name(execution: :fielddata) == ['Fred', 'Johny'] }
879
- ```
880
-
881
- * Regexp filter (== and =~ are equivalent)
882
-
883
- ```json
884
- {"regexp": {"name.first": "s.*y"}}
885
-
886
- {"not": {"regexp": {"name.first": "s.*y"}}}
887
-
888
- {"regexp": {"name.first": {"value": "s.*y", "flags": "ANYSTRING|INTERSECTION"}}}
889
- ```
890
-
891
- ```ruby
892
- UsersIndex.filter{ name.first == /s.*y/ }
893
- UsersIndex.filter{ name.first =~ /s.*y/ }
894
-
895
- UsersIndex.filter{ name.first != /s.*y/ }
896
- UsersIndex.filter{ name.first !~ /s.*y/ }
897
-
898
- UsersIndex.filter{ name.first(:anystring, :intersection) == /s.*y/ }
899
- UsersIndex.filter{ name.first(flags: [:anystring, :intersection]) == /s.*y/ }
900
- ```
901
-
902
- * Prefix filter
903
-
904
- ```json
905
- {"prefix": {"name": "Fre"}}
906
- {"not": {"prefix": {"name": "Joh"}}}
907
- ```
908
-
909
- ```ruby
910
- UsersIndex.filter{ name =~ re' }
911
- UsersIndex.filter{ name !~ 'Joh' }
912
- ```
913
-
914
- * Exists filter
915
-
916
- ```json
917
- {"exists": {"field": "name"}}
918
- ```
919
-
920
- ```ruby
921
- UsersIndex.filter{ name? }
922
- UsersIndex.filter{ !!name }
923
- UsersIndex.filter{ !!name? }
924
- UsersIndex.filter{ name != nil }
925
- UsersIndex.filter{ !(name == nil) }
926
- ```
927
-
928
- * Missing filter
929
-
930
- ```json
931
- {"missing": {"field": "name", "existence": true, "null_value": false}}
932
- {"missing": {"field": "name", "existence": true, "null_value": true}}
933
- {"missing": {"field": "name", "existence": false, "null_value": true}}
934
- ```
935
-
936
- ```ruby
937
- UsersIndex.filter{ !name }
938
- UsersIndex.filter{ !name? }
939
- UsersIndex.filter{ name == nil }
940
- ```
941
-
942
- * Range
943
-
944
- ```json
945
- {"range": {"age": {"gt": 42}}}
946
- {"range": {"age": {"gte": 42}}}
947
- {"range": {"age": {"lt": 42}}}
948
- {"range": {"age": {"lte": 42}}}
949
-
950
- {"range": {"age": {"gt": 40, "lt": 50}}}
951
- {"range": {"age": {"gte": 40, "lte": 50}}}
952
-
953
- {"range": {"age": {"gt": 40, "lte": 50}}}
954
- {"range": {"age": {"gte": 40, "lt": 50}}}
955
- ```
956
-
957
- ```ruby
958
- UsersIndex.filter{ age > 42 }
959
- UsersIndex.filter{ age >= 42 }
960
- UsersIndex.filter{ age < 42 }
961
- UsersIndex.filter{ age <= 42 }
962
-
963
- UsersIndex.filter{ age == (40..50) }
964
- UsersIndex.filter{ (age > 40) & (age < 50) }
965
- UsersIndex.filter{ age == [40..50] }
966
- UsersIndex.filter{ (age >= 40) & (age <= 50) }
967
-
968
- UsersIndex.filter{ (age > 40) & (age <= 50) }
969
- UsersIndex.filter{ (age >= 40) & (age < 50) }
970
- ```
971
-
972
- * Bool filter
973
-
974
- ```json
975
- {"bool": {
976
- "must": [{"term": {"name": "Name"}}],
977
- "should": [{"term": {"age": 42}}, {"term": {"age": 45}}]
978
- }}
979
- ```
980
-
981
- ```ruby
982
- UsersIndex.filter{ must(name == 'Name').should(age == 42, age == 45) }
983
- ```
984
-
985
- * And filter
986
-
987
- ```json
988
- {"and": [{"term": {"name": "Name"}}, {"range": {"age": {"lt": 42}}}]}
989
- ```
990
-
991
- ```ruby
992
- UsersIndex.filter{ (name == 'Name') & (age < 42) }
993
- ```
994
-
995
- * Or filter
861
+ 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.
996
862
 
997
- ```json
998
- {"or": [{"term": {"name": "Name"}}, {"range": {"age": {"lt": 42}}}]}
999
- ```
1000
-
1001
- ```ruby
1002
- UsersIndex.filter{ (name == 'Name') | (age < 42) }
1003
- ```
1004
-
1005
- ```json
1006
- {"not": {"term": {"name": "Name"}}}
1007
- {"not": {"range": {"age": {"lt": 42}}}}
1008
- ```
1009
-
1010
- ```ruby
1011
- UsersIndex.filter{ !(name == 'Name') } # or UsersIndex.filter{ name != 'Name' }
1012
- UsersIndex.filter{ !(age < 42) }
1013
- ```
1014
-
1015
- * Match all filter
1016
-
1017
- ```json
1018
- {"match_all": {}}
1019
- ```
1020
-
1021
- ```ruby
1022
- UsersIndex.filter{ match_all }
1023
- ```
1024
-
1025
- * Has child filter
1026
-
1027
- ```json
1028
- {"has_child": {"type": "blog_tag", "query": {"term": {"tag": "something"}}}
1029
- {"has_child": {"type": "comment", "filter": {"term": {"user": "john"}}}
1030
- ```
1031
-
1032
- ```ruby
1033
- UsersIndex.filter{ has_child(:blog_tag).query(term: {tag: 'something'}) }
1034
- UsersIndex.filter{ has_child(:comment).filter{ user == 'john' } }
1035
- ```
863
+ 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.
1036
864
 
1037
- * Has parent filter
1038
-
1039
- ```json
1040
- {"has_parent": {"type": "blog", "query": {"term": {"tag": "something"}}}}
1041
- {"has_parent": {"type": "blog", "filter": {"term": {"text": "bonsai three"}}}}
1042
- ```
1043
-
1044
- ```ruby
1045
- UsersIndex.filter{ has_parent(:blog).query(term: {tag: 'something'}) }
1046
- UsersIndex.filter{ has_parent(:blog).filter{ text == 'bonsai three' } }
1047
- ```
1048
-
1049
- See [filters.rb](lib/chewy/query/filters.rb) for more details.
1050
-
1051
- ### Faceting
1052
-
1053
- Facets are an optional sidechannel you can request from Elasticsearch describing certain fields of the resulting collection. The most common use for facets is to allow the user to continue filtering specifically within the subset, as opposed to the global index.
1054
-
1055
- For instance, let's request the `country` field as a facet along with our users collection. We can do this with the #facets method like so:
865
+ Every other request part is covered by a bunch of additional methods, see [Chewy::Search::Request](lib/chewy/search/request.rb) for details:
1056
866
 
1057
867
  ```ruby
1058
- UsersIndex.filter{ [...] }.facets({countries: {terms: {field: 'country'}}})
868
+ PlaceIndex.limit(10).offset(30).order(:name, {population: {order: :desc}})
1059
869
  ```
1060
870
 
1061
- Let's look at what we asked from Elasticsearch. The facets setter method accepts a hash. You can choose custom/semantic key names for this hash for your own convenience (in this case I used the plural version of the actual field), in our case `countries`. The following nested hash tells ES to grab and aggregate values (terms) from the `country` field on our indexed records.
871
+ Request DSL also provides additional scope actions, like `delete_all`, `exists?`, `count`, `pluck`, etc.
1062
872
 
1063
- The response will include the `:facets` sidechannel:
873
+ #### Pagination
1064
874
 
1065
- ```
1066
- < { ... ,"facets":{"countries":{"_type":"terms","missing":?,"total":?,"other":?,"terms":[{"term":"USA","count":?},{"term":"Brazil","count":?}, ...}}
1067
- ```
875
+ 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.
1068
876
 
1069
- ### Aggregations
877
+ #### Named scopes
1070
878
 
1071
- Aggregations are part of the optional sidechannel that can be requested with a query.
879
+ Chewy supports named scopes functionality. There is no specialized DSL for named scopes definition, it is simply about defining class methods.
1072
880
 
1073
- You interact with aggregations using the composable #aggregations method (or its alias #aggs)
881
+ See [Chewy::Search::Scoping](lib/chewy/search/scoping.rb) for details.
1074
882
 
1075
- Let's look at an example.
1076
-
1077
- ```ruby
1078
- class UsersIndex < Chewy::Index
1079
- define_type User do
1080
- field :name
1081
- field :rating
1082
- end
1083
- end
883
+ #### Scroll API
1084
884
 
1085
- all_johns = UsersIndex::User.filter { name == 'john' }.aggs({ avg_rating: { avg: { field: 'rating' } } })
885
+ ElasticSearch scroll API is utilized by a bunch of methods: `scroll_batches`, `scroll_hits`, `scroll_wrappers` and `scroll_objects`.
1086
886
 
1087
- avg_johns_rating = all_johns.aggs
1088
- # => {"avg_rating"=>{"value"=>3.5}}
1089
- ```
887
+ See [Chewy::Search::Scrolling](lib/chewy/search/scrolling.rb) for details.
1090
888
 
1091
- It is convenient to name aggregations that you intend to reuse regularly. This is achieve with the .aggregation method,
1092
- which is also available under the .agg alias method.
889
+ #### Loading objects
1093
890
 
1094
- Here's the same example from before
891
+ It is possible to load ORM/ODM source objects with the `objects` method. To provide additional loading options use `load` method:
1095
892
 
1096
893
  ```ruby
1097
- class UsersIndex < Chewy::Index
1098
- define_type User do
1099
- field :name
1100
- field :rating, type: "long"
1101
- agg :avg_rating do
1102
- { avg: { field: 'rating' } }
1103
- end
1104
- end
1105
- end
1106
-
1107
- all_johns = UsersIndex::User.filter { name == 'john' }.aggs(:avg_rating)
1108
-
1109
- avg_johns_rating = all_johns.aggs
1110
- # => {"avg_rating"=>{"value"=>3.5}}
894
+ PlacesIndex.load(scope: -> { active }).to_a # to_a returns `Chewy::Type` wrappers.
895
+ PlacesIndex.load(scope: -> { active }).objects # An array of AR source objects.
1111
896
  ```
1112
897
 
1113
- It is possible to run into collisions between named aggregations. This occurs when there is more than one aggregation
1114
- with the same name. To explicitly reference an aggregation you provide a string to the #aggs method of the form:
1115
- `index_name#document_type.aggregation_name`
898
+ See [Chewy::Search::Loader](lib/chewy/search/loader.rb) for more details.
1116
899
 
1117
- Consider this example where there are two separate aggregations named `avg_rating`
900
+ In case when it is necessary to iterate through both of the wrappers and objects simultaneously, `object_hash` method helps a lot:
1118
901
 
1119
902
  ```ruby
1120
- class UsersIndex < Chewy::Index
1121
- define_type User do
1122
- field :name
1123
- field :rating, type: "long"
1124
- agg :avg_rating do
1125
- { avg: { field: 'rating' } }
1126
- end
1127
- end
1128
- define_type Post do
1129
- field :title
1130
- field :body
1131
- field :comments do
1132
- field :message
1133
- field :rating, type: "long"
1134
- end
1135
- agg :avg_rating do
1136
- { avg: { field: 'comments.rating' } }
1137
- end
1138
- end
903
+ scope = PlacesIndex.load(scope: -> { active })
904
+ scope.each do |wrapper|
905
+ scope.object_hash[wrapper]
1139
906
  end
1140
-
1141
- all_docs = UsersIndex.filter {match_all}.aggs("users#user.avg_rating")
1142
- all_docs.aggs
1143
- # => {"users#user.avg_rating"=>{"value"=>3.5}}
1144
907
  ```
1145
908
 
1146
- ### Script fields
909
+ #### Legacy DSL incompatibilities
1147
910
 
1148
- Script fields allow you to execute Elasticsearch's scripting languages such as groovy and javascript. More about supported languages and what scripting is [here](https://www.elastic.co/guide/en/elasticsearch/reference/0.90/modules-scripting.html). This feature allows you to calculate the distance between geo points, for example. This is how to use the DSL:
911
+ * Filters advanced block DSL is not supported anymore, `elasticsearch-dsl` is used instead.
912
+ * 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.
913
+ * `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.
914
+ * 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`.
915
+ * `types!` method is no more, use `except(:types).types(...)`
916
+ * Named aggregations are not supported, use named scopes instead.
917
+ * A lot of query-level methods were not ported: everything that is related to boost and scoring. Use `query` manipulation to provide them.
918
+ * `Chewy::Type#_object` returns nil always. Use `Chewy::Search::Response#object_hash` instead.
1149
919
 
1150
- ```ruby
1151
- UsersIndex.script_fields(
1152
- distance: {
1153
- params: {
1154
- lat: 37.569976,
1155
- lon: -122.351591
1156
- },
1157
- script: "doc['coordinates'].distanceInMiles(lat, lon)"
1158
- }
1159
- )
1160
- ```
1161
- Here, `coordinates` is a field with type `geo_point`. There will be a `distance` field for the index's model in the search result.
920
+ ### Rake tasks
1162
921
 
1163
- ### Script scoring
922
+ For a Rails application, some index-maintaining rake tasks are defined.
1164
923
 
1165
- Script scoring is used to score the search results. All scores are added to the search request and combined according to boost mode and score mode. This can be useful if, for example, a score function is computationally expensive and it is sufficient to compute the score on a filtered set of documents. For example, you might want to multiply the score by another numeric field in the doc:
924
+ #### `chewy:reset`
1166
925
 
1167
- ```ruby
1168
- UsersIndex.script_score("_score * doc['my_numeric_field'].value")
926
+ Performs zero-downtime reindexing as described [here](https://www.elastic.co/blog/changing-mapping-with-zero-downtime). So the rake task creates a new index with unique suffix and then simply aliases it to the common index name. The previous index is deleted afterwards (see `Chewy::Index.reset!` for more details).
927
+
928
+ ```bash
929
+ rake chewy:reset # resets all the existing indices
930
+ rake chewy:reset[users] # resets UsersIndex only
931
+ rake chewy:reset[users,places] # resets UsersIndex and PlacesIndex
932
+ rake chewy:reset[-users,places] # resets every index in the application except specified ones
1169
933
  ```
1170
934
 
1171
- ### Boost Factor
935
+ #### `chewy:upgrade`
1172
936
 
1173
- Boost factors are a way to add a boost to a query where documents match the filter. If you have some users who are experts and some who are regular users, you might want to give the experts a higher score and boost to the top of the search results. You can accomplish this by using the #boost_factor method and adding a boost score of 5 for an expert user:
937
+ Performs reset exactly the same way as `chewy:reset` does, but only when the index specification (setting or mapping) was changed.
1174
938
 
1175
- ```ruby
1176
- UsersIndex.boost_factor(5, filter: {term: {type: 'Expert'}})
1177
- ```
939
+ It works only when index specification is locked in `Chewy::Stash` index. The first run will reset all indexes and lock their specifications.
1178
940
 
1179
- ### Objects loading
941
+ See [Chewy::Stash](lib/chewy/stash.rb) and [Chewy::Index::Specification](lib/chewy/index/specification.rb) for more details.
1180
942
 
1181
- It is possible to load source objects from the database for every search result:
1182
943
 
1183
- ```ruby
1184
- scope = UsersIndex.filter(range: {rating: {gte: 100}})
944
+ ```bash
945
+ rake chewy:upgrade # upgrades all the existing indices
946
+ rake chewy:upgrade[users] # upgrades UsersIndex only
947
+ rake chewy:upgrade[users,places] # upgrades UsersIndex and PlacesIndex
948
+ rake chewy:upgrade[-users,places] # upgrades every index in the application except specified ones
949
+ ```
1185
950
 
1186
- scope.load # => scope is marked to return User instances array
1187
- scope.load.query(...) # => since objects are loaded lazily you can complete scope
1188
- scope.load(user: { scope: ->{ includes(:country) }}) # you can also pass loading scopes for each
1189
- # possibly returned type
1190
- scope.load(user: { scope: User.includes(:country) }) # the second scope passing way.
1191
- scope.load(scope: ->{ includes(:country) }) # and more common scope applied to every loaded object type.
951
+ #### `chewy:update`
1192
952
 
1193
- scope.only(:id).load # it is optimal to request ids only if you are not planning to use type objects
1194
- ```
953
+ It doesn't create indexes, it simply imports everything to the existing ones and fails if the index was not created before.
1195
954
 
1196
- The `preload` method takes the same options as `load` and ORM/ODM objects will be loaded, but the scope will still return an array of Chewy wrappers. To access real objects use the `_object` wrapper method:
955
+ 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.
1197
956
 
1198
- ```ruby
1199
- UsersIndex.filter(range: {rating: {gte: 100}}).preload(...).query(...).map(&:_object)
957
+ ```bash
958
+ rake chewy:update # updates all the existing indices
959
+ rake chewy:update[users] # updates UsersIndex only
960
+ rake chewy:update[users,places#city] # updates the whole UsersIndex and PlacesIndex::City type
961
+ 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
1200
962
  ```
1201
963
 
1202
- See [loading.rb](lib/chewy/query/loading.rb) for more details.
964
+ #### `chewy:sync`
1203
965
 
1204
- ### `ActiveSupport::Notifications` support
1205
-
1206
- Chewy has notifying the following events:
966
+ Provides a way to synchronize outdated indexes with the source quickly and without doing a full reset.
1207
967
 
1208
- #### `search_query.chewy` payload
968
+ Arguments are similar to the ones taken by `chewy:update` task. It is possible to specify a particular type or a whole index.
1209
969
 
1210
- * `payload[:index]`: requested index class
1211
- * `payload[:request]`: request hash
970
+ See [Chewy::Type::Syncer](lib/chewy/type/syncer.rb) for more details.
1212
971
 
1213
- #### `import_objects.chewy` payload
972
+ ```bash
973
+ rake chewy:sync # synchronizes all the existing indices
974
+ rake chewy:sync[users] # synchronizes UsersIndex only
975
+ rake chewy:sync[users,places#city] # synchronizes the whole UsersIndex and PlacesIndex::City type
976
+ 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
977
+ ```
1214
978
 
1215
- * `payload[:type]`: currently imported type
1216
- * `payload[:import]`: imports stats, total imported and deleted objects count:
979
+ #### `chewy:deploy`
1217
980
 
1218
- ```ruby
1219
- {index: 30, delete: 5}
1220
- ```
981
+ 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.
1221
982
 
1222
- * `payload[:errors]`: might not exists. Contains grouped errors with objects ids list:
983
+ It is not possible to specify any particular types/indexes for this task as it doesn't make much sense.
1223
984
 
1224
- ```ruby
1225
- {index: {
1226
- 'error 1 text' => ['1', '2', '3'],
1227
- 'error 2 text' => ['4']
1228
- }, delete: {
1229
- 'delete error text' => ['10', '12']
1230
- }}
1231
- ```
985
+ 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.
1232
986
 
1233
- #### NewRelic integration
987
+ Also, there is always full reset alternative with `rake chewy:reset`.
1234
988
 
1235
- To integrate with NewRelic you may use the following example source (config/initializers/chewy.rb):
989
+ #### Parallelizing rake tasks
1236
990
 
1237
- ```ruby
1238
- ActiveSupport::Notifications.subscribe('import_objects.chewy') do |name, start, finish, id, payload|
1239
- metric_name = "Database/ElasticSearch/import"
1240
- duration = (finish - start).to_f
1241
- logged = "#{payload[:type]} #{payload[:import].to_a.map{ |i| i.join(':') }.join(', ')}"
991
+ Every task described above has its own parallel version. Every parallel rake task takes the number for processes for execution as the first argument and the rest of the arguments are exactly the same as for the non-parallel task version.
1242
992
 
1243
- self.class.trace_execution_scoped([metric_name]) do
1244
- NewRelic::Agent.instance.transaction_sampler.notice_sql(logged, nil, duration)
1245
- NewRelic::Agent.instance.sql_sampler.notice_sql(logged, metric_name, nil, duration)
1246
- NewRelic::Agent.record_metric(metric_name, duration)
1247
- end
1248
- end
993
+ [https://github.com/grosser/parallel](https://github.com/grosser/parallel) gem is required to use these tasks.
1249
994
 
1250
- ActiveSupport::Notifications.subscribe('search_query.chewy') do |name, start, finish, id, payload|
1251
- metric_name = "Database/ElasticSearch/search"
1252
- duration = (finish - start).to_f
1253
- logged = "#{payload[:type].presence || payload[:index]} #{payload[:request]}"
995
+ If the number of processes is not specified explicitly - `parallel` gem tries to automatically derive the number of processes to use.
1254
996
 
1255
- self.class.trace_execution_scoped([metric_name]) do
1256
- NewRelic::Agent.instance.transaction_sampler.notice_sql(logged, nil, duration)
1257
- NewRelic::Agent.instance.sql_sampler.notice_sql(logged, metric_name, nil, duration)
1258
- NewRelic::Agent.record_metric(metric_name, duration)
1259
- end
1260
- end
997
+ ```bash
998
+ rake chewy:parallel:reset
999
+ rake chewy:parallel:upgrade[4]
1000
+ rake chewy:parallel:update[4,places#city]
1001
+ rake chewy:parallel:sync[4,-users]
1002
+ rake chewy:parallel:deploy[4] # performs parallel upgrade and parallel sync afterwards
1261
1003
  ```
1262
1004
 
1263
- ### Rake tasks
1005
+ #### `chewy:journal`
1264
1006
 
1265
- Inside the Rails application, some index-maintaining rake tasks are defined.
1007
+ 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.
1266
1008
 
1267
1009
  ```bash
1268
- rake chewy:reset # resets all the existing indices, declared in app/chewy
1269
- rake chewy:reset[users] # resets UsersIndex only
1270
- rake chewy:reset[users,projects] # resets UsersIndex and ProjectsIndex
1271
- rake chewy:reset[-users,projects] # resets every index in application except specified ones
1272
-
1273
- rake chewy:update # updates all the existing indices, declared in app/chewy
1274
- rake chewy:update[users] # updates UsersIndex only
1275
- rake chewy:update[users,projects] # updates UsersIndex and ProjectsIndex
1276
- rake chewy:update[-users,projects] # updates every index in application except specified ones
1277
-
1010
+ rake chewy:journal:apply["$(date -v-1H -u +%FT%TZ)"] # apply journaled changes for the past hour
1011
+ rake chewy:journal:apply["$(date -v-1H -u +%FT%TZ)",users] # apply journaled changes for the past hour on UsersIndex only
1278
1012
  ```
1279
1013
 
1280
- `rake chewy:reset` performs zero-downtime reindexing as described [here](https://www.elastic.co/blog/changing-mapping-with-zero-downtime). So basically rake task creates a new index with uniq suffix and then simply aliases it to the common index name. The previous index is deleted afterwards (see `Chewy::Index.reset!` for more details).
1281
-
1282
-
1283
1014
  ### Rspec integration
1284
1015
 
1285
1016
  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.
@@ -1288,9 +1019,13 @@ Just add `require 'chewy/rspec'` to your spec_helper.rb and you will get additio
1288
1019
 
1289
1020
  Add `require 'chewy/minitest'` to your test_helper.rb, and then for tests which you'd like indexing test hooks, `include Chewy::Minitest::Helpers`.
1290
1021
 
1022
+ Since you can set `:bypass` strategy for test suites and manually handle import for the index and manually flush test indices using `Chewy.massacre`. This will help reduce unnecessary ES requests
1023
+
1024
+ 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.
1025
+
1291
1026
  ### DatabaseCleaner
1292
1027
 
1293
- 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` indexes 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:
1028
+ 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:
1294
1029
 
1295
1030
  ```ruby
1296
1031
  #config/initializers/chewy.rb
@@ -1300,7 +1035,6 @@ Chewy.use_after_commit_callbacks = !Rails.env.test?
1300
1035
  ## TODO a.k.a coming soon:
1301
1036
 
1302
1037
  * Typecasting support
1303
- * Advanced (simplified) query DSL: `UsersIndex.query { email == 'my@gmail.com' }` will produce term query
1304
1038
  * update_all support
1305
1039
  * Maybe, closer ORM/ODM integration, creating index classes implicitly
1306
1040