chewy 6.0.0 → 7.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (188) hide show
  1. checksums.yaml +4 -4
  2. data/.github/CODEOWNERS +1 -0
  3. data/.github/ISSUE_TEMPLATE/bug_report.md +39 -0
  4. data/.github/ISSUE_TEMPLATE/feature_request.md +20 -0
  5. data/.github/PULL_REQUEST_TEMPLATE.md +16 -0
  6. data/.github/dependabot.yml +42 -0
  7. data/.github/workflows/ruby.yml +60 -0
  8. data/.rubocop.yml +16 -8
  9. data/.rubocop_todo.yml +110 -22
  10. data/CHANGELOG.md +396 -105
  11. data/CODE_OF_CONDUCT.md +14 -0
  12. data/CONTRIBUTING.md +63 -0
  13. data/Gemfile +4 -10
  14. data/Guardfile +3 -1
  15. data/README.md +497 -275
  16. data/chewy.gemspec +5 -20
  17. data/gemfiles/base.gemfile +12 -0
  18. data/gemfiles/rails.6.1.activerecord.gemfile +10 -15
  19. data/gemfiles/rails.7.0.activerecord.gemfile +14 -0
  20. data/gemfiles/rails.7.1.activerecord.gemfile +14 -0
  21. data/lib/chewy/config.rb +60 -52
  22. data/lib/chewy/elastic_client.rb +31 -0
  23. data/lib/chewy/errors.rb +7 -10
  24. data/lib/chewy/fields/base.rb +79 -13
  25. data/lib/chewy/fields/root.rb +4 -14
  26. data/lib/chewy/index/actions.rb +54 -37
  27. data/lib/chewy/{type → index}/adapter/active_record.rb +30 -6
  28. data/lib/chewy/{type → index}/adapter/base.rb +2 -3
  29. data/lib/chewy/{type → index}/adapter/object.rb +27 -31
  30. data/lib/chewy/{type → index}/adapter/orm.rb +17 -18
  31. data/lib/chewy/index/aliases.rb +14 -5
  32. data/lib/chewy/index/crutch.rb +40 -0
  33. data/lib/chewy/index/import/bulk_builder.rb +311 -0
  34. data/lib/chewy/{type → index}/import/bulk_request.rb +6 -7
  35. data/lib/chewy/{type → index}/import/journal_builder.rb +11 -12
  36. data/lib/chewy/{type → index}/import/routine.rb +18 -17
  37. data/lib/chewy/{type → index}/import.rb +76 -32
  38. data/lib/chewy/{type → index}/mapping.rb +29 -34
  39. data/lib/chewy/index/observe/active_record_methods.rb +87 -0
  40. data/lib/chewy/index/observe/callback.rb +34 -0
  41. data/lib/chewy/index/observe.rb +17 -0
  42. data/lib/chewy/index/specification.rb +1 -0
  43. data/lib/chewy/{type → index}/syncer.rb +59 -59
  44. data/lib/chewy/{type → index}/witchcraft.rb +11 -7
  45. data/lib/chewy/{type → index}/wrapper.rb +2 -2
  46. data/lib/chewy/index.rb +67 -94
  47. data/lib/chewy/journal.rb +25 -14
  48. data/lib/chewy/log_subscriber.rb +5 -1
  49. data/lib/chewy/minitest/helpers.rb +86 -13
  50. data/lib/chewy/minitest/search_index_receiver.rb +24 -26
  51. data/lib/chewy/railtie.rb +6 -20
  52. data/lib/chewy/rake_helper.rb +169 -113
  53. data/lib/chewy/rspec/build_query.rb +12 -0
  54. data/lib/chewy/rspec/helpers.rb +55 -0
  55. data/lib/chewy/rspec/update_index.rb +55 -44
  56. data/lib/chewy/rspec.rb +2 -0
  57. data/lib/chewy/runtime/version.rb +1 -1
  58. data/lib/chewy/runtime.rb +1 -1
  59. data/lib/chewy/search/loader.rb +19 -41
  60. data/lib/chewy/search/parameters/collapse.rb +16 -0
  61. data/lib/chewy/search/parameters/concerns/query_storage.rb +2 -2
  62. data/lib/chewy/search/parameters/ignore_unavailable.rb +27 -0
  63. data/lib/chewy/search/parameters/indices.rb +13 -58
  64. data/lib/chewy/search/parameters/knn.rb +16 -0
  65. data/lib/chewy/search/parameters/order.rb +6 -19
  66. data/lib/chewy/search/parameters/source.rb +5 -1
  67. data/lib/chewy/search/parameters/storage.rb +1 -1
  68. data/lib/chewy/search/parameters/track_total_hits.rb +16 -0
  69. data/lib/chewy/search/parameters.rb +6 -4
  70. data/lib/chewy/search/query_proxy.rb +9 -2
  71. data/lib/chewy/search/request.rb +169 -134
  72. data/lib/chewy/search/response.rb +5 -5
  73. data/lib/chewy/search/scoping.rb +7 -8
  74. data/lib/chewy/search/scrolling.rb +13 -13
  75. data/lib/chewy/search.rb +9 -19
  76. data/lib/chewy/stash.rb +19 -30
  77. data/lib/chewy/strategy/active_job.rb +1 -1
  78. data/lib/chewy/strategy/atomic_no_refresh.rb +18 -0
  79. data/lib/chewy/strategy/base.rb +10 -0
  80. data/lib/chewy/strategy/delayed_sidekiq/scheduler.rb +168 -0
  81. data/lib/chewy/strategy/delayed_sidekiq/worker.rb +76 -0
  82. data/lib/chewy/strategy/delayed_sidekiq.rb +30 -0
  83. data/lib/chewy/strategy/lazy_sidekiq.rb +64 -0
  84. data/lib/chewy/strategy/sidekiq.rb +2 -1
  85. data/lib/chewy/strategy.rb +6 -19
  86. data/lib/chewy/version.rb +1 -1
  87. data/lib/chewy.rb +39 -86
  88. data/lib/generators/chewy/install_generator.rb +1 -1
  89. data/lib/tasks/chewy.rake +36 -32
  90. data/migration_guide.md +46 -8
  91. data/spec/chewy/config_spec.rb +16 -41
  92. data/spec/chewy/elastic_client_spec.rb +26 -0
  93. data/spec/chewy/fields/base_spec.rb +432 -147
  94. data/spec/chewy/fields/root_spec.rb +20 -28
  95. data/spec/chewy/fields/time_fields_spec.rb +5 -5
  96. data/spec/chewy/index/actions_spec.rb +368 -59
  97. data/spec/chewy/{type → index}/adapter/active_record_spec.rb +156 -40
  98. data/spec/chewy/{type → index}/adapter/object_spec.rb +21 -6
  99. data/spec/chewy/index/aliases_spec.rb +3 -3
  100. data/spec/chewy/index/import/bulk_builder_spec.rb +494 -0
  101. data/spec/chewy/{type → index}/import/bulk_request_spec.rb +5 -12
  102. data/spec/chewy/{type → index}/import/journal_builder_spec.rb +9 -19
  103. data/spec/chewy/{type → index}/import/routine_spec.rb +19 -19
  104. data/spec/chewy/{type → index}/import_spec.rb +164 -98
  105. data/spec/chewy/index/mapping_spec.rb +135 -0
  106. data/spec/chewy/index/observe/active_record_methods_spec.rb +68 -0
  107. data/spec/chewy/index/observe/callback_spec.rb +139 -0
  108. data/spec/chewy/index/observe_spec.rb +143 -0
  109. data/spec/chewy/index/settings_spec.rb +3 -1
  110. data/spec/chewy/index/specification_spec.rb +20 -30
  111. data/spec/chewy/{type → index}/syncer_spec.rb +14 -19
  112. data/spec/chewy/{type → index}/witchcraft_spec.rb +20 -22
  113. data/spec/chewy/index/wrapper_spec.rb +100 -0
  114. data/spec/chewy/index_spec.rb +60 -105
  115. data/spec/chewy/journal_spec.rb +25 -74
  116. data/spec/chewy/minitest/helpers_spec.rb +123 -15
  117. data/spec/chewy/minitest/search_index_receiver_spec.rb +28 -30
  118. data/spec/chewy/multi_search_spec.rb +4 -5
  119. data/spec/chewy/rake_helper_spec.rb +315 -55
  120. data/spec/chewy/rspec/build_query_spec.rb +34 -0
  121. data/spec/chewy/rspec/helpers_spec.rb +61 -0
  122. data/spec/chewy/rspec/update_index_spec.rb +74 -71
  123. data/spec/chewy/runtime_spec.rb +2 -2
  124. data/spec/chewy/search/loader_spec.rb +19 -53
  125. data/spec/chewy/search/pagination/kaminari_examples.rb +4 -6
  126. data/spec/chewy/search/pagination/kaminari_spec.rb +2 -2
  127. data/spec/chewy/search/parameters/collapse_spec.rb +5 -0
  128. data/spec/chewy/search/parameters/ignore_unavailable_spec.rb +67 -0
  129. data/spec/chewy/search/parameters/indices_spec.rb +26 -117
  130. data/spec/chewy/search/parameters/knn_spec.rb +5 -0
  131. data/spec/chewy/search/parameters/order_spec.rb +18 -11
  132. data/spec/chewy/search/parameters/query_storage_examples.rb +67 -21
  133. data/spec/chewy/search/parameters/search_after_spec.rb +4 -1
  134. data/spec/chewy/search/parameters/source_spec.rb +8 -2
  135. data/spec/chewy/search/parameters/track_total_hits_spec.rb +5 -0
  136. data/spec/chewy/search/parameters_spec.rb +18 -4
  137. data/spec/chewy/search/query_proxy_spec.rb +68 -17
  138. data/spec/chewy/search/request_spec.rb +292 -110
  139. data/spec/chewy/search/response_spec.rb +12 -12
  140. data/spec/chewy/search/scrolling_spec.rb +10 -17
  141. data/spec/chewy/search_spec.rb +40 -34
  142. data/spec/chewy/stash_spec.rb +9 -21
  143. data/spec/chewy/strategy/active_job_spec.rb +16 -16
  144. data/spec/chewy/strategy/atomic_no_refresh_spec.rb +60 -0
  145. data/spec/chewy/strategy/atomic_spec.rb +9 -10
  146. data/spec/chewy/strategy/delayed_sidekiq_spec.rb +208 -0
  147. data/spec/chewy/strategy/lazy_sidekiq_spec.rb +214 -0
  148. data/spec/chewy/strategy/sidekiq_spec.rb +12 -12
  149. data/spec/chewy/strategy_spec.rb +19 -15
  150. data/spec/chewy_spec.rb +24 -107
  151. data/spec/spec_helper.rb +3 -22
  152. data/spec/support/active_record.rb +25 -7
  153. metadata +78 -339
  154. data/.circleci/config.yml +0 -240
  155. data/Appraisals +0 -81
  156. data/gemfiles/rails.5.2.activerecord.gemfile +0 -17
  157. data/gemfiles/rails.5.2.mongoid.6.4.gemfile +0 -17
  158. data/gemfiles/rails.6.0.activerecord.gemfile +0 -17
  159. data/gemfiles/sequel.4.45.gemfile +0 -11
  160. data/lib/chewy/backports/deep_dup.rb +0 -46
  161. data/lib/chewy/backports/duplicable.rb +0 -91
  162. data/lib/chewy/search/pagination/will_paginate.rb +0 -43
  163. data/lib/chewy/search/parameters/types.rb +0 -20
  164. data/lib/chewy/strategy/resque.rb +0 -27
  165. data/lib/chewy/strategy/shoryuken.rb +0 -40
  166. data/lib/chewy/type/actions.rb +0 -43
  167. data/lib/chewy/type/adapter/mongoid.rb +0 -67
  168. data/lib/chewy/type/adapter/sequel.rb +0 -93
  169. data/lib/chewy/type/crutch.rb +0 -32
  170. data/lib/chewy/type/import/bulk_builder.rb +0 -122
  171. data/lib/chewy/type/observe.rb +0 -82
  172. data/lib/chewy/type.rb +0 -120
  173. data/lib/sequel/plugins/chewy_observe.rb +0 -63
  174. data/spec/chewy/search/pagination/will_paginate_examples.rb +0 -63
  175. data/spec/chewy/search/pagination/will_paginate_spec.rb +0 -23
  176. data/spec/chewy/search/parameters/types_spec.rb +0 -5
  177. data/spec/chewy/strategy/resque_spec.rb +0 -46
  178. data/spec/chewy/strategy/shoryuken_spec.rb +0 -70
  179. data/spec/chewy/type/actions_spec.rb +0 -50
  180. data/spec/chewy/type/adapter/mongoid_spec.rb +0 -372
  181. data/spec/chewy/type/adapter/sequel_spec.rb +0 -472
  182. data/spec/chewy/type/import/bulk_builder_spec.rb +0 -194
  183. data/spec/chewy/type/mapping_spec.rb +0 -175
  184. data/spec/chewy/type/observe_spec.rb +0 -137
  185. data/spec/chewy/type/wrapper_spec.rb +0 -100
  186. data/spec/chewy/type_spec.rb +0 -55
  187. data/spec/support/mongoid.rb +0 -93
  188. data/spec/support/sequel.rb +0 -80
data/chewy.gemspec CHANGED
@@ -1,8 +1,8 @@
1
- lib = File.expand_path('../lib', __FILE__)
1
+ lib = File.expand_path('lib', __dir__)
2
2
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
3
  require 'chewy/version'
4
4
 
5
- Gem::Specification.new do |spec| # rubocop:disable BlockLength
5
+ Gem::Specification.new do |spec|
6
6
  spec.name = 'chewy'
7
7
  spec.version = Chewy::VERSION
8
8
  spec.authors = ['Toptal, LLC', 'pyromaniac']
@@ -14,25 +14,10 @@ Gem::Specification.new do |spec| # rubocop:disable BlockLength
14
14
 
15
15
  spec.files = `git ls-files`.split($RS)
16
16
  spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
17
- spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
18
17
  spec.require_paths = ['lib']
19
18
 
20
- spec.add_development_dependency 'appraisal'
21
- spec.add_development_dependency 'database_cleaner'
22
- spec.add_development_dependency 'elasticsearch-extensions'
23
- spec.add_development_dependency 'rake'
24
- spec.add_development_dependency 'resque_spec'
25
- spec.add_development_dependency 'rspec', '>= 3.7.0'
26
- spec.add_development_dependency 'rspec-collection_matchers'
27
- spec.add_development_dependency 'rspec-its'
28
- spec.add_development_dependency 'rubocop', '0.52.1'
29
- spec.add_development_dependency 'sqlite3'
30
- spec.add_development_dependency 'timecop'
31
-
32
- spec.add_development_dependency 'method_source'
33
- spec.add_development_dependency 'unparser'
34
-
35
- spec.add_dependency 'activesupport', '>= 5.2'
36
- spec.add_dependency 'elasticsearch', '>= 6.3.0'
19
+ spec.add_dependency 'activesupport', '>= 5.2' # Remove with major version bump, 8.x
20
+ spec.add_dependency 'elasticsearch', '>= 7.14.0', '< 8'
37
21
  spec.add_dependency 'elasticsearch-dsl'
22
+ spec.metadata['rubygems_mfa_required'] = 'true'
38
23
  end
@@ -0,0 +1,12 @@
1
+ gem 'database_cleaner'
2
+ gem 'elasticsearch-extensions'
3
+ gem 'method_source'
4
+ gem 'rake'
5
+ gem 'redis', require: false
6
+ gem 'rspec', '>= 3.7.0'
7
+ gem 'rspec-collection_matchers'
8
+ gem 'rspec-its'
9
+ gem 'rubocop', '1.63.4'
10
+ gem 'sqlite3', '~> 1.4'
11
+ gem 'timecop'
12
+ gem 'unparser'
@@ -1,19 +1,14 @@
1
- # This file was generated by Appraisal
1
+ source 'https://rubygems.org'
2
2
 
3
- source "https://rubygems.org"
4
-
5
- gem "activerecord", "~> 6.1.0"
6
- gem "activesupport", "~> 6.1.0"
7
- gem "activejob", "~> 6.1.0"
8
- gem "resque", require: false
9
- gem "shoryuken", require: false
10
- gem "aws-sdk-sqs", require: false
11
- gem "sidekiq", require: false
12
- gem "kaminari-core", "~> 1.1.0", require: false
13
- gem "will_paginate", require: false
14
- gem "parallel", require: false
3
+ gem 'activejob', '~> 6.1.0'
4
+ gem 'activerecord', '~> 6.1.0'
5
+ gem 'activesupport', '~> 6.1.0'
6
+ gem 'kaminari-core', '~> 1.1.0', require: false
7
+ gem 'parallel', require: false
15
8
  gem 'rspec_junit_formatter', '~> 0.4.1'
9
+ gem 'sidekiq', require: false
16
10
 
17
- gem 'rexml' if RUBY_VERSION >= '3.0.0'
11
+ gem 'rexml'
18
12
 
19
- gemspec path: "../"
13
+ gemspec path: '../'
14
+ eval_gemfile 'base.gemfile'
@@ -0,0 +1,14 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem 'activejob', '~> 7.0.0'
4
+ gem 'activerecord', '~> 7.0.0'
5
+ gem 'activesupport', '~> 7.0.0'
6
+ gem 'kaminari-core', '~> 1.1.0', require: false
7
+ gem 'parallel', require: false
8
+ gem 'rspec_junit_formatter', '~> 0.4.1'
9
+ gem 'sidekiq', require: false
10
+
11
+ gem 'rexml'
12
+
13
+ gemspec path: '../'
14
+ eval_gemfile 'base.gemfile'
@@ -0,0 +1,14 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem 'activejob', '~> 7.1.0'
4
+ gem 'activerecord', '~> 7.1.0'
5
+ gem 'activesupport', '~> 7.1.0'
6
+ gem 'kaminari-core', '~> 1.1.0', require: false
7
+ gem 'parallel', require: false
8
+ gem 'rspec_junit_formatter', '~> 0.4.1'
9
+ gem 'sidekiq', require: false
10
+
11
+ gem 'rexml'
12
+
13
+ gemspec path: '../'
14
+ eval_gemfile 'base.gemfile'
data/lib/chewy/config.rb CHANGED
@@ -3,43 +3,51 @@ module Chewy
3
3
  include Singleton
4
4
 
5
5
  attr_accessor :settings, :logger,
6
- # The first strategy in stack. `:base` by default.
7
- # If you need to return to the previous chewy behavior -
8
- # just set it to `:bypass`
9
- #
10
- :root_strategy,
11
- # Default request strategy middleware, used in e.g
12
- # Rails controllers. See Chewy::Railtie::RequestStrategy
13
- # for more info.
14
- #
15
- :request_strategy,
16
- # Use after_commit callbacks for RDBMS instead of
17
- # after_save and after_destroy. True by default. Useful
18
- # in tests with transactional fixtures or transactional
19
- # DatabaseCleaner strategy.
20
- #
21
- :use_after_commit_callbacks,
22
- # Where Chewy expects to find index definitions
23
- # within a Rails app folder.
24
- :indices_path,
25
- # Set index refresh_interval setting to -1 before reset and put the original value after.
26
- # If setting not present, put back to default 1s
27
- # https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-update-settings.html
28
- :reset_disable_refresh_interval,
29
- # Set number_of_replicas to 0 before reset and put the original value after
30
- # https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-update-settings.html
31
- :reset_no_replicas,
32
- # Refresh or not when import async (sidekiq, resque, activejob)
33
- :disable_refresh_async,
34
- # Default options for root of Chewy type. Allows to set default options
35
- # for type mappings like `_all`.
36
- :default_root_options,
37
- # Default field type for any field in any Chewy type. Defaults to 'text'.
38
- :default_field_type
6
+ # The first strategy in stack. `:base` by default.
7
+ # If you need to return to the previous chewy behavior -
8
+ # just set it to `:bypass`
9
+ #
10
+ :root_strategy,
11
+ # Default request strategy middleware, used in e.g
12
+ # Rails controllers. See Chewy::Railtie::RequestStrategy
13
+ # for more info.
14
+ #
15
+ :request_strategy,
16
+ # Rails console strategy, `:urgent` by default.
17
+ #
18
+ :console_strategy,
19
+ # Use after_commit callbacks for RDBMS instead of
20
+ # after_save and after_destroy. True by default. Useful
21
+ # in tests with transactional fixtures or transactional
22
+ # DatabaseCleaner strategy.
23
+ #
24
+ :use_after_commit_callbacks,
25
+ # Where Chewy expects to find index definitions
26
+ # within a Rails app folder.
27
+ :indices_path,
28
+ # Set index refresh_interval setting to -1 before reset and put the original value after.
29
+ # If setting not present, put back to default 1s
30
+ # https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-update-settings.html
31
+ :reset_disable_refresh_interval,
32
+ # Set number_of_replicas to 0 before reset and put the original value after
33
+ # https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-update-settings.html
34
+ :reset_no_replicas,
35
+ # Refresh or not when import async (sidekiq, lazy_sidekiq, activejob)
36
+ :disable_refresh_async,
37
+ # Default options for root of Chewy type. Allows to set default options
38
+ # for type mappings like `_all`.
39
+ :default_root_options,
40
+ # Default field type for any field in any Chewy type. Defaults to 'text'.
41
+ :default_field_type,
42
+ # Callback called on each search request to be done into ES
43
+ :before_es_request_filter,
44
+ # Behavior when import scope for index includes order, offset or limit.
45
+ # Can be :ignore, :warn, :raise. Defaults to :warn
46
+ :import_scope_cleanup_behavior
39
47
 
40
48
  attr_reader :transport_logger, :transport_tracer,
41
- # Chewy search request DSL base class, used by every index.
42
- :search_class
49
+ # Chewy search request DSL base class, used by every index.
50
+ :search_class
43
51
 
44
52
  def self.delegated
45
53
  public_instance_methods - superclass.public_instance_methods - Singleton.public_instance_methods
@@ -49,6 +57,7 @@ module Chewy
49
57
  @settings = {}
50
58
  @root_strategy = :base
51
59
  @request_strategy = :atomic
60
+ @console_strategy = :urgent
52
61
  @use_after_commit_callbacks = true
53
62
  @reset_disable_refresh_interval = false
54
63
  @reset_no_replicas = false
@@ -56,16 +65,17 @@ module Chewy
56
65
  @indices_path = 'app/chewy'
57
66
  @default_root_options = {}
58
67
  @default_field_type = 'text'.freeze
68
+ @import_scope_cleanup_behavior = :warn
59
69
  @search_class = build_search_class(Chewy::Search::Request)
60
70
  end
61
71
 
62
72
  def transport_logger=(logger)
63
- Chewy.client.transport.logger = logger
73
+ Chewy.client.transport.transport.logger = logger
64
74
  @transport_logger = logger
65
75
  end
66
76
 
67
77
  def transport_tracer=(tracer)
68
- Chewy.client.transport.tracer = tracer
78
+ Chewy.client.transport.transport.tracer = tracer
69
79
  @transport_tracer = tracer
70
80
  end
71
81
 
@@ -123,26 +133,24 @@ module Chewy
123
133
  private
124
134
 
125
135
  def yaml_settings
126
- @yaml_settings ||= begin
127
- if defined?(Rails::VERSION)
128
- file = Rails.root.join('config', 'chewy.yml')
136
+ @yaml_settings ||= build_yaml_settings || {}
137
+ end
129
138
 
130
- if File.exist?(file)
131
- yaml = ERB.new(File.read(file)).result
132
- hash = YAML.load(yaml) # rubocop:disable Security/YAMLLoad
133
- hash[Rails.env].try(:deep_symbolize_keys) if hash
134
- end
135
- end || {}
136
- end
139
+ def build_yaml_settings
140
+ return unless defined?(Rails::VERSION)
141
+
142
+ file = Rails.root.join('config', 'chewy.yml')
143
+
144
+ return unless File.exist?(file)
145
+
146
+ yaml = ERB.new(File.read(file)).result
147
+ hash = YAML.unsafe_load(yaml)
148
+ hash[Rails.env].try(:deep_symbolize_keys) if hash
137
149
  end
138
150
 
139
151
  def build_search_class(base)
140
152
  Class.new(base).tap do |search_class|
141
- if defined?(::Kaminari)
142
- search_class.send :include, Chewy::Search::Pagination::Kaminari
143
- elsif defined?(::WillPaginate)
144
- search_class.send :include, Chewy::Search::Pagination::WillPaginate
145
- end
153
+ search_class.send :include, Chewy::Search::Pagination::Kaminari if defined?(::Kaminari)
146
154
  end
147
155
  end
148
156
  end
@@ -0,0 +1,31 @@
1
+ module Chewy
2
+ # Replacement for Chewy.client
3
+ class ElasticClient
4
+ def self.build_es_client(configuration = Chewy.configuration)
5
+ client_configuration = configuration.deep_dup
6
+ client_configuration.delete(:prefix) # used by Chewy, not relevant to Elasticsearch::Client
7
+ block = client_configuration[:transport_options].try(:delete, :proc)
8
+ ::Elasticsearch::Client.new(client_configuration, &block)
9
+ end
10
+
11
+ def initialize(elastic_client = self.class.build_es_client)
12
+ @elastic_client = elastic_client
13
+ end
14
+
15
+ private
16
+
17
+ def method_missing(name, *args, **kwargs, &block)
18
+ inspect_payload(name, args, kwargs)
19
+
20
+ @elastic_client.__send__(name, *args, **kwargs, &block)
21
+ end
22
+
23
+ def respond_to_missing?(name, _include_private = false)
24
+ @elastic_client.respond_to?(name) || super
25
+ end
26
+
27
+ def inspect_payload(name, args, kwargs)
28
+ Chewy.config.before_es_request_filter&.call(name, args, kwargs)
29
+ end
30
+ end
31
+ end
data/lib/chewy/errors.rb CHANGED
@@ -5,15 +5,9 @@ module Chewy
5
5
  class UndefinedIndex < Error
6
6
  end
7
7
 
8
- class UndefinedType < Error
9
- end
10
-
11
- class UnderivableType < Error
12
- end
13
-
14
8
  class UndefinedUpdateStrategy < Error
15
9
  def initialize(_type)
16
- super <<-MESSAGE
10
+ super(<<-MESSAGE)
17
11
  Index update strategy is undefined for current context.
18
12
  Please wrap your code with `Chewy.strategy(:strategy_name) block.`
19
13
  MESSAGE
@@ -33,13 +27,16 @@ module Chewy
33
27
  message << " on #{documents.count} documents: #{documents}\n"
34
28
  end
35
29
  end
36
- super message
30
+ super(message)
37
31
  end
38
32
  end
39
33
 
40
- class RemovedFeature < Error
34
+ class InvalidJoinFieldType < Error
35
+ def initialize(join_field_type, join_field_name, relations)
36
+ super("`#{join_field_type}` set for the join field `#{join_field_name}` is not on the :relations list (#{relations})")
37
+ end
41
38
  end
42
39
 
43
- class PluginMissing < Error
40
+ class ImportScopeCleanupError < Error
44
41
  end
45
42
  end
@@ -1,8 +1,8 @@
1
1
  module Chewy
2
2
  module Fields
3
3
  class Base
4
- attr_reader :name, :options, :value, :children
5
- attr_accessor :parent
4
+ attr_reader :name, :join_options, :options, :children
5
+ attr_accessor :parent # used by Chewy::Index::Mapping to expand nested fields
6
6
 
7
7
  def initialize(name, value: nil, **options)
8
8
  @name = name.to_sym
@@ -10,9 +10,11 @@ module Chewy
10
10
  update_options!(**options)
11
11
  @value = value
12
12
  @children = []
13
+ @allowed_relations = find_allowed_relations(options[:relations]) # for join fields
13
14
  end
14
15
 
15
16
  def update_options!(**options)
17
+ @join_options = options.delete(:join) || {}
16
18
  @options = options
17
19
  end
18
20
 
@@ -31,7 +33,7 @@ module Chewy
31
33
  else
32
34
  {}
33
35
  end
34
- mapping.reverse_merge!(options)
36
+ mapping.reverse_merge!(options.except(:ignore_blank))
35
37
  mapping.reverse_merge!(type: (children.present? ? 'object' : Chewy.default_field_type))
36
38
 
37
39
  {name => mapping}
@@ -40,6 +42,8 @@ module Chewy
40
42
  def compose(*objects)
41
43
  result = evaluate(objects)
42
44
 
45
+ return {} if result.blank? && ignore_blank?
46
+
43
47
  if children.present? && !multi_field?
44
48
  result = if result.respond_to?(:to_ary)
45
49
  result.to_ary.map { |item| compose_children(item, *objects) }
@@ -51,22 +55,70 @@ module Chewy
51
55
  {name => result}
52
56
  end
53
57
 
58
+ def value
59
+ if join_field?
60
+ join_type = join_options[:type]
61
+ join_id = join_options[:id]
62
+ # memoize
63
+ @value ||= proc do |object|
64
+ validate_join_type!(value_by_name_proc(join_type).call(object))
65
+ # If it's a join field and it has join_id, the value is compound and contains
66
+ # both name (type) and id of the parent object
67
+ if value_by_name_proc(join_id).call(object).present?
68
+ {
69
+ name: value_by_name_proc(join_type).call(object), # parent type
70
+ parent: value_by_name_proc(join_id).call(object) # parent id
71
+ }
72
+ else
73
+ value_by_name_proc(join_type).call(object)
74
+ end
75
+ end
76
+ else
77
+ @value
78
+ end
79
+ end
80
+
54
81
  private
55
82
 
56
- def evaluate(objects)
57
- object = objects.first
83
+ def geo_point?
84
+ @options[:type].to_s == 'geo_point'
85
+ end
86
+
87
+ def join_field?
88
+ @options[:type].to_s == 'join'
89
+ end
58
90
 
91
+ def ignore_blank?
92
+ @options.fetch(:ignore_blank) { geo_point? }
93
+ end
94
+
95
+ def evaluate(objects)
59
96
  if value.is_a?(Proc)
60
- if value.arity.zero?
61
- object.instance_exec(&value)
62
- elsif value.arity < 0
63
- value.call(*object)
64
- else
65
- value.call(*objects.first(value.arity))
66
- end
97
+ value_by_proc(objects, value)
67
98
  else
68
- message = value.is_a?(Symbol) || value.is_a?(String) ? value.to_sym : name
99
+ value_by_name(objects, value)
100
+ end
101
+ end
69
102
 
103
+ def value_by_proc(objects, value)
104
+ object = objects.first
105
+ if value.arity.zero?
106
+ object.instance_exec(&value)
107
+ elsif value.arity.negative?
108
+ value.call(*object)
109
+ else
110
+ value.call(*objects.first(value.arity))
111
+ end
112
+ end
113
+
114
+ def value_by_name(objects, value)
115
+ object = objects.first
116
+ message = value.is_a?(Symbol) || value.is_a?(String) ? value.to_sym : name
117
+ value_by_name_proc(message).call(object)
118
+ end
119
+
120
+ def value_by_name_proc(message)
121
+ proc do |object|
70
122
  if object.is_a?(Hash)
71
123
  if object.key?(message)
72
124
  object[message]
@@ -79,6 +131,20 @@ module Chewy
79
131
  end
80
132
  end
81
133
 
134
+ def validate_join_type!(type)
135
+ return unless type
136
+ return if @allowed_relations.include?(type.to_sym)
137
+
138
+ raise Chewy::InvalidJoinFieldType.new(type, @name, options[:relations])
139
+ end
140
+
141
+ def find_allowed_relations(relations)
142
+ return [] unless relations
143
+ return relations unless relations.is_a?(Hash)
144
+
145
+ (relations.keys + relations.values).flatten.uniq
146
+ end
147
+
82
148
  def compose_children(value, *parent_objects)
83
149
  return unless value
84
150
 
@@ -1,10 +1,7 @@
1
1
  module Chewy
2
2
  module Fields
3
3
  class Root < Chewy::Fields::Base
4
- attr_reader :dynamic_templates
5
- attr_reader :id
6
- attr_reader :parent
7
- attr_reader :parent_id
4
+ attr_reader :dynamic_templates, :id
8
5
 
9
6
  def initialize(name, **options)
10
7
  super(name, **options)
@@ -15,9 +12,7 @@ module Chewy
15
12
 
16
13
  def update_options!(**options)
17
14
  @id = options.fetch(:id, options.fetch(:_id, @id))
18
- @parent = options.fetch(:parent, options.fetch(:_parent, @parent))
19
- @parent_id = options.fetch(:parent_id, @parent_id)
20
- @options.merge!(options.except(:id, :_id, :parent, :_parent, :parent_id, :type))
15
+ @options.merge!(options.except(:id, :_id, :type))
21
16
  end
22
17
 
23
18
  def mappings_hash
@@ -29,8 +24,7 @@ module Chewy
29
24
  mappings[name][:dynamic_templates].concat dynamic_templates
30
25
  end
31
26
 
32
- mappings[name][:_parent] = parent.is_a?(Hash) ? parent : {type: parent} if parent
33
- mappings
27
+ mappings[name]
34
28
  end
35
29
 
36
30
  def dynamic_template(*args)
@@ -54,13 +48,9 @@ module Chewy
54
48
  end
55
49
  end
56
50
 
57
- def compose_parent(object)
58
- return unless parent_id
59
- parent_id.arity.zero? ? object.instance_exec(&parent_id) : parent_id.call(object)
60
- end
61
-
62
51
  def compose_id(object)
63
52
  return unless id
53
+
64
54
  id.arity.zero? ? object.instance_exec(&id) : id.call(object)
65
55
  end
66
56
 
@@ -59,9 +59,7 @@ module Chewy
59
59
 
60
60
  body = specification_hash
61
61
  body[:aliases] = {general_name => {}} if options[:alias] && suffixed_name != general_name
62
- args = {index: suffixed_name, body: body}
63
- args[:include_type_name] = true if Runtime.version >= '6.7.0'
64
- result = client.indices.create(**args)
62
+ result = client.indices.create(index: suffixed_name, body: body)
65
63
 
66
64
  Chewy.wait_for_status if result
67
65
  result
@@ -131,37 +129,6 @@ module Chewy
131
129
  create! suffix
132
130
  end
133
131
 
134
- # Perform import operation for every defined type
135
- #
136
- # UsersIndex.import # imports default data for every index type
137
- # UsersIndex.import user: User.active # imports specified objects for user type and default data for other types
138
- # UsersIndex.import refresh: false # to disable index refreshing after import
139
- # UsersIndex.import suffix: Time.now.to_i # imports data to index with specified suffix if such is exists
140
- # UsersIndex.import batch_size: 300 # import batch size
141
- #
142
- # See [import.rb](lib/chewy/type/import.rb) for more details.
143
- #
144
- %i[import import!].each do |method|
145
- class_eval <<-METHOD, __FILE__, __LINE__ + 1
146
- def #{method}(*args)
147
- return true if args.first.blank? && !args.first.nil?
148
-
149
- options = args.extract_options!
150
- if args.one? && type_names.one?
151
- objects = {type_names.first.to_sym => args.first}
152
- elsif args.one?
153
- fail ArgumentError, "Please pass objects for `#{method}` as a hash with type names"
154
- else
155
- objects = options.reject { |k, v| !type_names.map(&:to_sym).include?(k) }
156
- end
157
- types.map do |type|
158
- args = [objects[type.type_name.to_sym], options.dup].reject(&:blank?)
159
- type.#{method} *args
160
- end.all?
161
- end
162
- METHOD
163
- end
164
-
165
132
  # Deletes, creates and imports data to the index. Returns the
166
133
  # import result. If index name suffix is passed as the first
167
134
  # argument - performs zero-downtime index resetting.
@@ -182,14 +149,18 @@ module Chewy
182
149
  def reset!(suffix = nil, apply_journal: true, journal: false, **import_options)
183
150
  result = if suffix.present?
184
151
  start_time = Time.now
185
- indexes = self.indexes
152
+ indexes = self.indexes - [index_name]
186
153
  create! suffix, alias: false
187
154
 
188
155
  general_name = index_name
189
156
  suffixed_name = index_name(suffix: suffix)
190
157
 
191
158
  optimize_index_settings suffixed_name
192
- result = import import_options.merge(suffix: suffix, journal: journal, refresh: !Chewy.reset_disable_refresh_interval)
159
+ result = import(**import_options.merge(
160
+ suffix: suffix,
161
+ journal: journal,
162
+ refresh: !Chewy.reset_disable_refresh_interval
163
+ ))
193
164
  original_index_settings suffixed_name
194
165
 
195
166
  delete if indexes.blank?
@@ -205,12 +176,13 @@ module Chewy
205
176
  result
206
177
  else
207
178
  purge!
208
- import import_options.merge(journal: journal)
179
+ import(**import_options.merge(journal: journal))
209
180
  end
210
181
 
211
182
  specification.lock!
212
183
  result
213
184
  end
185
+ alias_method :reset, :reset!
214
186
 
215
187
  # A {Chewy::Journal} instance for the particular index
216
188
  #
@@ -219,6 +191,50 @@ module Chewy
219
191
  @journal ||= Chewy::Journal.new(self)
220
192
  end
221
193
 
194
+ def clear_cache(args = {index: index_name})
195
+ client.indices.clear_cache(args)
196
+ end
197
+
198
+ def reindex(source: index_name, dest: index_name)
199
+ client.reindex(
200
+ {
201
+ body:
202
+ {
203
+ source: {index: source},
204
+ dest: {index: dest}
205
+ }
206
+ }
207
+ )
208
+ end
209
+
210
+ # Adds new fields to an existing data stream or index.
211
+ # Change the search settings of existing fields.
212
+ #
213
+ # @example
214
+ # Chewy.client.update_mapping('cities', {properties: {new_field: {type: :text}}})
215
+ #
216
+ def update_mapping(name = index_name, body = root.mappings_hash)
217
+ client.indices.put_mapping(
218
+ index: name,
219
+ body: body
220
+ )['acknowledged']
221
+ end
222
+
223
+ # Performs missing and outdated objects synchronization for the current index.
224
+ #
225
+ # @example
226
+ # UsersIndex.sync
227
+ #
228
+ # @see Chewy::Index::Syncer
229
+ # @param parallel [true, Integer, Hash] options for parallel execution or the number of processes
230
+ # @return [Hash{Symbol, Object}, nil] a number of missing and outdated documents re-indexed and their ids,
231
+ # nil in case of errors
232
+ def sync(parallel: nil)
233
+ syncer = Syncer.new(self, parallel: parallel)
234
+ count = syncer.perform
235
+ {count: count, missing: syncer.missing_ids, outdated: syncer.outdated_ids} if count
236
+ end
237
+
222
238
  private
223
239
 
224
240
  def optimize_index_settings(index_name)
@@ -244,6 +260,7 @@ module Chewy
244
260
 
245
261
  def index_settings(setting_name)
246
262
  return {} unless settings_hash.key?(:settings) && settings_hash[:settings].key?(:index)
263
+
247
264
  settings_hash[:settings][:index].slice(setting_name)
248
265
  end
249
266
  end