chewy 6.0.0 → 7.5.1

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 +48 -0
  8. data/.rubocop.yml +16 -8
  9. data/.rubocop_todo.yml +110 -22
  10. data/CHANGELOG.md +385 -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 +494 -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 +58 -50
  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 +151 -0
  81. data/lib/chewy/strategy/delayed_sidekiq/worker.rb +52 -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 +14 -39
  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 +202 -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
@@ -0,0 +1,494 @@
1
+ require 'spec_helper'
2
+
3
+ SimpleComment = Class.new do
4
+ attr_reader :content, :comment_type, :commented_id, :updated_at, :id
5
+
6
+ def initialize(hash)
7
+ @id = hash['id']
8
+ @content = hash['content']
9
+ @comment_type = hash['comment_type']
10
+ @commented_id = hash['commented_id']
11
+ @updated_at = hash['updated_at']
12
+ end
13
+
14
+ def derived
15
+ "[derived] #{content}"
16
+ end
17
+ end
18
+
19
+ describe Chewy::Index::Import::BulkBuilder do
20
+ before { Chewy.massacre }
21
+
22
+ subject { described_class.new(index, to_index: to_index, delete: delete, fields: fields) }
23
+ let(:index) { CitiesIndex }
24
+ let(:to_index) { [] }
25
+ let(:delete) { [] }
26
+ let(:fields) { [] }
27
+
28
+ describe '#bulk_body' do
29
+ context 'simple bulk', :orm do
30
+ before do
31
+ stub_model(:city)
32
+ stub_index(:cities) do
33
+ index_scope City
34
+ field :name, :rating
35
+ end
36
+ end
37
+ let(:cities) { Array.new(3) { |i| City.create!(id: i + 1, name: "City#{i + 17}", rating: 42) } }
38
+
39
+ specify { expect(subject.bulk_body).to eq([]) }
40
+
41
+ context do
42
+ let(:to_index) { cities }
43
+ specify do
44
+ expect(subject.bulk_body).to eq([
45
+ {index: {_id: 1, data: {'name' => 'City17', 'rating' => 42}}},
46
+ {index: {_id: 2, data: {'name' => 'City18', 'rating' => 42}}},
47
+ {index: {_id: 3, data: {'name' => 'City19', 'rating' => 42}}}
48
+ ])
49
+ end
50
+ end
51
+
52
+ context do
53
+ let(:delete) { cities }
54
+ specify do
55
+ expect(subject.bulk_body).to eq([
56
+ {delete: {_id: 1}}, {delete: {_id: 2}}, {delete: {_id: 3}}
57
+ ])
58
+ end
59
+ end
60
+
61
+ context do
62
+ let(:to_index) { cities.first(2) }
63
+ let(:delete) { [cities.last] }
64
+ specify do
65
+ expect(subject).to receive(:data_for).with(cities.first).and_call_original
66
+ expect(subject).to receive(:data_for).with(cities.second).and_call_original
67
+ expect(subject.bulk_body).to eq([
68
+ {index: {_id: 1, data: {'name' => 'City17', 'rating' => 42}}},
69
+ {index: {_id: 2, data: {'name' => 'City18', 'rating' => 42}}},
70
+ {delete: {_id: 3}}
71
+ ])
72
+ end
73
+
74
+ context ':fields' do
75
+ let(:fields) { %w[name] }
76
+ specify do
77
+ expect(subject).to receive(:data_for).with(cities.first, fields: [:name]).and_call_original
78
+ expect(subject).to receive(:data_for).with(cities.second, fields: [:name]).and_call_original
79
+ expect(subject.bulk_body).to eq([
80
+ {update: {_id: 1, data: {doc: {'name' => 'City17'}}}},
81
+ {update: {_id: 2, data: {doc: {'name' => 'City18'}}}},
82
+ {delete: {_id: 3}}
83
+ ])
84
+ end
85
+ end
86
+ end
87
+ end
88
+
89
+ context 'custom id', :orm do
90
+ before do
91
+ stub_model(:city)
92
+ end
93
+
94
+ before do
95
+ stub_index(:cities) do
96
+ index_scope City
97
+ root id: -> { name } do
98
+ field :rating
99
+ end
100
+ end
101
+ end
102
+
103
+ let(:london) { City.create(id: 1, name: 'London', rating: 4) }
104
+
105
+ specify do
106
+ expect { CitiesIndex.import(london) }
107
+ .to update_index(CitiesIndex).and_reindex(london.name)
108
+ end
109
+
110
+ context 'indexing' do
111
+ let(:to_index) { [london] }
112
+
113
+ specify do
114
+ expect(subject.bulk_body).to eq([
115
+ {index: {_id: london.name, data: {'rating' => 4}}}
116
+ ])
117
+ end
118
+ end
119
+
120
+ context 'destroying' do
121
+ let(:delete) { [london] }
122
+
123
+ specify do
124
+ expect(subject.bulk_body).to eq([
125
+ {delete: {_id: london.name}}
126
+ ])
127
+ end
128
+ end
129
+ end
130
+
131
+ context 'crutches' do
132
+ before do
133
+ stub_index(:cities) do
134
+ crutch :names do |collection|
135
+ collection.to_h { |item| [item.id, "Name#{item.id}"] }
136
+ end
137
+
138
+ field :name, value: ->(o, c) { c.names[o.id] }
139
+ end
140
+ end
141
+
142
+ let(:to_index) { [double(id: 42)] }
143
+
144
+ specify do
145
+ expect(subject.bulk_body).to eq([
146
+ {index: {_id: 42, data: {'name' => 'Name42'}}}
147
+ ])
148
+ end
149
+
150
+ context 'witchcraft' do
151
+ before { CitiesIndex.witchcraft! }
152
+ specify do
153
+ expect(subject.bulk_body).to eq([
154
+ {index: {_id: 42, data: {'name' => 'Name42'}}}
155
+ ])
156
+ end
157
+ end
158
+ end
159
+
160
+ context 'empty ids' do
161
+ before do
162
+ stub_index(:cities) do
163
+ field :name
164
+ end
165
+ end
166
+
167
+ let(:to_index) { [{id: 1, name: 'Name0'}, double(id: '', name: 'Name1'), double(name: 'Name2')] }
168
+ let(:delete) { [double(id: '', name: 'Name3'), {name: 'Name4'}, '', 2] }
169
+
170
+ specify do
171
+ expect(subject.bulk_body).to eq([
172
+ {index: {_id: 1, data: {'name' => 'Name0'}}},
173
+ {index: {data: {'name' => 'Name1'}}},
174
+ {index: {data: {'name' => 'Name2'}}},
175
+ {delete: {_id: {'name' => 'Name4'}}},
176
+ {delete: {_id: 2}}
177
+ ])
178
+ end
179
+
180
+ context do
181
+ let(:fields) { %w[name] }
182
+
183
+ specify do
184
+ expect(subject.bulk_body).to eq([
185
+ {update: {_id: 1, data: {doc: {'name' => 'Name0'}}}},
186
+ {delete: {_id: {'name' => 'Name4'}}},
187
+ {delete: {_id: 2}}
188
+ ])
189
+ end
190
+ end
191
+ end
192
+
193
+ context 'with parents' do
194
+ let(:index) { CommentsIndex }
195
+ before do
196
+ stub_model(:comment)
197
+ stub_index(:comments) do
198
+ index_scope Comment
199
+
200
+ crutch :content_with_crutches do |collection| # collection here is a current batch of products
201
+ collection.to_h { |comment| [comment.id, "[crutches] #{comment.content}"] }
202
+ end
203
+
204
+ field :content
205
+ field :content_with_crutches, value: ->(comment, crutches) { crutches.content_with_crutches[comment.id] }
206
+ field :comment_type, type: :join, relations: {question: %i[answer comment], answer: :vote, vote: :subvote}, join: {type: :comment_type, id: :commented_id}
207
+ end
208
+ end
209
+
210
+ let!(:existing_comments) do
211
+ [
212
+ Comment.create!(id: 1, content: 'Where is Nemo?', comment_type: :question),
213
+ Comment.create!(id: 2, content: 'Here.', comment_type: :answer, commented_id: 1),
214
+ Comment.create!(id: 31, content: 'What is the best programming language?', comment_type: :question)
215
+ ]
216
+ end
217
+
218
+ def do_raw_index_comment(options:, data:)
219
+ CommentsIndex.client.index(options.merge(index: 'comments', type: '_doc', refresh: true, body: data))
220
+ end
221
+
222
+ def raw_index_comment(comment)
223
+ options = {id: comment.id, routing: root(comment).id}
224
+ comment_type = comment.commented_id.present? ? {name: comment.comment_type, parent: comment.commented_id} : comment.comment_type
225
+ do_raw_index_comment(
226
+ options: options,
227
+ data: {content: comment.content, comment_type: comment_type}
228
+ )
229
+ end
230
+
231
+ def root(comment)
232
+ current = comment
233
+ # slow, but it's OK, as we don't have too deep trees
234
+ current = Comment.find(current.commented_id) while current.commented_id
235
+ current
236
+ end
237
+
238
+ before do
239
+ CommentsIndex.reset! # initialize index
240
+ end
241
+
242
+ let(:comments) do
243
+ [
244
+ Comment.create!(id: 3, content: 'There!', comment_type: :answer, commented_id: 1),
245
+ Comment.create!(id: 4, content: 'Yes, he is here.', comment_type: :vote, commented_id: 2),
246
+
247
+ Comment.create!(id: 11, content: 'What is the sense of the universe?', comment_type: :question),
248
+ Comment.create!(id: 12, content: 'I don\'t know.', comment_type: :answer, commented_id: 11),
249
+ Comment.create!(id: 13, content: '42', comment_type: :answer, commented_id: 11),
250
+ Comment.create!(id: 14, content: 'I think that 42 is a correct answer', comment_type: :vote, commented_id: 13),
251
+
252
+ Comment.create!(id: 21, content: 'How are you?', comment_type: :question),
253
+
254
+ Comment.create!(id: 32, content: 'Ruby', comment_type: :answer, commented_id: 31)
255
+ ]
256
+ end
257
+
258
+ context 'when indexing a single object' do
259
+ let(:to_index) { [comments[0]] }
260
+
261
+ specify do
262
+ expect(subject.bulk_body).to eq([
263
+ {index: {_id: 3, routing: '1', data: {'content' => 'There!', 'content_with_crutches' => '[crutches] There!', 'comment_type' => {'name' => 'answer', 'parent' => 1}}}}
264
+ ])
265
+ end
266
+ end
267
+
268
+ context 'with raw import' do
269
+ before do
270
+ stub_index(:comments) do
271
+ index_scope Comment
272
+ default_import_options raw_import: ->(hash) { SimpleComment.new(hash) }
273
+
274
+ crutch :content_with_crutches do |collection| # collection here is a current batch of products
275
+ collection.to_h { |comment| [comment.id, "[crutches] #{comment.content}"] }
276
+ end
277
+
278
+ field :content
279
+ field :content_with_crutches, value: ->(comment, crutches) { crutches.content_with_crutches[comment.id] }
280
+ field :derived
281
+ field :comment_type, type: :join, relations: {question: %i[answer comment], answer: :vote, vote: :subvote}, join: {type: :comment_type, id: :commented_id}
282
+ end
283
+ end
284
+
285
+ let(:to_index) { [comments[0]].map { |c| SimpleComment.new(c.attributes) } } # id: 3
286
+ let(:delete) { [existing_comments[0]].map { |c| c } } # id: 1
287
+
288
+ specify do
289
+ expected_data = {'content' => 'There!', 'content_with_crutches' => '[crutches] There!', 'derived' => '[derived] There!', 'comment_type' => {'name' => 'answer', 'parent' => 1}}
290
+ expect(subject.bulk_body).to eq([
291
+ {index: {_id: 3, routing: '1', data: expected_data}},
292
+ {delete: {_id: 1, routing: '1'}}
293
+ ])
294
+ end
295
+ end
296
+
297
+ context 'when switching parents' do
298
+ let(:switching_parent_comment) { comments[0].tap { |c| c.update!(commented_id: 31) } } # id: 3
299
+ let(:removing_parent_comment) { comments[1].tap { |c| c.update!(commented_id: nil, comment_type: nil) } } # id: 4
300
+ let(:converting_to_parent_comment) { comments[3].tap { |c| c.update!(commented_id: nil, comment_type: :question) } } # id: 12
301
+ let(:converting_to_child_comment) { comments[6].tap { |c| c.update!(commented_id: 1, comment_type: :answer) } } # id: 21
302
+ let(:fields) { %w[commented_id comment_type] }
303
+
304
+ let(:to_index) { [switching_parent_comment, removing_parent_comment, converting_to_parent_comment, converting_to_child_comment] }
305
+
306
+ before do
307
+ existing_comments.each { |c| raw_index_comment(c) }
308
+ comments.each { |c| raw_index_comment(c) }
309
+ end
310
+
311
+ specify do
312
+ expect(subject.bulk_body).to eq([
313
+ {delete: {_id: 3, routing: '1', parent: 1}},
314
+ {index: {_id: 3, routing: '31', data: {'content' => 'There!', 'content_with_crutches' => '[crutches] There!', 'comment_type' => {'name' => 'answer', 'parent' => 31}}}},
315
+ {delete: {_id: 4, routing: '1', parent: 2}},
316
+ {index: {_id: 4, routing: '4', data: {'content' => 'Yes, he is here.', 'content_with_crutches' => '[crutches] Yes, he is here.', 'comment_type' => nil}}},
317
+ {delete: {_id: 12, routing: '11', parent: 11}},
318
+ {index: {_id: 12, routing: '12', data: {'content' => 'I don\'t know.', 'content_with_crutches' => '[crutches] I don\'t know.', 'comment_type' => 'question'}}},
319
+ {delete: {_id: 21, routing: '21'}},
320
+ {index: {_id: 21, routing: '1', data: {'content' => 'How are you?', 'content_with_crutches' => '[crutches] How are you?', 'comment_type' => {'name' => 'answer', 'parent' => 1}}}}
321
+ ])
322
+ end
323
+ end
324
+
325
+ context 'when indexing with grandparents' do
326
+ let(:comments) do
327
+ [
328
+ Comment.create!(id: 3, content: 'Yes, he is here.', comment_type: :vote, commented_id: 2),
329
+ Comment.create!(id: 4, content: 'What?', comment_type: :subvote, commented_id: 3)
330
+ ]
331
+ end
332
+ let(:to_index) { comments }
333
+
334
+ before do
335
+ existing_comments.each { |c| raw_index_comment(c) }
336
+ end
337
+
338
+ specify do
339
+ expected_data3 = {'content' => 'Yes, he is here.', 'content_with_crutches' => '[crutches] Yes, he is here.', 'comment_type' => {'name' => 'vote', 'parent' => 2}}
340
+ expected_data4 = {'content' => 'What?', 'content_with_crutches' => '[crutches] What?', 'comment_type' => {'name' => 'subvote', 'parent' => 3}}
341
+ expect(subject.bulk_body).to eq([
342
+ {index: {_id: 3, routing: '1', data: expected_data3}},
343
+ {index: {_id: 4, routing: '1', data: expected_data4}}
344
+ ])
345
+ end
346
+ end
347
+
348
+ context 'when switching grandparents' do
349
+ let(:comments) do
350
+ [
351
+ Comment.create!(id: 3, content: 'Yes, he is here.', comment_type: :vote, commented_id: 2),
352
+ Comment.create!(id: 4, content: 'What?', comment_type: :subvote, commented_id: 3)
353
+ ]
354
+ end
355
+ let(:switching_parent_comment) { existing_comments[1].tap { |c| c.update!(commented_id: 31) } } # id: 2
356
+ let(:fields) { %w[commented_id comment_type] }
357
+ let(:to_index) { [switching_parent_comment] }
358
+
359
+ before do
360
+ existing_comments.each { |c| raw_index_comment(c) }
361
+ comments.each { |c| raw_index_comment(c) }
362
+ end
363
+
364
+ it 'reindexes children and grandchildren' do
365
+ expected_data2 = {'content' => 'Here.', 'content_with_crutches' => '[crutches] Here.', 'comment_type' => {'name' => 'answer', 'parent' => 31}}
366
+ expected_data3 = {'content' => 'Yes, he is here.', 'content_with_crutches' => '[crutches] Yes, he is here.', 'comment_type' => {'name' => 'vote', 'parent' => 2}}
367
+ expected_data4 = {'content' => 'What?', 'content_with_crutches' => '[crutches] What?', 'comment_type' => {'name' => 'subvote', 'parent' => 3}}
368
+ expect(subject.bulk_body).to eq([
369
+ {delete: {_id: 2, routing: '1', parent: 1}},
370
+ {index: {_id: 2, routing: '31', data: expected_data2}},
371
+ {delete: {_id: 3, routing: '1', parent: 2}},
372
+ {index: {_id: 3, routing: '31', data: expected_data3}},
373
+ {delete: {_id: 4, routing: '1', parent: 3}},
374
+ {index: {_id: 4, routing: '31', data: expected_data4}}
375
+ ])
376
+ end
377
+ end
378
+
379
+ describe 'when removing parents or grandparents' do
380
+ let(:comments) do
381
+ [
382
+ Comment.create!(id: 3, content: 'Yes, he is here.', comment_type: :vote, commented_id: 2),
383
+ Comment.create!(id: 4, content: 'What?', comment_type: :subvote, commented_id: 3)
384
+ ]
385
+ end
386
+ let(:delete) { [existing_comments[0]] } # id: 1
387
+
388
+ before do
389
+ existing_comments.each { |c| raw_index_comment(c) }
390
+ comments.each { |c| raw_index_comment(c) }
391
+ end
392
+
393
+ it 'does not remove all descendants' do
394
+ expect(subject.bulk_body).to eq([
395
+ {delete: {_id: 1, routing: '1'}}
396
+ ])
397
+ end
398
+ end
399
+
400
+ context 'when indexing' do
401
+ let(:to_index) { comments }
402
+
403
+ specify do
404
+ expected_data3 = {'content' => 'There!', 'content_with_crutches' => '[crutches] There!', 'comment_type' => {'name' => 'answer', 'parent' => 1}}
405
+ expected_data4 = {'content' => 'Yes, he is here.', 'content_with_crutches' => '[crutches] Yes, he is here.', 'comment_type' => {'name' => 'vote', 'parent' => 2}}
406
+
407
+ expected_data11 = {'content' => 'What is the sense of the universe?', 'content_with_crutches' => '[crutches] What is the sense of the universe?', 'comment_type' => 'question'}
408
+ expected_data12 = {'content' => 'I don\'t know.', 'content_with_crutches' => '[crutches] I don\'t know.', 'comment_type' => {'name' => 'answer', 'parent' => 11}}
409
+ expected_data13 = {'content' => '42', 'content_with_crutches' => '[crutches] 42', 'comment_type' => {'name' => 'answer', 'parent' => 11}}
410
+ expected_data14 = {'content' => 'I think that 42 is a correct answer', 'content_with_crutches' => '[crutches] I think that 42 is a correct answer',
411
+ 'comment_type' => {'name' => 'vote', 'parent' => 13}}
412
+
413
+ expected_data21 = {'content' => 'How are you?', 'content_with_crutches' => '[crutches] How are you?', 'comment_type' => 'question'}
414
+
415
+ expected_data32 = {'content' => 'Ruby', 'content_with_crutches' => '[crutches] Ruby', 'comment_type' => {'name' => 'answer', 'parent' => 31}}
416
+
417
+ expect(subject.bulk_body).to eq([
418
+ {index: {_id: 3, routing: '1', data: expected_data3}},
419
+ {index: {_id: 4, routing: '1', data: expected_data4}},
420
+
421
+ {index: {_id: 11, routing: '11', data: expected_data11}},
422
+ {index: {_id: 12, routing: '11', data: expected_data12}},
423
+ {index: {_id: 13, routing: '11', data: expected_data13}},
424
+ {index: {_id: 14, routing: '11', data: expected_data14}},
425
+
426
+ {index: {_id: 21, routing: '21', data: expected_data21}},
427
+
428
+ {index: {_id: 32, routing: '31', data: expected_data32}}
429
+ ])
430
+ end
431
+ end
432
+
433
+ context 'when deleting' do
434
+ before do
435
+ existing_comments.each { |c| raw_index_comment(c) }
436
+ comments.each { |c| raw_index_comment(c) }
437
+ end
438
+
439
+ let(:delete) { comments }
440
+ specify do
441
+ expect(subject.bulk_body).to eq([
442
+ {delete: {_id: 3, routing: '1', parent: 1}},
443
+ {delete: {_id: 4, routing: '1', parent: 2}},
444
+
445
+ {delete: {_id: 11, routing: '11'}},
446
+ {delete: {_id: 12, routing: '11', parent: 11}},
447
+ {delete: {_id: 13, routing: '11', parent: 11}},
448
+ {delete: {_id: 14, routing: '11', parent: 13}},
449
+
450
+ {delete: {_id: 21, routing: '21'}},
451
+
452
+ {delete: {_id: 32, routing: '31', parent: 31}}
453
+ ])
454
+ end
455
+ end
456
+
457
+ context 'when updating' do
458
+ before do
459
+ comments.each { |c| raw_index_comment(c) }
460
+ end
461
+ let(:fields) { %w[content] }
462
+ let(:to_index) { comments }
463
+ specify do
464
+ expect(subject.bulk_body).to eq([
465
+ {update: {_id: 3, routing: '1', data: {doc: {'content' => comments[0].content}}}},
466
+ {update: {_id: 4, routing: '1', data: {doc: {'content' => comments[1].content}}}},
467
+
468
+ {update: {_id: 11, routing: '11', data: {doc: {'content' => comments[2].content}}}},
469
+ {update: {_id: 12, routing: '11', data: {doc: {'content' => comments[3].content}}}},
470
+ {update: {_id: 13, routing: '11', data: {doc: {'content' => comments[4].content}}}},
471
+ {update: {_id: 14, routing: '11', data: {doc: {'content' => comments[5].content}}}},
472
+
473
+ {update: {_id: 21, routing: '21', data: {doc: {'content' => comments[6].content}}}},
474
+
475
+ {update: {_id: 32, routing: '31', data: {doc: {'content' => comments[7].content}}}}
476
+ ])
477
+ end
478
+ end
479
+ end
480
+ end
481
+
482
+ describe '#index_objects_by_id' do
483
+ before do
484
+ stub_index(:cities) do
485
+ field :name
486
+ end
487
+ end
488
+
489
+ let(:to_index) { [double(id: 1), double(id: 2), double(id: ''), double] }
490
+ let(:delete) { [double(id: 3)] }
491
+
492
+ specify { expect(subject.index_objects_by_id).to eq('1' => to_index.first, '2' => to_index.second) }
493
+ end
494
+ end
@@ -1,13 +1,13 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe Chewy::Type::Import::BulkRequest do
3
+ describe Chewy::Index::Import::BulkRequest do
4
4
  before { Chewy.massacre }
5
5
 
6
- subject { described_class.new(type, suffix: suffix, bulk_size: bulk_size, **bulk_options) }
6
+ subject { described_class.new(index, suffix: suffix, bulk_size: bulk_size, **bulk_options) }
7
7
  let(:suffix) {}
8
8
  let(:bulk_size) {}
9
9
  let(:bulk_options) { {} }
10
- let(:type) { PlacesIndex::City }
10
+ let(:index) { PlacesIndex }
11
11
 
12
12
  describe '#initialize' do
13
13
  specify { expect { described_class.new(nil, bulk_size: 100) }.to raise_error(ArgumentError) }
@@ -18,9 +18,8 @@ describe Chewy::Type::Import::BulkRequest do
18
18
  before do
19
19
  stub_model(:city)
20
20
  stub_index(:places) do
21
- define_type City do
22
- field :name
23
- end
21
+ index_scope City
22
+ field :name
24
23
  end
25
24
  end
26
25
 
@@ -32,7 +31,6 @@ describe Chewy::Type::Import::BulkRequest do
32
31
  specify do
33
32
  expect(Chewy.client).to receive(:bulk).with(
34
33
  index: 'places',
35
- type: 'city',
36
34
  body: [{index: {id: 42, data: {name: 'Name'}}}]
37
35
  )
38
36
  subject.perform([{index: {id: 42, data: {name: 'Name'}}}])
@@ -44,7 +42,6 @@ describe Chewy::Type::Import::BulkRequest do
44
42
  specify do
45
43
  expect(Chewy.client).to receive(:bulk).with(
46
44
  index: 'places_suffix',
47
- type: 'city',
48
45
  body: [{index: {id: 42, data: {name: 'Name'}}}]
49
46
  )
50
47
  subject.perform([{index: {id: 42, data: {name: 'Name'}}}])
@@ -57,7 +54,6 @@ describe Chewy::Type::Import::BulkRequest do
57
54
  specify do
58
55
  expect(Chewy.client).to receive(:bulk).with(
59
56
  index: 'places',
60
- type: 'city',
61
57
  body: "{\"index\":{\"id\":42}}\n{\"name\":\"#{'Name' * 10}\"}\n{\"index\":{\"id\":43}}\n{\"name\":\"#{'Shame' * 10}\"}\n"
62
58
  )
63
59
  subject.perform([
@@ -69,17 +65,14 @@ describe Chewy::Type::Import::BulkRequest do
69
65
  specify do
70
66
  expect(Chewy.client).to receive(:bulk).with(
71
67
  index: 'places',
72
- type: 'city',
73
68
  body: "{\"index\":{\"id\":42}}\n{\"name\":\"#{'Name' * 30}\"}\n"
74
69
  )
75
70
  expect(Chewy.client).to receive(:bulk).with(
76
71
  index: 'places',
77
- type: 'city',
78
72
  body: "{\"index\":{\"id\":43}}\n{\"name\":\"#{'Shame' * 100}\"}\n"
79
73
  )
80
74
  expect(Chewy.client).to receive(:bulk).with(
81
75
  index: 'places',
82
- type: 'city',
83
76
  body: "{\"index\":{\"id\":44}}\n{\"name\":\"#{'Blame' * 30}\"}\n"
84
77
  )
85
78
  subject.perform([
@@ -1,13 +1,11 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe Chewy::Type::Import::JournalBuilder, :orm do
3
+ describe Chewy::Index::Import::JournalBuilder, :orm do
4
4
  before do
5
5
  stub_model(:country)
6
- stub_index 'namespace/cities' do
7
- define_type :city
8
- end
6
+ stub_index 'namespace/cities'
9
7
  stub_index 'namespace/countries' do
10
- define_type Country
8
+ index_scope Country
11
9
  end
12
10
  Timecop.freeze(time)
13
11
  end
@@ -15,24 +13,22 @@ describe Chewy::Type::Import::JournalBuilder, :orm do
15
13
 
16
14
  let(:time) { Time.parse('2017-07-14 12:00Z') }
17
15
 
18
- let(:type) { Namespace::CitiesIndex::City }
19
- let(:index) { [] }
16
+ let(:index) { Namespace::CitiesIndex }
17
+ let(:to_index) { [] }
20
18
  let(:delete) { [] }
21
- subject { described_class.new(type, index: index, delete: delete) }
19
+ subject { described_class.new(index, to_index: to_index, delete: delete) }
22
20
 
23
21
  describe '#bulk_body' do
24
22
  specify { expect(subject.bulk_body).to eq([]) }
25
23
 
26
24
  context do
27
- let(:index) { [{id: 1, name: 'City'}] }
25
+ let(:to_index) { [{id: 1, name: 'City'}] }
28
26
  specify do
29
27
  expect(subject.bulk_body).to eq([{
30
28
  index: {
31
29
  _index: 'chewy_journal',
32
- _type: 'journal',
33
30
  data: {
34
31
  'index_name' => 'namespace/cities',
35
- 'type_name' => 'city',
36
32
  'action' => 'index',
37
33
  'references' => [Base64.encode64('{"id":1,"name":"City"}')],
38
34
  'created_at' => time.as_json
@@ -48,10 +44,8 @@ describe Chewy::Type::Import::JournalBuilder, :orm do
48
44
  expect(subject.bulk_body).to eq([{
49
45
  index: {
50
46
  _index: 'chewy_journal',
51
- _type: 'journal',
52
47
  data: {
53
48
  'index_name' => 'namespace/cities',
54
- 'type_name' => 'city',
55
49
  'action' => 'delete',
56
50
  'references' => [Base64.encode64('{"id":1,"name":"City"}')],
57
51
  'created_at' => time.as_json
@@ -62,17 +56,15 @@ describe Chewy::Type::Import::JournalBuilder, :orm do
62
56
  end
63
57
 
64
58
  context do
65
- let(:type) { Namespace::CountriesIndex::Country }
66
- let(:index) { [Country.new(id: 1, name: 'City')] }
59
+ let(:index) { Namespace::CountriesIndex }
60
+ let(:to_index) { [Country.new(id: 1, name: 'City')] }
67
61
  let(:delete) { [Country.new(id: 2, name: 'City')] }
68
62
  specify do
69
63
  expect(subject.bulk_body).to eq([{
70
64
  index: {
71
65
  _index: 'chewy_journal',
72
- _type: 'journal',
73
66
  data: {
74
67
  'index_name' => 'namespace/countries',
75
- 'type_name' => 'country',
76
68
  'action' => 'index',
77
69
  'references' => [Base64.encode64('1')],
78
70
  'created_at' => time.as_json
@@ -81,10 +73,8 @@ describe Chewy::Type::Import::JournalBuilder, :orm do
81
73
  }, {
82
74
  index: {
83
75
  _index: 'chewy_journal',
84
- _type: 'journal',
85
76
  data: {
86
77
  'index_name' => 'namespace/countries',
87
- 'type_name' => 'country',
88
78
  'action' => 'delete',
89
79
  'references' => [Base64.encode64('2')],
90
80
  'created_at' => time.as_json