chewy 0.9.0 → 0.10.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.rubocop.yml +24 -2
- data/.rubocop_todo.yml +2 -2
- data/.travis.yml +38 -21
- data/.yardopts +5 -0
- data/Appraisals +55 -27
- data/CHANGELOG.md +57 -12
- data/Gemfile +14 -10
- data/LEGACY_DSL.md +497 -0
- data/README.md +249 -515
- data/chewy.gemspec +5 -4
- data/gemfiles/rails.4.0.activerecord.gemfile +14 -0
- data/gemfiles/rails.4.1.activerecord.gemfile +14 -0
- data/gemfiles/rails.4.2.activerecord.gemfile +8 -10
- data/gemfiles/rails.4.2.mongoid.5.1.gemfile +9 -10
- data/gemfiles/rails.5.0.activerecord.gemfile +8 -10
- data/gemfiles/rails.5.0.mongoid.6.0.gemfile +15 -0
- data/gemfiles/rails.5.1.activerecord.gemfile +15 -0
- data/gemfiles/rails.5.1.mongoid.6.1.gemfile +15 -0
- data/gemfiles/sequel.4.45.gemfile +11 -0
- data/lib/chewy.rb +77 -43
- data/lib/chewy/config.rb +44 -7
- data/lib/chewy/errors.rb +2 -2
- data/lib/chewy/fields/base.rb +39 -32
- data/lib/chewy/fields/root.rb +33 -7
- data/lib/chewy/index.rb +237 -149
- data/lib/chewy/index/actions.rb +85 -28
- data/lib/chewy/index/aliases.rb +2 -1
- data/lib/chewy/index/settings.rb +9 -5
- data/lib/chewy/index/specification.rb +58 -0
- data/lib/chewy/journal.rb +40 -92
- data/lib/chewy/query.rb +43 -27
- data/lib/chewy/query/compose.rb +13 -13
- data/lib/chewy/query/criteria.rb +13 -13
- data/lib/chewy/query/filters.rb +1 -1
- data/lib/chewy/query/loading.rb +1 -1
- data/lib/chewy/query/nodes/and.rb +2 -2
- data/lib/chewy/query/nodes/bool.rb +1 -1
- data/lib/chewy/query/nodes/equal.rb +2 -2
- data/lib/chewy/query/nodes/exists.rb +1 -1
- data/lib/chewy/query/nodes/has_relation.rb +2 -2
- data/lib/chewy/query/nodes/match_all.rb +1 -1
- data/lib/chewy/query/nodes/missing.rb +1 -1
- data/lib/chewy/query/nodes/not.rb +2 -2
- data/lib/chewy/query/nodes/or.rb +2 -2
- data/lib/chewy/query/nodes/prefix.rb +1 -1
- data/lib/chewy/query/nodes/query.rb +2 -2
- data/lib/chewy/query/nodes/range.rb +4 -4
- data/lib/chewy/query/nodes/regexp.rb +4 -4
- data/lib/chewy/query/nodes/script.rb +3 -3
- data/lib/chewy/query/pagination.rb +10 -1
- data/lib/chewy/railtie.rb +1 -0
- data/lib/chewy/rake_helper.rb +265 -48
- data/lib/chewy/rspec/update_index.rb +30 -22
- data/lib/chewy/search.rb +78 -21
- data/lib/chewy/search/loader.rb +83 -0
- data/lib/chewy/{query → search}/pagination/kaminari.rb +13 -5
- data/lib/chewy/search/pagination/will_paginate.rb +41 -0
- data/lib/chewy/search/parameters.rb +150 -0
- data/lib/chewy/search/parameters/aggs.rb +16 -0
- data/lib/chewy/search/parameters/concerns/bool_storage.rb +24 -0
- data/lib/chewy/search/parameters/concerns/hash_storage.rb +23 -0
- data/lib/chewy/search/parameters/concerns/integer_storage.rb +14 -0
- data/lib/chewy/search/parameters/concerns/query_storage.rb +237 -0
- data/lib/chewy/search/parameters/concerns/string_array_storage.rb +23 -0
- data/lib/chewy/search/parameters/concerns/string_storage.rb +14 -0
- data/lib/chewy/search/parameters/docvalue_fields.rb +12 -0
- data/lib/chewy/search/parameters/explain.rb +16 -0
- data/lib/chewy/search/parameters/filter.rb +47 -0
- data/lib/chewy/search/parameters/highlight.rb +16 -0
- data/lib/chewy/search/parameters/indices_boost.rb +52 -0
- data/lib/chewy/search/parameters/limit.rb +17 -0
- data/lib/chewy/search/parameters/load.rb +32 -0
- data/lib/chewy/search/parameters/min_score.rb +16 -0
- data/lib/chewy/search/parameters/none.rb +27 -0
- data/lib/chewy/search/parameters/offset.rb +17 -0
- data/lib/chewy/search/parameters/order.rb +64 -0
- data/lib/chewy/search/parameters/post_filter.rb +19 -0
- data/lib/chewy/search/parameters/preference.rb +16 -0
- data/lib/chewy/search/parameters/profile.rb +16 -0
- data/lib/chewy/search/parameters/query.rb +19 -0
- data/lib/chewy/search/parameters/request_cache.rb +27 -0
- data/lib/chewy/search/parameters/rescore.rb +29 -0
- data/lib/chewy/search/parameters/script_fields.rb +16 -0
- data/lib/chewy/search/parameters/search_after.rb +20 -0
- data/lib/chewy/search/parameters/search_type.rb +16 -0
- data/lib/chewy/search/parameters/source.rb +73 -0
- data/lib/chewy/search/parameters/storage.rb +95 -0
- data/lib/chewy/search/parameters/stored_fields.rb +63 -0
- data/lib/chewy/search/parameters/suggest.rb +16 -0
- data/lib/chewy/search/parameters/terminate_after.rb +16 -0
- data/lib/chewy/search/parameters/timeout.rb +16 -0
- data/lib/chewy/search/parameters/track_scores.rb +16 -0
- data/lib/chewy/search/parameters/types.rb +20 -0
- data/lib/chewy/search/parameters/version.rb +16 -0
- data/lib/chewy/search/query_proxy.rb +257 -0
- data/lib/chewy/search/request.rb +1021 -0
- data/lib/chewy/search/response.rb +119 -0
- data/lib/chewy/search/scoping.rb +50 -0
- data/lib/chewy/search/scrolling.rb +136 -0
- data/lib/chewy/stash.rb +70 -0
- data/lib/chewy/strategy.rb +10 -3
- data/lib/chewy/strategy/active_job.rb +1 -0
- data/lib/chewy/strategy/atomic.rb +1 -3
- data/lib/chewy/strategy/bypass.rb +1 -1
- data/lib/chewy/strategy/resque.rb +1 -0
- data/lib/chewy/strategy/shoryuken.rb +40 -0
- data/lib/chewy/strategy/sidekiq.rb +13 -3
- data/lib/chewy/type.rb +29 -7
- data/lib/chewy/type/actions.rb +26 -2
- data/lib/chewy/type/adapter/active_record.rb +44 -29
- data/lib/chewy/type/adapter/base.rb +27 -7
- data/lib/chewy/type/adapter/mongoid.rb +18 -7
- data/lib/chewy/type/adapter/object.rb +187 -26
- data/lib/chewy/type/adapter/orm.rb +59 -32
- data/lib/chewy/type/adapter/sequel.rb +32 -16
- data/lib/chewy/type/import.rb +145 -191
- data/lib/chewy/type/import/bulk_builder.rb +122 -0
- data/lib/chewy/type/import/bulk_request.rb +76 -0
- data/lib/chewy/type/import/journal_builder.rb +45 -0
- data/lib/chewy/type/import/routine.rb +138 -0
- data/lib/chewy/type/mapping.rb +11 -1
- data/lib/chewy/type/observe.rb +1 -1
- data/lib/chewy/type/syncer.rb +220 -0
- data/lib/chewy/type/witchcraft.rb +27 -13
- data/lib/chewy/type/wrapper.rb +28 -2
- data/lib/chewy/version.rb +1 -1
- data/lib/tasks/chewy.rake +84 -26
- data/spec/chewy/config_spec.rb +82 -1
- data/spec/chewy/fields/base_spec.rb +147 -112
- data/spec/chewy/fields/root_spec.rb +75 -18
- data/spec/chewy/fields/time_fields_spec.rb +2 -3
- data/spec/chewy/index/actions_spec.rb +180 -50
- data/spec/chewy/index/aliases_spec.rb +2 -2
- data/spec/chewy/index/settings_spec.rb +67 -38
- data/spec/chewy/index/specification_spec.rb +160 -0
- data/spec/chewy/index_spec.rb +57 -66
- data/spec/chewy/journal_spec.rb +149 -54
- data/spec/chewy/minitest/helpers_spec.rb +4 -4
- data/spec/chewy/minitest/search_index_receiver_spec.rb +1 -1
- data/spec/chewy/query/criteria_spec.rb +179 -179
- data/spec/chewy/query/filters_spec.rb +15 -15
- data/spec/chewy/query/loading_spec.rb +22 -20
- data/spec/chewy/query/nodes/and_spec.rb +2 -2
- data/spec/chewy/query/nodes/bool_spec.rb +4 -4
- data/spec/chewy/query/nodes/equal_spec.rb +19 -19
- data/spec/chewy/query/nodes/exists_spec.rb +6 -6
- data/spec/chewy/query/nodes/has_child_spec.rb +19 -19
- data/spec/chewy/query/nodes/has_parent_spec.rb +19 -19
- data/spec/chewy/query/nodes/missing_spec.rb +5 -5
- data/spec/chewy/query/nodes/not_spec.rb +3 -2
- data/spec/chewy/query/nodes/or_spec.rb +2 -2
- data/spec/chewy/query/nodes/prefix_spec.rb +5 -5
- data/spec/chewy/query/nodes/query_spec.rb +2 -2
- data/spec/chewy/query/nodes/range_spec.rb +18 -18
- data/spec/chewy/query/nodes/raw_spec.rb +1 -1
- data/spec/chewy/query/nodes/regexp_spec.rb +14 -14
- data/spec/chewy/query/nodes/script_spec.rb +4 -4
- data/spec/chewy/query/pagination/kaminari_spec.rb +3 -55
- data/spec/chewy/query/pagination/will_paginate_spec.rb +5 -0
- data/spec/chewy/query/pagination_spec.rb +25 -21
- data/spec/chewy/query_spec.rb +501 -560
- data/spec/chewy/rake_helper_spec.rb +368 -0
- data/spec/chewy/repository_spec.rb +4 -4
- data/spec/chewy/rspec/update_index_spec.rb +89 -56
- data/spec/chewy/runtime_spec.rb +2 -2
- data/spec/chewy/search/loader_spec.rb +117 -0
- data/spec/chewy/search/pagination/kaminari_examples.rb +71 -0
- data/spec/chewy/search/pagination/kaminari_spec.rb +17 -0
- data/spec/chewy/search/pagination/will_paginate_examples.rb +63 -0
- data/spec/chewy/search/pagination/will_paginate_spec.rb +17 -0
- data/spec/chewy/search/parameters/aggs_spec.rb +5 -0
- data/spec/chewy/search/parameters/bool_storage_examples.rb +53 -0
- data/spec/chewy/search/parameters/docvalue_fields_spec.rb +5 -0
- data/spec/chewy/search/parameters/explain_spec.rb +5 -0
- data/spec/chewy/search/parameters/filter_spec.rb +5 -0
- data/spec/chewy/search/parameters/hash_storage_examples.rb +59 -0
- data/spec/chewy/search/parameters/highlight_spec.rb +5 -0
- data/spec/chewy/search/parameters/indices_boost_spec.rb +83 -0
- data/spec/chewy/search/parameters/integer_storage_examples.rb +32 -0
- data/spec/chewy/search/parameters/limit_spec.rb +5 -0
- data/spec/chewy/search/parameters/load_spec.rb +60 -0
- data/spec/chewy/search/parameters/min_score_spec.rb +32 -0
- data/spec/chewy/search/parameters/none_spec.rb +5 -0
- data/spec/chewy/search/parameters/offset_spec.rb +5 -0
- data/spec/chewy/search/parameters/order_spec.rb +65 -0
- data/spec/chewy/search/parameters/post_filter_spec.rb +5 -0
- data/spec/chewy/search/parameters/preference_spec.rb +5 -0
- data/spec/chewy/search/parameters/profile_spec.rb +5 -0
- data/spec/chewy/search/parameters/query_spec.rb +5 -0
- data/spec/chewy/search/parameters/query_storage_examples.rb +388 -0
- data/spec/chewy/search/parameters/request_cache_spec.rb +67 -0
- data/spec/chewy/search/parameters/rescore_spec.rb +62 -0
- data/spec/chewy/search/parameters/script_fields_spec.rb +5 -0
- data/spec/chewy/search/parameters/search_after_spec.rb +32 -0
- data/spec/chewy/search/parameters/search_type_spec.rb +5 -0
- data/spec/chewy/search/parameters/source_spec.rb +156 -0
- data/spec/chewy/search/parameters/storage_spec.rb +60 -0
- data/spec/chewy/search/parameters/stored_fields_spec.rb +126 -0
- data/spec/chewy/search/parameters/string_array_storage_examples.rb +63 -0
- data/spec/chewy/search/parameters/string_storage_examples.rb +32 -0
- data/spec/chewy/search/parameters/suggest_spec.rb +5 -0
- data/spec/chewy/search/parameters/terminate_after_spec.rb +5 -0
- data/spec/chewy/search/parameters/timeout_spec.rb +5 -0
- data/spec/chewy/search/parameters/track_scores_spec.rb +5 -0
- data/spec/chewy/search/parameters/types_spec.rb +5 -0
- data/spec/chewy/search/parameters/version_spec.rb +5 -0
- data/spec/chewy/search/parameters_spec.rb +130 -0
- data/spec/chewy/search/query_proxy_spec.rb +68 -0
- data/spec/chewy/search/request_spec.rb +669 -0
- data/spec/chewy/search/response_spec.rb +192 -0
- data/spec/chewy/search/scrolling_spec.rb +169 -0
- data/spec/chewy/search_spec.rb +13 -6
- data/spec/chewy/stash_spec.rb +95 -0
- data/spec/chewy/strategy/active_job_spec.rb +6 -0
- data/spec/chewy/strategy/resque_spec.rb +6 -0
- data/spec/chewy/strategy/shoryuken_spec.rb +64 -0
- data/spec/chewy/strategy/sidekiq_spec.rb +8 -0
- data/spec/chewy/strategy_spec.rb +6 -6
- data/spec/chewy/type/actions_spec.rb +29 -10
- data/spec/chewy/type/adapter/active_record_spec.rb +203 -91
- data/spec/chewy/type/adapter/mongoid_spec.rb +112 -54
- data/spec/chewy/type/adapter/object_spec.rb +101 -28
- data/spec/chewy/type/adapter/sequel_spec.rb +149 -82
- data/spec/chewy/type/import/bulk_builder_spec.rb +279 -0
- data/spec/chewy/type/import/bulk_request_spec.rb +102 -0
- data/spec/chewy/type/import/journal_builder_spec.rb +95 -0
- data/spec/chewy/type/import/routine_spec.rb +110 -0
- data/spec/chewy/type/import_spec.rb +350 -271
- data/spec/chewy/type/mapping_spec.rb +54 -18
- data/spec/chewy/type/observe_spec.rb +5 -1
- data/spec/chewy/type/syncer_spec.rb +123 -0
- data/spec/chewy/type/witchcraft_spec.rb +45 -29
- data/spec/chewy/type/wrapper_spec.rb +63 -23
- data/spec/chewy/type_spec.rb +28 -7
- data/spec/chewy_spec.rb +75 -7
- data/spec/spec_helper.rb +5 -2
- data/spec/support/active_record.rb +5 -1
- data/spec/support/class_helpers.rb +0 -14
- data/spec/support/mongoid.rb +15 -3
- data/spec/support/sequel.rb +6 -1
- metadata +198 -37
- data/gemfiles/rails.3.2.activerecord.gemfile +0 -16
- data/gemfiles/rails.3.2.activerecord.kaminari.gemfile +0 -15
- data/gemfiles/rails.3.2.activerecord.will_paginate.gemfile +0 -15
- data/gemfiles/rails.4.2.activerecord.kaminari.gemfile +0 -16
- data/gemfiles/rails.4.2.activerecord.will_paginate.gemfile +0 -16
- data/gemfiles/rails.4.2.mongoid.4.0.gemfile +0 -16
- data/gemfiles/rails.4.2.mongoid.4.0.kaminari.gemfile +0 -15
- data/gemfiles/rails.4.2.mongoid.4.0.will_paginate.gemfile +0 -15
- data/gemfiles/rails.4.2.mongoid.5.1.kaminari.gemfile +0 -15
- data/gemfiles/rails.4.2.mongoid.5.1.will_paginate.gemfile +0 -15
- data/gemfiles/rails.5.0.activerecord.kaminari.gemfile +0 -16
- data/gemfiles/rails.5.0.activerecord.will_paginate.gemfile +0 -16
- data/gemfiles/sequel.4.38.gemfile +0 -14
- data/lib/chewy/journal/apply.rb +0 -31
- data/lib/chewy/journal/clean.rb +0 -24
- data/lib/chewy/journal/entry.rb +0 -83
- data/lib/chewy/journal/query.rb +0 -87
- data/lib/chewy/query/pagination/will_paginate.rb +0 -27
- data/lib/chewy/query/scoping.rb +0 -20
- data/spec/chewy/journal/apply_spec.rb +0 -120
- data/spec/chewy/journal/entry_spec.rb +0 -237
- 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?]
|
16
|
-
* [
|
17
|
-
|
18
|
-
* [
|
19
|
-
|
20
|
-
* [
|
21
|
-
* [
|
22
|
-
* [
|
23
|
-
* [
|
24
|
-
* [
|
25
|
-
* [
|
26
|
-
* [
|
27
|
-
* [
|
28
|
-
* [Index
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
* [
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
* [
|
37
|
-
* [
|
38
|
-
* [
|
39
|
-
* [
|
40
|
-
|
41
|
-
|
42
|
-
* [
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
* [
|
47
|
-
* [
|
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
|
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
|
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
|
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
|
489
|
-
If you make something with a batch of
|
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:
|
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
|
-
|
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
|
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
|
-
###
|
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
|
-
|
778
|
+
Chewy has notifying the following events:
|
740
779
|
|
741
|
-
|
742
|
-
UsersIndex::User.filter(term: {name: 'foo'}) # will return UserIndex::User collection only
|
743
|
-
```
|
780
|
+
#### `search_query.chewy` payload
|
744
781
|
|
745
|
-
|
746
|
-
`
|
782
|
+
* `payload[:index]`: requested index class
|
783
|
+
* `payload[:request]`: request hash
|
747
784
|
|
748
|
-
|
785
|
+
#### `import_objects.chewy` payload
|
749
786
|
|
750
|
-
|
787
|
+
* `payload[:type]`: currently imported type
|
788
|
+
* `payload[:import]`: imports stats, total imported and deleted objects count:
|
751
789
|
|
752
|
-
|
790
|
+
```ruby
|
791
|
+
{index: 30, delete: 5}
|
792
|
+
```
|
753
793
|
|
754
|
-
|
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
|
-
|
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
|
-
###
|
805
|
+
### NewRelic integration
|
763
806
|
|
764
|
-
|
807
|
+
To integrate with NewRelic you may use the following example source (config/initializers/chewy.rb):
|
765
808
|
|
766
809
|
```ruby
|
767
|
-
|
768
|
-
|
769
|
-
|
770
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
778
|
-
|
779
|
-
|
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
|
-
|
835
|
+
### Search requests
|
783
836
|
|
784
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
804
|
-
|
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
|
-
|
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
|
-
|
815
|
-
|
816
|
-
|
817
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
868
|
+
PlaceIndex.limit(10).offset(30).order(:name, {population: {order: :desc}})
|
1059
869
|
```
|
1060
870
|
|
1061
|
-
|
871
|
+
Request DSL also provides additional scope actions, like `delete_all`, `exists?`, `count`, `pluck`, etc.
|
1062
872
|
|
1063
|
-
|
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
|
-
|
877
|
+
#### Named scopes
|
1070
878
|
|
1071
|
-
|
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
|
-
|
881
|
+
See [Chewy::Search::Scoping](lib/chewy/search/scoping.rb) for details.
|
1074
882
|
|
1075
|
-
|
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
|
-
|
885
|
+
ElasticSearch scroll API is utilized by a bunch of methods: `scroll_batches`, `scroll_hits`, `scroll_wrappers` and `scroll_objects`.
|
1086
886
|
|
1087
|
-
|
1088
|
-
# => {"avg_rating"=>{"value"=>3.5}}
|
1089
|
-
```
|
887
|
+
See [Chewy::Search::Scrolling](lib/chewy/search/scrolling.rb) for details.
|
1090
888
|
|
1091
|
-
|
1092
|
-
which is also available under the .agg alias method.
|
889
|
+
#### Loading objects
|
1093
890
|
|
1094
|
-
|
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
|
-
|
1098
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
1121
|
-
|
1122
|
-
|
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
|
-
|
909
|
+
#### Legacy DSL incompatibilities
|
1147
910
|
|
1148
|
-
|
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
|
-
|
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
|
-
|
922
|
+
For a Rails application, some index-maintaining rake tasks are defined.
|
1164
923
|
|
1165
|
-
|
924
|
+
#### `chewy:reset`
|
1166
925
|
|
1167
|
-
|
1168
|
-
|
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
|
-
|
935
|
+
#### `chewy:upgrade`
|
1172
936
|
|
1173
|
-
|
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
|
-
|
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
|
-
|
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
|
-
```
|
1184
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
```
|
1199
|
-
|
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
|
-
|
964
|
+
#### `chewy:sync`
|
1203
965
|
|
1204
|
-
|
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
|
-
|
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
|
-
|
1211
|
-
* `payload[:request]`: request hash
|
970
|
+
See [Chewy::Type::Syncer](lib/chewy/type/syncer.rb) for more details.
|
1212
971
|
|
1213
|
-
|
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
|
-
|
1216
|
-
* `payload[:import]`: imports stats, total imported and deleted objects count:
|
979
|
+
#### `chewy:deploy`
|
1217
980
|
|
1218
|
-
|
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
|
-
|
983
|
+
It is not possible to specify any particular types/indexes for this task as it doesn't make much sense.
|
1223
984
|
|
1224
|
-
|
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
|
-
|
987
|
+
Also, there is always full reset alternative with `rake chewy:reset`.
|
1234
988
|
|
1235
|
-
|
989
|
+
#### Parallelizing rake tasks
|
1236
990
|
|
1237
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
1256
|
-
|
1257
|
-
|
1258
|
-
|
1259
|
-
|
1260
|
-
|
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
|
-
|
1005
|
+
#### `chewy:journal`
|
1264
1006
|
|
1265
|
-
|
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:
|
1269
|
-
rake chewy:
|
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`
|
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
|
|