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
@@ -1,26 +1,26 @@
1
- require 'i18n/core_ext/hash'
1
+ require 'active_support/core_ext/hash/keys'
2
2
 
3
3
  # Rspec matcher `update_index`
4
4
  # To use it - add `require 'chewy/rspec'` to the `spec_helper.rb`
5
- # Simple usage - just pass type as argument.
5
+ # Simple usage - just pass index as argument.
6
6
  #
7
- # specify { expect { user.save! }.to update_index(UsersIndex::User) }
8
- # specify { expect { user.save! }.to update_index('users#user') }
9
- # specify { expect { user.save! }.not_to update_index('users#user') }
7
+ # specify { expect { user.save! }.to update_index(UsersIndex) }
8
+ # specify { expect { user.save! }.to update_index('users') }
9
+ # specify { expect { user.save! }.not_to update_index('users') }
10
10
  #
11
11
  # This example will pass as well because user1 was reindexed
12
12
  # and nothing was said about user2:
13
13
  #
14
14
  # specify { expect { [user1, user2].map(&:save!) }
15
- # .to update_index(UsersIndex.user).and_reindex(user1) }
15
+ # .to update_index(UsersIndex).and_reindex(user1) }
16
16
  #
17
17
  # If you need to specify reindexed records strictly - use `only` chain.
18
18
  # Combined matcher chain methods:
19
19
  #
20
20
  # specify { expect { user1.destroy!; user2.save! } }
21
- # .to update_index(UsersIndex::User).and_reindex(user2).and_delete(user1) }
21
+ # .to update_index(UsersIndex).and_reindex(user2).and_delete(user1) }
22
22
  #
23
- RSpec::Matchers.define :update_index do |type_name, options = {}| # rubocop:disable BlockLength
23
+ RSpec::Matchers.define :update_index do |index_name, options = {}| # rubocop:disable Metrics/BlockLength
24
24
  if !respond_to?(:failure_message) && respond_to?(:failure_message_for_should)
25
25
  alias_method :failure_message, :failure_message_for_should
26
26
  alias_method :failure_message_when_negated, :failure_message_for_should_not
@@ -28,30 +28,30 @@ RSpec::Matchers.define :update_index do |type_name, options = {}| # rubocop:disa
28
28
 
29
29
  # Specify indexed records by passing record itself or id.
30
30
  #
31
- # specify { expect { user.save! }.to update_index(UsersIndex::User).and_reindex(user)
32
- # specify { expect { user.save! }.to update_index(UsersIndex::User).and_reindex(42)
31
+ # specify { expect { user.save! }.to update_index(UsersIndex).and_reindex(user)
32
+ # specify { expect { user.save! }.to update_index(UsersIndex).and_reindex(42)
33
33
  # specify { expect { [user1, user2].map(&:save!) }
34
- # .to update_index(UsersIndex::User).and_reindex(user1, user2) }
34
+ # .to update_index(UsersIndex).and_reindex(user1, user2) }
35
35
  # specify { expect { [user1, user2].map(&:save!) }
36
- # .to update_index(UsersIndex::User).and_reindex(user1).and_reindex(user2) }
36
+ # .to update_index(UsersIndex).and_reindex(user1).and_reindex(user2) }
37
37
  #
38
38
  # Specify indexing count for every particular record. Useful in case
39
39
  # urgent index updates.
40
40
  #
41
41
  # specify { expect { 2.times { user.save! } }
42
- # .to update_index(UsersIndex::User).and_reindex(user, times: 2) }
42
+ # .to update_index(UsersIndex).and_reindex(user, times: 2) }
43
43
  #
44
44
  # Specify reindexed attributes. Note that arrays are
45
45
  # compared position-independently.
46
46
  #
47
47
  # specify { expect { user.update_attributes!(name: 'Duke') }
48
- # .to update_index(UsersIndex.user).and_reindex(user, with: {name: 'Duke'}) }
48
+ # .to update_index(UsersIndex).and_reindex(user, with: {name: 'Duke'}) }
49
49
  #
50
50
  # You can combine all the options and chain `and_reindex` method to
51
51
  # specify options for every indexed record:
52
52
  #
53
53
  # specify { expect { 2.times { [user1, user2].map { |u| u.update_attributes!(name: "Duke#{u.id}") } } }
54
- # .to update_index(UsersIndex.user)
54
+ # .to update_index(UsersIndex)
55
55
  # .and_reindex(user1, with: {name: 'Duke42'}) }
56
56
  # .and_reindex(user2, times: 1, with: {name: 'Duke43'}) }
57
57
  #
@@ -62,8 +62,8 @@ RSpec::Matchers.define :update_index do |type_name, options = {}| # rubocop:disa
62
62
 
63
63
  # Specify deleted records with record itself or id passed.
64
64
  #
65
- # specify { expect { user.destroy! }.to update_index(UsersIndex::User).and_delete(user) }
66
- # specify { expect { user.destroy! }.to update_index(UsersIndex::User).and_delete(user.id) }
65
+ # specify { expect { user.destroy! }.to update_index(UsersIndex).and_delete(user) }
66
+ # specify { expect { user.destroy! }.to update_index(UsersIndex).and_delete(user.id) }
67
67
  #
68
68
  chain(:and_delete) do |*args|
69
69
  @delete ||= {}
@@ -73,14 +73,14 @@ RSpec::Matchers.define :update_index do |type_name, options = {}| # rubocop:disa
73
73
  # Used for specifying than no other records would be indexed or deleted:
74
74
  #
75
75
  # specify { expect { [user1, user2].map(&:save!) }
76
- # .to update_index(UsersIndex.user).and_reindex(user1, user2).only }
76
+ # .to update_index(UsersIndex).and_reindex(user1, user2).only }
77
77
  # specify { expect { [user1, user2].map(&:destroy!) }
78
- # .to update_index(UsersIndex.user).and_delete(user1, user2).only }
78
+ # .to update_index(UsersIndex).and_delete(user1, user2).only }
79
79
  #
80
80
  # This example will fail:
81
81
  #
82
82
  # specify { expect { [user1, user2].map(&:save!) }
83
- # .to update_index(UsersIndex.user).and_reindex(user1).only }
83
+ # .to update_index(UsersIndex).and_reindex(user1).only }
84
84
  #
85
85
  chain(:only) do |*_args|
86
86
  raise 'Use `only` in conjunction with `and_reindex` or `and_delete`' if @reindex.blank? && @delete.blank?
@@ -88,23 +88,30 @@ RSpec::Matchers.define :update_index do |type_name, options = {}| # rubocop:disa
88
88
  @only = true
89
89
  end
90
90
 
91
+ # Expect import to be called with refresh=false parameter
92
+ chain(:no_refresh) do
93
+ @no_refresh = true
94
+ end
95
+
91
96
  def supports_block_expectations?
92
97
  true
93
98
  end
94
99
 
95
- match do |block| # rubocop:disable BlockLength
100
+ match do |block| # rubocop:disable Metrics/BlockLength
96
101
  @reindex ||= {}
97
102
  @delete ||= {}
98
103
  @missed_reindex = []
99
104
  @missed_delete = []
100
105
 
101
- type = Chewy.derive_type(type_name)
106
+ index = Chewy.derive_name(index_name)
102
107
  if defined?(Mocha) && RSpec.configuration.mock_framework.to_s == 'RSpec::Core::MockingAdapters::Mocha'
103
- Chewy::Type::Import::BulkRequest.stubs(:new).with(type, any_parameters).returns(mock_bulk_request)
108
+ params_matcher = @no_refresh ? has_entry(refresh: false) : any_parameters
109
+ Chewy::Index::Import::BulkRequest.stubs(:new).with(index, params_matcher).returns(mock_bulk_request)
104
110
  else
105
- mocked_already = ::RSpec::Mocks.space.proxy_for(Chewy::Type::Import::BulkRequest).method_double_if_exists_for_message(:new)
106
- allow(Chewy::Type::Import::BulkRequest).to receive(:new).and_call_original unless mocked_already
107
- allow(Chewy::Type::Import::BulkRequest).to receive(:new).with(type, any_args).and_return(mock_bulk_request)
111
+ mocked_already = RSpec::Mocks.space.proxy_for(Chewy::Index::Import::BulkRequest).method_double_if_exists_for_message(:new)
112
+ allow(Chewy::Index::Import::BulkRequest).to receive(:new).and_call_original unless mocked_already
113
+ params_matcher = @no_refresh ? hash_including(refresh: false) : any_args
114
+ allow(Chewy::Index::Import::BulkRequest).to receive(:new).with(index, params_matcher).and_return(mock_bulk_request)
108
115
  end
109
116
 
110
117
  Chewy.strategy(options[:strategy] || :atomic) { block.call }
@@ -127,13 +134,13 @@ RSpec::Matchers.define :update_index do |type_name, options = {}| # rubocop:disa
127
134
  end
128
135
 
129
136
  @reindex.each_value do |document|
130
- document[:match_count] = (!document[:expected_count] && document[:real_count] > 0) ||
137
+ document[:match_count] = (!document[:expected_count] && (document[:real_count]).positive?) ||
131
138
  (document[:expected_count] && document[:expected_count] == document[:real_count])
132
139
  document[:match_attributes] = document[:expected_attributes].blank? ||
133
140
  compare_attributes(document[:expected_attributes], document[:real_attributes])
134
141
  end
135
142
  @delete.each_value do |document|
136
- document[:match_count] = (!document[:expected_count] && document[:real_count] > 0) ||
143
+ document[:match_count] = (!document[:expected_count] && (document[:real_count]).positive?) ||
137
144
  (document[:expected_count] && document[:expected_count] == document[:real_count])
138
145
  end
139
146
 
@@ -142,13 +149,13 @@ RSpec::Matchers.define :update_index do |type_name, options = {}| # rubocop:disa
142
149
  @delete.all? { |_, document| document[:match_count] }
143
150
  end
144
151
 
145
- failure_message do # rubocop:disable BlockLength
152
+ failure_message do # rubocop:disable Metrics/BlockLength
146
153
  output = ''
147
154
 
148
155
  if mock_bulk_request.updates.none?
149
- output << "Expected index `#{type_name}` to be updated, but it was not\n"
156
+ output << "Expected index `#{index_name}` to be updated#{' with no refresh' if @no_refresh}, but it was not\n"
150
157
  elsif @missed_reindex.present? || @missed_delete.present?
151
- message = "Expected index `#{type_name}` "
158
+ message = "Expected index `#{index_name}` "
152
159
  message << [
153
160
  ("to update documents #{@reindex.keys}" if @reindex.present?),
154
161
  ("to delete documents #{@delete.keys}" if @delete.present?)
@@ -166,9 +173,13 @@ RSpec::Matchers.define :update_index do |type_name, options = {}| # rubocop:disa
166
173
  output << @reindex.each.with_object('') do |(id, document), result|
167
174
  unless document[:match_count] && document[:match_attributes]
168
175
  result << "Expected document with id `#{id}` to be reindexed"
169
- if document[:real_count] > 0
170
- result << "\n #{document[:expected_count]} times, but was reindexed #{document[:real_count]} times" if document[:expected_count] && !document[:match_count]
171
- result << "\n with #{document[:expected_attributes]}, but it was reindexed with #{document[:real_attributes]}" if document[:expected_attributes].present? && !document[:match_attributes]
176
+ if (document[:real_count]).positive?
177
+ if document[:expected_count] && !document[:match_count]
178
+ result << "\n #{document[:expected_count]} times, but was reindexed #{document[:real_count]} times"
179
+ end
180
+ if document[:expected_attributes].present? && !document[:match_attributes]
181
+ result << "\n with #{document[:expected_attributes]}, but it was reindexed with #{document[:real_attributes]}"
182
+ end
172
183
  else
173
184
  result << ', but it was not'
174
185
  end
@@ -179,11 +190,11 @@ RSpec::Matchers.define :update_index do |type_name, options = {}| # rubocop:disa
179
190
  output << @delete.each.with_object('') do |(id, document), result|
180
191
  unless document[:match_count]
181
192
  result << "Expected document with id `#{id}` to be deleted"
182
- result << if document[:real_count] > 0 && document[:expected_count]
183
- "\n #{document[:expected_count]} times, but was deleted #{document[:real_count]} times"
184
- else
185
- ', but it was not'
186
- end
193
+ result << if (document[:real_count]).positive? && document[:expected_count]
194
+ "\n #{document[:expected_count]} times, but was deleted #{document[:real_count]} times"
195
+ else
196
+ ', but it was not'
197
+ end
187
198
  result << "\n"
188
199
  end
189
200
  end
@@ -193,9 +204,9 @@ RSpec::Matchers.define :update_index do |type_name, options = {}| # rubocop:disa
193
204
 
194
205
  failure_message_when_negated do
195
206
  if mock_bulk_request.updates.present?
196
- "Expected index `#{type_name}` not to be updated, but it was with #{mock_bulk_request.updates.map(&:values).flatten.group_by { |documents| documents[:_id] }.map do |id, documents|
197
- "\n document id `#{id}` (#{documents.count} times)"
198
- end.join}\n"
207
+ "Expected index `#{index_name}` not to be updated, but it was with #{mock_bulk_request.updates.map(&:values).flatten.group_by { |documents| documents[:_id] }.map do |id, documents|
208
+ "\n document id `#{id}` (#{documents.count} times)"
209
+ end.join}\n"
199
210
  end
200
211
  end
201
212
 
@@ -209,7 +220,7 @@ RSpec::Matchers.define :update_index do |type_name, options = {}| # rubocop:disa
209
220
  expected_count = options[:times] || options[:count]
210
221
  expected_attributes = (options[:with] || options[:attributes] || {}).deep_symbolize_keys
211
222
 
212
- Hash[args.flatten.map do |document|
223
+ args.flatten.to_h do |document|
213
224
  id = document.respond_to?(:id) ? document.id.to_s : document.to_s
214
225
  [id, {
215
226
  document: document,
@@ -218,7 +229,7 @@ RSpec::Matchers.define :update_index do |type_name, options = {}| # rubocop:disa
218
229
  real_count: 0,
219
230
  real_attributes: {}
220
231
  }]
221
- end]
232
+ end
222
233
  end
223
234
 
224
235
  def compare_attributes(expected, real)
data/lib/chewy/rspec.rb CHANGED
@@ -1 +1,3 @@
1
+ require 'chewy/rspec/build_query'
2
+ require 'chewy/rspec/helpers'
1
3
  require 'chewy/rspec/update_index'
@@ -5,7 +5,7 @@ module Chewy
5
5
  attr_reader :major, :minor, :patch
6
6
 
7
7
  def initialize(version)
8
- @major, @minor, @patch = *(version.to_s.split('.', 3) + [0] * 3).first(3).map(&:to_i)
8
+ @major, @minor, @patch = *(version.to_s.split('.', 3) + ([0] * 3)).first(3).map(&:to_i)
9
9
  end
10
10
 
11
11
  def to_s
data/lib/chewy/runtime.rb CHANGED
@@ -3,7 +3,7 @@ require 'chewy/runtime/version'
3
3
  module Chewy
4
4
  module Runtime
5
5
  def self.version
6
- Thread.current[:chewy_runtime_version] ||= Version.new(Chewy.client.info['version']['number'])
6
+ Chewy.current[:chewy_runtime_version] ||= Version.new(Chewy.client.info['version']['number'])
7
7
  end
8
8
  end
9
9
  end
@@ -3,36 +3,28 @@ module Chewy
3
3
  # This class is used for two different purposes: load ORM/ODM
4
4
  # source objects.
5
5
  #
6
- # @see Chewy::Type::Import
6
+ # @see Chewy::Index::Import
7
7
  # @see Chewy::Search::Request#load
8
8
  # @see Chewy::Search::Response#objects
9
9
  # @see Chewy::Search::Scrolling#scroll_objects
10
10
  class Loader
11
- # @param indexes [Array<Chewy::Index>] list of indexes to lookup types
12
- # @param only [Array<String, Symbol>] list of selected type names to load
13
- # @param except [Array<String, Symbol>] list of type names which will not be loaded
11
+ # @param indexes [Array<Chewy::Index>] list of indexes to lookup
14
12
  # @param options [Hash] adapter-specific load options
15
- # @see Chewy::Type::Adapter::Base#load
16
- def initialize(indexes: [], only: [], except: [], **options)
13
+ # @see Chewy::Index::Adapter::Base#load
14
+ def initialize(indexes: [], **options)
17
15
  @indexes = indexes
18
- @only = Array.wrap(only).map(&:to_s)
19
- @except = Array.wrap(except).map(&:to_s)
20
16
  @options = options
21
17
  end
22
18
 
23
- # Returns a {Chewy::Type} object for index name and type name passed. Caches
24
- # the result for each pair to make lookup faster.
25
- #
26
- # @param index [String] index name
27
- # @param type [String] type name
28
- # @return [Chewy::Type]
29
- # @raise [Chewy::UnderivableType] when index or hash were not found
30
- def derive_type(index, type)
31
- (@derive_type ||= {})[[index, type]] ||= begin
32
- index_class = derive_index(index)
33
- raise Chewy::UnderivableType, "Can not find index named `#{index}`" unless index_class
34
- index_class.type_hash[type] or raise Chewy::UnderivableType, "Index `#{index}` doesn`t have type named `#{type}`"
35
- end
19
+ def derive_index(index_name)
20
+ index = (@derive_index ||= {})[index_name] ||= indexes_hash[index_name] ||
21
+ indexes_hash[indexes_hash.keys.sort_by(&:length)
22
+ .reverse.detect do |name|
23
+ index_name.match(/#{name}(_.+|\z)/)
24
+ end]
25
+ raise Chewy::UndefinedIndex, "Can not find index named `#{index}`" unless index
26
+
27
+ index
36
28
  end
37
29
 
38
30
  # For each passed hit this method loads an ORM/ORD source object
@@ -41,19 +33,17 @@ module Chewy
41
33
  # will be returned at the corresponding position in array.
42
34
  #
43
35
  # Records/documents are loaded in an efficient manner, performing
44
- # a single query for each type present.
36
+ # a single query for each index present.
45
37
  #
46
38
  # @param hits [Array<Hash>] ES hits array
47
39
  # @return [Array<Object, nil>] the array of corresponding ORM/ODM objects
48
40
  def load(hits)
49
- hit_groups = hits.group_by { |hit| [hit['_index'], hit['_type']] }
50
- loaded_objects = hit_groups.each_with_object({}) do |((index_name, type_name), hit_group), result|
51
- next if skip_type?(type_name)
52
-
53
- type = derive_type(index_name, type_name)
41
+ hit_groups = hits.group_by { |hit| hit['_index'] }
42
+ loaded_objects = hit_groups.each_with_object({}) do |(index_name, hit_group), result|
43
+ index = derive_index(index_name)
54
44
  ids = hit_group.map { |hit| hit['_id'] }
55
- loaded = type.adapter.load(ids, **@options.merge(_type: type))
56
- loaded ||= hit_group.map { |hit| type.build(hit) }
45
+ loaded = index.adapter.load(ids, **@options.merge(_index: index.base_name))
46
+ loaded ||= hit_group.map { |hit| index.build(hit) }
57
47
 
58
48
  result.merge!(hit_group.zip(loaded).to_h)
59
49
  end
@@ -63,21 +53,9 @@ module Chewy
63
53
 
64
54
  private
65
55
 
66
- def derive_index(index_name)
67
- (@derive_index ||= {})[index_name] ||= indexes_hash[index_name] ||
68
- indexes_hash[indexes_hash.keys.sort_by(&:length)
69
- .reverse.detect do |name|
70
- index_name.match(/#{name}(_.+|\z)/)
71
- end]
72
- end
73
-
74
56
  def indexes_hash
75
57
  @indexes_hash ||= @indexes.index_by(&:index_name)
76
58
  end
77
-
78
- def skip_type?(type_name)
79
- @except.include?(type_name) || @only.present? && !@only.include?(type_name)
80
- end
81
59
  end
82
60
  end
83
61
  end
@@ -0,0 +1,16 @@
1
+ require 'chewy/search/parameters/storage'
2
+
3
+ module Chewy
4
+ module Search
5
+ class Parameters
6
+ # Just a standard hash storage. Nothing to see here.
7
+ #
8
+ # @see Chewy::Search::Parameters::HashStorage
9
+ # @see Chewy::Search::Request#collapse
10
+ # @see https://www.elastic.co/guide/en/elasticsearch/reference/current/collapse-search-results.html
11
+ class Collapse < Storage
12
+ include HashStorage
13
+ end
14
+ end
15
+ end
16
+ end
@@ -5,7 +5,7 @@ module Chewy
5
5
  class Parameters
6
6
  # This is a basic storage implementation for `query`, `filter`
7
7
  # and `post_filter` storages. It uses `bool` query as a root
8
- # structure for each of them. The `bool` root is ommited on
8
+ # structure for each of them. The `bool` root is omitted on
9
9
  # rendering if there is only a single query in the `must` or
10
10
  # `should` array. Besides the standard parameter storage
11
11
  # capabilities, it provides specialized methods for the `bool`
@@ -86,7 +86,7 @@ module Chewy
86
86
  def reduce
87
87
  value = to_h
88
88
  .reject { |_, v| v.blank? }
89
- .map { |k, v| [k, v.is_a?(Array) && v.one? ? v.first : v] }.to_h
89
+ .transform_values { |v| v.is_a?(Array) && v.one? ? v.first : v }
90
90
  value.delete(:minimum_should_match) if should.empty?
91
91
  value
92
92
  end
@@ -0,0 +1,27 @@
1
+ require 'chewy/search/parameters/storage'
2
+
3
+ module Chewy
4
+ module Search
5
+ class Parameters
6
+ # Stores boolean value, but has 3 states: `true`, `false` and `nil`.
7
+ #
8
+ # @see Chewy::Search::Request#ignore_unavailable
9
+ # @see https://www.elastic.co/guide/en/elasticsearch/reference/current/multi-index.html#multi-index
10
+ class IgnoreUnavailable < Storage
11
+ # We don't want to render `nil`, but render `true` and `false` values.
12
+ #
13
+ # @see Chewy::Search::Parameters::Storage#render
14
+ # @return [{Symbol => Object}, nil]
15
+ def render
16
+ {self.class.param_name => value} unless value.nil?
17
+ end
18
+
19
+ private
20
+
21
+ def normalize(value)
22
+ !!value unless value.nil?
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -3,70 +3,48 @@ require 'chewy/search/parameters/storage'
3
3
  module Chewy
4
4
  module Search
5
5
  class Parameters
6
- # Stores indices and/or types to query.
6
+ # Stores indices to query.
7
7
  # Renders it to lists of string accepted by ElasticSearch
8
8
  # API.
9
9
  #
10
- # The semantics behind it can be described in the
11
- # following statements:
12
- # 1. If index is added to the storage, no matter, a class
10
+ # If index is added to the storage, no matter, a class
13
11
  # or a string/symbol, it gets appended to the list.
14
- # 2. If type is added to the storage, it filters out types
15
- # assigned via indices.
16
- # 3. But when a type class with non-existing index is added,
17
- # this index got also added to the list if indices.
18
- # 4. In cases when of an index identifier added, type
19
- # indetifiers also got appended instead of filtering.
20
12
  class Indices < Storage
21
13
  # Two index storages are equal if they produce the
22
14
  # same output on render.
23
15
  #
24
16
  # @see Chewy::Search::Parameters::Storage#==
25
17
  # @param other [Chewy::Search::Parameters::Storage] any storage instance
26
- # @return [true, false] the result of comparision
18
+ # @return [true, false] the result of comparison
27
19
  def ==(other)
28
- super || other.class == self.class && other.render == render
20
+ super || (other.class == self.class && other.render == render)
29
21
  end
30
22
 
31
- # Just adds types to types and indices to indices.
23
+ # Just adds indices to indices.
32
24
  #
33
25
  # @see Chewy::Search::Parameters::Storage#update!
34
- # @param other_value [{Symbol => Array<Chewy::Index, Chewy::Type, String, Symbol>}] any acceptable storage value
35
- # @return [{Symbol => Array<Chewy::Index, Chewy::Type, String, Symbol>}] updated value
26
+ # @param other_value [{Symbol => Array<Chewy::Index, String, Symbol>}] any acceptable storage value
27
+ # @return [{Symbol => Array<Chewy::Index, String, Symbol>}] updated value
36
28
  def update!(other_value)
37
29
  new_value = normalize(other_value)
38
30
 
39
- @value = {
40
- indices: value[:indices] | new_value[:indices],
41
- types: value[:types] | new_value[:types]
42
- }
31
+ @value = {indices: value[:indices] | new_value[:indices]}
43
32
  end
44
33
 
45
- # Returns desired index and type names.
34
+ # Returns desired index names.
46
35
  #
47
36
  # @see Chewy::Search::Parameters::Storage#render
48
37
  # @return [{Symbol => Array<String>}] rendered value with the parameter name
49
38
  def render
50
- {
51
- index: index_names.uniq.sort,
52
- type: type_names.uniq.sort
53
- }.reject { |_, v| v.blank? }
39
+ {index: index_names.uniq.sort}.reject { |_, v| v.blank? }
54
40
  end
55
41
 
56
42
  # Returns index classes used for the request.
57
- # No strings/symbos included.
43
+ # No strings/symbols included.
58
44
  #
59
45
  # @return [Array<Chewy::Index>] a list of index classes
60
46
  def indices
61
- index_classes | type_classes.map(&:index)
62
- end
63
-
64
- # Returns type classes used for the request.
65
- # No strings/symbos included.
66
- #
67
- # @return [Array<Chewy::Type>] a list of types classes
68
- def types
69
- type_classes | (index_classes - type_classes.map(&:index)).flat_map(&:types)
47
+ index_classes
70
48
  end
71
49
 
72
50
  private
@@ -78,10 +56,7 @@ module Chewy
78
56
  def normalize(value)
79
57
  value ||= {}
80
58
 
81
- {
82
- indices: Array.wrap(value[:indices]).flatten.compact,
83
- types: Array.wrap(value[:types]).flatten.compact
84
- }
59
+ {indices: Array.wrap(value[:indices]).flatten.compact}
85
60
  end
86
61
 
87
62
  def index_classes
@@ -97,26 +72,6 @@ module Chewy
97
72
  def index_names
98
73
  indices.map(&:index_name) | index_identifiers.map(&:to_s)
99
74
  end
100
-
101
- def type_classes
102
- value[:types].select do |klass|
103
- klass.is_a?(Class) && klass < Chewy::Type
104
- end
105
- end
106
-
107
- def type_identifiers
108
- value[:types] - type_classes
109
- end
110
-
111
- def type_names
112
- type_names = types.map(&:type_name)
113
-
114
- if index_identifiers.blank? && type_identifiers.present?
115
- (type_names & type_identifiers.map(&:to_s)).presence || type_names
116
- else
117
- type_names | type_identifiers.map(&:to_s)
118
- end
119
- end
120
75
  end
121
76
  end
122
77
  end
@@ -0,0 +1,16 @@
1
+ require 'chewy/search/parameters/storage'
2
+
3
+ module Chewy
4
+ module Search
5
+ class Parameters
6
+ # Just a standard hash storage. Nothing to see here.
7
+ #
8
+ # @see Chewy::Search::Parameters::HashStorage
9
+ # @see Chewy::Search::Request#knn
10
+ # @see https://www.elastic.co/guide/en/elasticsearch/reference/current/knn-search.html
11
+ class Knn < Storage
12
+ include HashStorage
13
+ end
14
+ end
15
+ end
16
+ end
@@ -17,7 +17,7 @@ module Chewy
17
17
  # @param other_value [Object] any acceptable storage value
18
18
  # @return [Object] updated value
19
19
  def update!(other_value)
20
- value.merge!(normalize(other_value))
20
+ value.concat(normalize(other_value))
21
21
  end
22
22
 
23
23
  # Size requires specialized rendering logic, it should return
@@ -28,20 +28,7 @@ module Chewy
28
28
  def render
29
29
  return if value.blank?
30
30
 
31
- sort = value.map do |(field, options)|
32
- options ? {field => options} : field
33
- end
34
- {sort: sort}
35
- end
36
-
37
- # Comparison also reqires additional logic. Hashes are compared
38
- # orderlessly, but for `sort` parameter oder is important, so we
39
- # compare hash key collections additionally.
40
- #
41
- # @see Chewy::Search::Parameters::Storage#==
42
- # @return [true, false]
43
- def ==(other)
44
- super && value.keys == other.value.keys
31
+ {sort: value}
45
32
  end
46
33
 
47
34
  private
@@ -49,13 +36,13 @@ module Chewy
49
36
  def normalize(value)
50
37
  case value
51
38
  when Array
52
- value.each_with_object({}) do |sv, res|
53
- res.merge!(normalize(sv))
39
+ value.each_with_object([]) do |sv, res|
40
+ res.concat(normalize(sv))
54
41
  end
55
42
  when Hash
56
- value.stringify_keys
43
+ [value.stringify_keys]
57
44
  else
58
- value.present? ? {value.to_s => nil} : {}
45
+ value.present? ? [value.to_s] : []
59
46
  end
60
47
  end
61
48
  end
@@ -17,7 +17,11 @@ module Chewy
17
17
  # In case of hash, respective values are concatenated as well.
18
18
  #
19
19
  # @see Chewy::Search::Parameters::Storage#update!
20
- # @param other_value [true, false, {Symbol => Array<String, Symbol>, String, Symbol}, Array<String, Symbol>, String, Symbol] any acceptable storage value
20
+ # @param other_value
21
+ # [true, false, {
22
+ # Symbol => Array<String, Symbol>, String, Symbol},
23
+ # Array<String, Symbol>, String, Symbol
24
+ # ] any acceptable storage value
21
25
  # @return [{Symbol => Array<String>, true, false}] updated value
22
26
  def update!(other_value)
23
27
  new_value = normalize(other_value)
@@ -35,7 +35,7 @@ module Chewy
35
35
  # @param other [Chewy::Search::Parameters::Storage] any storage instance
36
36
  # @return [true, false] the result of comparision
37
37
  def ==(other)
38
- super || other.class == self.class && other.value == value
38
+ super || (other.class == self.class && other.value == value)
39
39
  end
40
40
 
41
41
  # Replaces current value with normalized provided one. Doesn't
@@ -0,0 +1,16 @@
1
+ require 'chewy/search/parameters/storage'
2
+
3
+ module Chewy
4
+ module Search
5
+ class Parameters
6
+ # Just a standard boolean storage, nothing to see here.
7
+ #
8
+ # @see Chewy::Search::Parameters::BoolStorage
9
+ # @see Chewy::Search::Request#track_total_hits
10
+ # @see https://www.elastic.co/guide/en/elasticsearch/reference/current/search-your-data.html#track-total-hits
11
+ class TrackTotalHits < Storage
12
+ include BoolStorage
13
+ end
14
+ end
15
+ end
16
+ end