chewy 7.2.3 → 7.2.4
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.
- checksums.yaml +4 -4
- data/.github/workflows/ruby.yml +12 -4
- data/CHANGELOG.md +13 -1
- data/Gemfile +1 -0
- data/README.md +20 -3
- data/gemfiles/rails.7.0.activerecord.gemfile +13 -0
- data/lib/chewy/errors.rb +6 -0
- data/lib/chewy/fields/base.rb +68 -12
- data/lib/chewy/fields/root.rb +3 -11
- data/lib/chewy/index/actions.rb +3 -3
- data/lib/chewy/index/adapter/active_record.rb +5 -0
- data/lib/chewy/index/adapter/object.rb +3 -3
- data/lib/chewy/index/adapter/orm.rb +8 -6
- data/lib/chewy/index/import/bulk_builder.rb +219 -31
- data/lib/chewy/index/import/bulk_request.rb +1 -1
- data/lib/chewy/index/import.rb +3 -4
- data/lib/chewy/index/mapping.rb +2 -2
- data/lib/chewy/index/observe.rb +1 -1
- data/lib/chewy/rspec/update_index.rb +1 -1
- data/lib/chewy/search/request.rb +2 -2
- data/lib/chewy/search.rb +1 -1
- data/lib/chewy/strategy/active_job.rb +1 -1
- data/lib/chewy/strategy/sidekiq.rb +1 -1
- data/lib/chewy/version.rb +1 -1
- data/lib/chewy.rb +2 -2
- data/lib/tasks/chewy.rake +1 -1
- data/spec/chewy/fields/base_spec.rb +38 -18
- data/spec/chewy/index/adapter/active_record_spec.rb +26 -0
- data/spec/chewy/index/import/bulk_builder_spec.rb +304 -0
- data/spec/chewy/index/import/routine_spec.rb +3 -3
- data/spec/chewy/index/import_spec.rb +40 -0
- data/spec/support/active_record.rb +8 -1
- metadata +4 -5
- data/lib/chewy/backports/deep_dup.rb +0 -46
- data/lib/chewy/backports/duplicable.rb +0 -91
@@ -1,5 +1,21 @@
|
|
1
1
|
require 'spec_helper'
|
2
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
|
+
|
3
19
|
describe Chewy::Index::Import::BulkBuilder do
|
4
20
|
before { Chewy.massacre }
|
5
21
|
|
@@ -169,6 +185,294 @@ describe Chewy::Index::Import::BulkBuilder do
|
|
169
185
|
end
|
170
186
|
end
|
171
187
|
end
|
188
|
+
|
189
|
+
context 'with parents' do
|
190
|
+
let(:index) { CommentsIndex }
|
191
|
+
before do
|
192
|
+
stub_model(:comment)
|
193
|
+
stub_index(:comments) do
|
194
|
+
index_scope Comment
|
195
|
+
|
196
|
+
crutch :content_with_crutches do |collection| # collection here is a current batch of products
|
197
|
+
collection.map { |comment| [comment.id, "[crutches] #{comment.content}"] }.to_h
|
198
|
+
end
|
199
|
+
|
200
|
+
field :content
|
201
|
+
field :content_with_crutches, value: ->(comment, crutches) { crutches.content_with_crutches[comment.id] }
|
202
|
+
field :comment_type, type: :join, relations: {question: %i[answer comment], answer: :vote, vote: :subvote}, join: {type: :comment_type, id: :commented_id}
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
let!(:existing_comments) do
|
207
|
+
[
|
208
|
+
Comment.create!(id: 1, content: 'Where is Nemo?', comment_type: :question),
|
209
|
+
Comment.create!(id: 2, content: 'Here.', comment_type: :answer, commented_id: 1),
|
210
|
+
Comment.create!(id: 31, content: 'What is the best programming language?', comment_type: :question)
|
211
|
+
]
|
212
|
+
end
|
213
|
+
|
214
|
+
def do_raw_index_comment(options:, data:)
|
215
|
+
CommentsIndex.client.index(options.merge(index: 'comments', type: '_doc', refresh: true, body: data))
|
216
|
+
end
|
217
|
+
|
218
|
+
def raw_index_comment(comment)
|
219
|
+
options = {id: comment.id, routing: root(comment).id}
|
220
|
+
comment_type = comment.commented_id.present? ? {name: comment.comment_type, parent: comment.commented_id} : comment.comment_type
|
221
|
+
do_raw_index_comment(
|
222
|
+
options: options,
|
223
|
+
data: {content: comment.content, comment_type: comment_type}
|
224
|
+
)
|
225
|
+
end
|
226
|
+
|
227
|
+
def root(comment)
|
228
|
+
current = comment
|
229
|
+
# slow, but it's OK, as we don't have too deep trees
|
230
|
+
current = Comment.find(current.commented_id) while current.commented_id
|
231
|
+
current
|
232
|
+
end
|
233
|
+
|
234
|
+
before do
|
235
|
+
CommentsIndex.reset! # initialize index
|
236
|
+
end
|
237
|
+
|
238
|
+
let(:comments) do
|
239
|
+
[
|
240
|
+
Comment.create!(id: 3, content: 'There!', comment_type: :answer, commented_id: 1),
|
241
|
+
Comment.create!(id: 4, content: 'Yes, he is here.', comment_type: :vote, commented_id: 2),
|
242
|
+
|
243
|
+
Comment.create!(id: 11, content: 'What is the sense of the universe?', comment_type: :question),
|
244
|
+
Comment.create!(id: 12, content: 'I don\'t know.', comment_type: :answer, commented_id: 11),
|
245
|
+
Comment.create!(id: 13, content: '42', comment_type: :answer, commented_id: 11),
|
246
|
+
Comment.create!(id: 14, content: 'I think that 42 is a correct answer', comment_type: :vote, commented_id: 13),
|
247
|
+
|
248
|
+
Comment.create!(id: 21, content: 'How are you?', comment_type: :question),
|
249
|
+
|
250
|
+
Comment.create!(id: 32, content: 'Ruby', comment_type: :answer, commented_id: 31)
|
251
|
+
]
|
252
|
+
end
|
253
|
+
|
254
|
+
context 'when indexing a single object' do
|
255
|
+
let(:to_index) { [comments[0]] }
|
256
|
+
|
257
|
+
specify do
|
258
|
+
expect(subject.bulk_body).to eq([
|
259
|
+
{index: {_id: 3, routing: '1', data: {'content' => 'There!', 'content_with_crutches' => '[crutches] There!', 'comment_type' => {'name' => 'answer', 'parent' => 1}}}}
|
260
|
+
])
|
261
|
+
end
|
262
|
+
end
|
263
|
+
|
264
|
+
context 'with raw import' do
|
265
|
+
before do
|
266
|
+
stub_index(:comments) do
|
267
|
+
index_scope Comment
|
268
|
+
default_import_options raw_import: ->(hash) { SimpleComment.new(hash) }
|
269
|
+
|
270
|
+
crutch :content_with_crutches do |collection| # collection here is a current batch of products
|
271
|
+
collection.map { |comment| [comment.id, "[crutches] #{comment.content}"] }.to_h
|
272
|
+
end
|
273
|
+
|
274
|
+
field :content
|
275
|
+
field :content_with_crutches, value: ->(comment, crutches) { crutches.content_with_crutches[comment.id] }
|
276
|
+
field :derived
|
277
|
+
field :comment_type, type: :join, relations: {question: %i[answer comment], answer: :vote, vote: :subvote}, join: {type: :comment_type, id: :commented_id}
|
278
|
+
end
|
279
|
+
end
|
280
|
+
|
281
|
+
let(:to_index) { [comments[0]].map { |c| SimpleComment.new(c.attributes) } } # id: 3
|
282
|
+
let(:delete) { [existing_comments[0]].map { |c| c } } # id: 1
|
283
|
+
|
284
|
+
specify do
|
285
|
+
expected_data = {'content' => 'There!', 'content_with_crutches' => '[crutches] There!', 'derived' => '[derived] There!', 'comment_type' => {'name' => 'answer', 'parent' => 1}}
|
286
|
+
expect(subject.bulk_body).to eq([
|
287
|
+
{index: {_id: 3, routing: '1', data: expected_data}},
|
288
|
+
{delete: {_id: 1, routing: '1'}}
|
289
|
+
])
|
290
|
+
end
|
291
|
+
end
|
292
|
+
|
293
|
+
context 'when switching parents' do
|
294
|
+
let(:switching_parent_comment) { comments[0].tap { |c| c.update!(commented_id: 31) } } # id: 3
|
295
|
+
let(:removing_parent_comment) { comments[1].tap { |c| c.update!(commented_id: nil, comment_type: nil) } } # id: 4
|
296
|
+
let(:converting_to_parent_comment) { comments[3].tap { |c| c.update!(commented_id: nil, comment_type: :question) } } # id: 12
|
297
|
+
let(:converting_to_child_comment) { comments[6].tap { |c| c.update!(commented_id: 1, comment_type: :answer) } } # id: 21
|
298
|
+
let(:fields) { %w[commented_id comment_type] }
|
299
|
+
|
300
|
+
let(:to_index) { [switching_parent_comment, removing_parent_comment, converting_to_parent_comment, converting_to_child_comment] }
|
301
|
+
|
302
|
+
before do
|
303
|
+
existing_comments.each { |c| raw_index_comment(c) }
|
304
|
+
comments.each { |c| raw_index_comment(c) }
|
305
|
+
end
|
306
|
+
|
307
|
+
specify do
|
308
|
+
expect(subject.bulk_body).to eq([
|
309
|
+
{delete: {_id: 3, routing: '1', parent: 1}},
|
310
|
+
{index: {_id: 3, routing: '31', data: {'content' => 'There!', 'content_with_crutches' => '[crutches] There!', 'comment_type' => {'name' => 'answer', 'parent' => 31}}}},
|
311
|
+
{delete: {_id: 4, routing: '1', parent: 2}},
|
312
|
+
{index: {_id: 4, routing: '4', data: {'content' => 'Yes, he is here.', 'content_with_crutches' => '[crutches] Yes, he is here.', 'comment_type' => nil}}},
|
313
|
+
{delete: {_id: 12, routing: '11', parent: 11}},
|
314
|
+
{index: {_id: 12, routing: '12', data: {'content' => 'I don\'t know.', 'content_with_crutches' => '[crutches] I don\'t know.', 'comment_type' => 'question'}}},
|
315
|
+
{delete: {_id: 21, routing: '21'}},
|
316
|
+
{index: {_id: 21, routing: '1', data: {'content' => 'How are you?', 'content_with_crutches' => '[crutches] How are you?', 'comment_type' => {'name' => 'answer', 'parent' => 1}}}}
|
317
|
+
])
|
318
|
+
end
|
319
|
+
end
|
320
|
+
|
321
|
+
context 'when indexing with grandparents' do
|
322
|
+
let(:comments) do
|
323
|
+
[
|
324
|
+
Comment.create!(id: 3, content: 'Yes, he is here.', comment_type: :vote, commented_id: 2),
|
325
|
+
Comment.create!(id: 4, content: 'What?', comment_type: :subvote, commented_id: 3)
|
326
|
+
]
|
327
|
+
end
|
328
|
+
let(:to_index) { comments }
|
329
|
+
|
330
|
+
before do
|
331
|
+
existing_comments.each { |c| raw_index_comment(c) }
|
332
|
+
end
|
333
|
+
|
334
|
+
specify do
|
335
|
+
expected_data3 = {'content' => 'Yes, he is here.', 'content_with_crutches' => '[crutches] Yes, he is here.', 'comment_type' => {'name' => 'vote', 'parent' => 2}}
|
336
|
+
expected_data4 = {'content' => 'What?', 'content_with_crutches' => '[crutches] What?', 'comment_type' => {'name' => 'subvote', 'parent' => 3}}
|
337
|
+
expect(subject.bulk_body).to eq([
|
338
|
+
{index: {_id: 3, routing: '1', data: expected_data3}},
|
339
|
+
{index: {_id: 4, routing: '1', data: expected_data4}}
|
340
|
+
])
|
341
|
+
end
|
342
|
+
end
|
343
|
+
|
344
|
+
context 'when switching grandparents' do
|
345
|
+
let(:comments) do
|
346
|
+
[
|
347
|
+
Comment.create!(id: 3, content: 'Yes, he is here.', comment_type: :vote, commented_id: 2),
|
348
|
+
Comment.create!(id: 4, content: 'What?', comment_type: :subvote, commented_id: 3)
|
349
|
+
]
|
350
|
+
end
|
351
|
+
let(:switching_parent_comment) { existing_comments[1].tap { |c| c.update!(commented_id: 31) } } # id: 2
|
352
|
+
let(:fields) { %w[commented_id comment_type] }
|
353
|
+
let(:to_index) { [switching_parent_comment] }
|
354
|
+
|
355
|
+
before do
|
356
|
+
existing_comments.each { |c| raw_index_comment(c) }
|
357
|
+
comments.each { |c| raw_index_comment(c) }
|
358
|
+
end
|
359
|
+
|
360
|
+
it 'reindexes children and grandchildren' do
|
361
|
+
expected_data2 = {'content' => 'Here.', 'content_with_crutches' => '[crutches] Here.', 'comment_type' => {'name' => 'answer', 'parent' => 31}}
|
362
|
+
expected_data3 = {'content' => 'Yes, he is here.', 'content_with_crutches' => '[crutches] Yes, he is here.', 'comment_type' => {'name' => 'vote', 'parent' => 2}}
|
363
|
+
expected_data4 = {'content' => 'What?', 'content_with_crutches' => '[crutches] What?', 'comment_type' => {'name' => 'subvote', 'parent' => 3}}
|
364
|
+
expect(subject.bulk_body).to eq([
|
365
|
+
{delete: {_id: 2, routing: '1', parent: 1}},
|
366
|
+
{index: {_id: 2, routing: '31', data: expected_data2}},
|
367
|
+
{delete: {_id: 3, routing: '1', parent: 2}},
|
368
|
+
{index: {_id: 3, routing: '31', data: expected_data3}},
|
369
|
+
{delete: {_id: 4, routing: '1', parent: 3}},
|
370
|
+
{index: {_id: 4, routing: '31', data: expected_data4}}
|
371
|
+
])
|
372
|
+
end
|
373
|
+
end
|
374
|
+
|
375
|
+
describe 'when removing parents or grandparents' do
|
376
|
+
let(:comments) do
|
377
|
+
[
|
378
|
+
Comment.create!(id: 3, content: 'Yes, he is here.', comment_type: :vote, commented_id: 2),
|
379
|
+
Comment.create!(id: 4, content: 'What?', comment_type: :subvote, commented_id: 3)
|
380
|
+
]
|
381
|
+
end
|
382
|
+
let(:delete) { [existing_comments[0]] } # id: 1
|
383
|
+
|
384
|
+
before do
|
385
|
+
existing_comments.each { |c| raw_index_comment(c) }
|
386
|
+
comments.each { |c| raw_index_comment(c) }
|
387
|
+
end
|
388
|
+
|
389
|
+
it 'does not remove all descendants' do
|
390
|
+
expect(subject.bulk_body).to eq([
|
391
|
+
{delete: {_id: 1, routing: '1'}}
|
392
|
+
])
|
393
|
+
end
|
394
|
+
end
|
395
|
+
|
396
|
+
context 'when indexing' do
|
397
|
+
let(:to_index) { comments }
|
398
|
+
|
399
|
+
specify do
|
400
|
+
expected_data3 = {'content' => 'There!', 'content_with_crutches' => '[crutches] There!', 'comment_type' => {'name' => 'answer', 'parent' => 1}}
|
401
|
+
expected_data4 = {'content' => 'Yes, he is here.', 'content_with_crutches' => '[crutches] Yes, he is here.', 'comment_type' => {'name' => 'vote', 'parent' => 2}}
|
402
|
+
|
403
|
+
expected_data11 = {'content' => 'What is the sense of the universe?', 'content_with_crutches' => '[crutches] What is the sense of the universe?', 'comment_type' => 'question'}
|
404
|
+
expected_data12 = {'content' => 'I don\'t know.', 'content_with_crutches' => '[crutches] I don\'t know.', 'comment_type' => {'name' => 'answer', 'parent' => 11}}
|
405
|
+
expected_data13 = {'content' => '42', 'content_with_crutches' => '[crutches] 42', 'comment_type' => {'name' => 'answer', 'parent' => 11}}
|
406
|
+
expected_data14 = {'content' => 'I think that 42 is a correct answer', 'content_with_crutches' => '[crutches] I think that 42 is a correct answer',
|
407
|
+
'comment_type' => {'name' => 'vote', 'parent' => 13}}
|
408
|
+
|
409
|
+
expected_data21 = {'content' => 'How are you?', 'content_with_crutches' => '[crutches] How are you?', 'comment_type' => 'question'}
|
410
|
+
|
411
|
+
expected_data32 = {'content' => 'Ruby', 'content_with_crutches' => '[crutches] Ruby', 'comment_type' => {'name' => 'answer', 'parent' => 31}}
|
412
|
+
|
413
|
+
expect(subject.bulk_body).to eq([
|
414
|
+
{index: {_id: 3, routing: '1', data: expected_data3}},
|
415
|
+
{index: {_id: 4, routing: '1', data: expected_data4}},
|
416
|
+
|
417
|
+
{index: {_id: 11, routing: '11', data: expected_data11}},
|
418
|
+
{index: {_id: 12, routing: '11', data: expected_data12}},
|
419
|
+
{index: {_id: 13, routing: '11', data: expected_data13}},
|
420
|
+
{index: {_id: 14, routing: '11', data: expected_data14}},
|
421
|
+
|
422
|
+
{index: {_id: 21, routing: '21', data: expected_data21}},
|
423
|
+
|
424
|
+
{index: {_id: 32, routing: '31', data: expected_data32}}
|
425
|
+
])
|
426
|
+
end
|
427
|
+
end
|
428
|
+
|
429
|
+
context 'when deleting' do
|
430
|
+
before do
|
431
|
+
existing_comments.each { |c| raw_index_comment(c) }
|
432
|
+
comments.each { |c| raw_index_comment(c) }
|
433
|
+
end
|
434
|
+
|
435
|
+
let(:delete) { comments }
|
436
|
+
specify do
|
437
|
+
expect(subject.bulk_body).to eq([
|
438
|
+
{delete: {_id: 3, routing: '1', parent: 1}},
|
439
|
+
{delete: {_id: 4, routing: '1', parent: 2}},
|
440
|
+
|
441
|
+
{delete: {_id: 11, routing: '11'}},
|
442
|
+
{delete: {_id: 12, routing: '11', parent: 11}},
|
443
|
+
{delete: {_id: 13, routing: '11', parent: 11}},
|
444
|
+
{delete: {_id: 14, routing: '11', parent: 13}},
|
445
|
+
|
446
|
+
{delete: {_id: 21, routing: '21'}},
|
447
|
+
|
448
|
+
{delete: {_id: 32, routing: '31', parent: 31}}
|
449
|
+
])
|
450
|
+
end
|
451
|
+
end
|
452
|
+
|
453
|
+
context 'when updating' do
|
454
|
+
before do
|
455
|
+
comments.each { |c| raw_index_comment(c) }
|
456
|
+
end
|
457
|
+
let(:fields) { %w[content] }
|
458
|
+
let(:to_index) { comments }
|
459
|
+
specify do
|
460
|
+
expect(subject.bulk_body).to eq([
|
461
|
+
{update: {_id: 3, routing: '1', data: {doc: {'content' => comments[0].content}}}},
|
462
|
+
{update: {_id: 4, routing: '1', data: {doc: {'content' => comments[1].content}}}},
|
463
|
+
|
464
|
+
{update: {_id: 11, routing: '11', data: {doc: {'content' => comments[2].content}}}},
|
465
|
+
{update: {_id: 12, routing: '11', data: {doc: {'content' => comments[3].content}}}},
|
466
|
+
{update: {_id: 13, routing: '11', data: {doc: {'content' => comments[4].content}}}},
|
467
|
+
{update: {_id: 14, routing: '11', data: {doc: {'content' => comments[5].content}}}},
|
468
|
+
|
469
|
+
{update: {_id: 21, routing: '21', data: {doc: {'content' => comments[6].content}}}},
|
470
|
+
|
471
|
+
{update: {_id: 32, routing: '31', data: {doc: {'content' => comments[7].content}}}}
|
472
|
+
])
|
473
|
+
end
|
474
|
+
end
|
475
|
+
end
|
172
476
|
end
|
173
477
|
|
174
478
|
describe '#index_objects_by_id' do
|
@@ -11,8 +11,8 @@ describe Chewy::Index::Import::Routine do
|
|
11
11
|
CitiesIndex.create!
|
12
12
|
end
|
13
13
|
|
14
|
-
let(:index) { [double(id: 1, name: 'Name', object: {}), double(id: 2, name: 'Name', object: {})] }
|
15
|
-
let(:delete) { [double(id: 3, name: 'Name')] }
|
14
|
+
let(:index) { [double('city_1', id: 1, name: 'Name', object: {}), double('city_2', id: 2, name: 'Name', object: {})] }
|
15
|
+
let(:delete) { [double('city_3', id: 3, name: 'Name', object: {})] }
|
16
16
|
|
17
17
|
describe '#options' do
|
18
18
|
specify do
|
@@ -93,7 +93,7 @@ describe Chewy::Index::Import::Routine do
|
|
93
93
|
|
94
94
|
describe '#errors' do
|
95
95
|
subject { described_class.new(CitiesIndex) }
|
96
|
-
let(:index) { [double(id: 1, name: 'Name', object: ''), double(id: 2, name: 'Name', object: {})] }
|
96
|
+
let(:index) { [double('city_1', id: 1, name: 'Name', object: ''), double('city_2', id: 2, name: 'Name', object: {})] }
|
97
97
|
|
98
98
|
specify { expect(subject.errors).to eq([]) }
|
99
99
|
specify do
|
@@ -493,6 +493,46 @@ describe Chewy::Index::Import do
|
|
493
493
|
|
494
494
|
it_behaves_like 'importing'
|
495
495
|
end
|
496
|
+
|
497
|
+
context 'with parent-child relationship' do
|
498
|
+
before do
|
499
|
+
stub_model(:comment)
|
500
|
+
stub_index(:comments) do
|
501
|
+
index_scope Comment
|
502
|
+
field :content
|
503
|
+
field :comment_type, type: :join, relations: {question: %i[answer comment], answer: :vote}, join: {type: :comment_type, id: :commented_id}
|
504
|
+
end
|
505
|
+
end
|
506
|
+
|
507
|
+
let!(:comments) do
|
508
|
+
[
|
509
|
+
Comment.create!(id: 1, content: 'Where is Nemo?', comment_type: :question),
|
510
|
+
Comment.create!(id: 2, content: 'Here.', comment_type: :answer, commented_id: 1),
|
511
|
+
Comment.create!(id: 3, content: 'There!', comment_type: :answer, commented_id: 1),
|
512
|
+
Comment.create!(id: 4, content: 'Yes, he is here.', comment_type: :vote, commented_id: 2)
|
513
|
+
]
|
514
|
+
end
|
515
|
+
|
516
|
+
def imported_comments
|
517
|
+
CommentsIndex.all.map do |comment|
|
518
|
+
comment.attributes.except('_score', '_explanation')
|
519
|
+
end
|
520
|
+
end
|
521
|
+
|
522
|
+
it 'imports parent and children' do
|
523
|
+
CommentsIndex.import!(comments.map(&:id))
|
524
|
+
|
525
|
+
expect(imported_comments).to match_array([
|
526
|
+
{'id' => '1', 'content' => 'Where is Nemo?', 'comment_type' => 'question'},
|
527
|
+
{'id' => '2', 'content' => 'Here.', 'comment_type' => {'name' => 'answer', 'parent' => 1}},
|
528
|
+
{'id' => '3', 'content' => 'There!', 'comment_type' => {'name' => 'answer', 'parent' => 1}},
|
529
|
+
{'id' => '4', 'content' => 'Yes, he is here.', 'comment_type' => {'name' => 'vote', 'parent' => 2}}
|
530
|
+
])
|
531
|
+
|
532
|
+
answer_ids = CommentsIndex.query(has_parent: {parent_type: 'question', query: {match: {content: 'Where'}}}).pluck(:_id)
|
533
|
+
expect(answer_ids).to match_array(%w[2 3])
|
534
|
+
end
|
535
|
+
end
|
496
536
|
end
|
497
537
|
|
498
538
|
describe '.import!', :orm do
|
@@ -1,6 +1,6 @@
|
|
1
1
|
require 'database_cleaner'
|
2
2
|
|
3
|
-
ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: '
|
3
|
+
ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: ':memory:', pool: 10)
|
4
4
|
ActiveRecord::Base.logger = Logger.new('/dev/null')
|
5
5
|
if ActiveRecord::Base.respond_to?(:raise_in_transactional_callbacks)
|
6
6
|
ActiveRecord::Base.raise_in_transactional_callbacks = true
|
@@ -31,6 +31,13 @@ ActiveRecord::Schema.define do
|
|
31
31
|
t.column :lat, :string
|
32
32
|
t.column :lon, :string
|
33
33
|
end
|
34
|
+
|
35
|
+
create_table :comments do |t|
|
36
|
+
t.column :content, :string
|
37
|
+
t.column :comment_type, :string
|
38
|
+
t.column :commented_id, :integer
|
39
|
+
t.column :updated_at, :datetime
|
40
|
+
end
|
34
41
|
end
|
35
42
|
|
36
43
|
module ActiveRecordClassHelpers
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: chewy
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 7.2.
|
4
|
+
version: 7.2.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Toptal, LLC
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2022-02-03 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: database_cleaner
|
@@ -244,9 +244,8 @@ files:
|
|
244
244
|
- gemfiles/rails.5.2.activerecord.gemfile
|
245
245
|
- gemfiles/rails.6.0.activerecord.gemfile
|
246
246
|
- gemfiles/rails.6.1.activerecord.gemfile
|
247
|
+
- gemfiles/rails.7.0.activerecord.gemfile
|
247
248
|
- lib/chewy.rb
|
248
|
-
- lib/chewy/backports/deep_dup.rb
|
249
|
-
- lib/chewy/backports/duplicable.rb
|
250
249
|
- lib/chewy/config.rb
|
251
250
|
- lib/chewy/errors.rb
|
252
251
|
- lib/chewy/fields/base.rb
|
@@ -454,7 +453,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
454
453
|
- !ruby/object:Gem::Version
|
455
454
|
version: '0'
|
456
455
|
requirements: []
|
457
|
-
rubygems_version: 3.
|
456
|
+
rubygems_version: 3.2.32
|
458
457
|
signing_key:
|
459
458
|
specification_version: 4
|
460
459
|
summary: Elasticsearch ODM client wrapper
|
@@ -1,46 +0,0 @@
|
|
1
|
-
require 'chewy/backports/duplicable'
|
2
|
-
|
3
|
-
class Object
|
4
|
-
# Returns a deep copy of object if it's duplicable. If it's
|
5
|
-
# not duplicable, returns +self+.
|
6
|
-
#
|
7
|
-
# object = Object.new
|
8
|
-
# dup = object.deep_dup
|
9
|
-
# dup.instance_variable_set(:@a, 1)
|
10
|
-
#
|
11
|
-
# object.instance_variable_defined?(:@a) # => false
|
12
|
-
# dup.instance_variable_defined?(:@a) # => true
|
13
|
-
def deep_dup
|
14
|
-
duplicable? ? dup : self
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
class Array
|
19
|
-
# Returns a deep copy of array.
|
20
|
-
#
|
21
|
-
# array = [1, [2, 3]]
|
22
|
-
# dup = array.deep_dup
|
23
|
-
# dup[1][2] = 4
|
24
|
-
#
|
25
|
-
# array[1][2] # => nil
|
26
|
-
# dup[1][2] # => 4
|
27
|
-
def deep_dup
|
28
|
-
map(&:deep_dup)
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
class Hash
|
33
|
-
# Returns a deep copy of hash.
|
34
|
-
#
|
35
|
-
# hash = { a: { b: 'b' } }
|
36
|
-
# dup = hash.deep_dup
|
37
|
-
# dup[:a][:c] = 'c'
|
38
|
-
#
|
39
|
-
# hash[:a][:c] # => nil
|
40
|
-
# dup[:a][:c] # => "c"
|
41
|
-
def deep_dup
|
42
|
-
each_with_object(dup) do |(key, value), hash|
|
43
|
-
hash[key.deep_dup] = value.deep_dup
|
44
|
-
end
|
45
|
-
end
|
46
|
-
end
|
@@ -1,91 +0,0 @@
|
|
1
|
-
#--
|
2
|
-
# Most objects are cloneable, but not all. For example you can't dup +nil+:
|
3
|
-
#
|
4
|
-
# nil.dup # => TypeError: can't dup NilClass
|
5
|
-
#
|
6
|
-
# Classes may signal their instances are not duplicable removing +dup+/+clone+
|
7
|
-
# or raising exceptions from them. So, to dup an arbitrary object you normally
|
8
|
-
# use an optimistic approach and are ready to catch an exception, say:
|
9
|
-
#
|
10
|
-
# arbitrary_object.dup rescue object
|
11
|
-
#
|
12
|
-
# Rails dups objects in a few critical spots where they are not that arbitrary.
|
13
|
-
# That rescue is very expensive (like 40 times slower than a predicate), and it
|
14
|
-
# is often triggered.
|
15
|
-
#
|
16
|
-
# That's why we hardcode the following cases and check duplicable? instead of
|
17
|
-
# using that rescue idiom.
|
18
|
-
#++
|
19
|
-
class Object
|
20
|
-
# Can you safely dup this object?
|
21
|
-
#
|
22
|
-
# False for +nil+, +false+, +true+, symbol, and number objects;
|
23
|
-
# true otherwise.
|
24
|
-
def duplicable?
|
25
|
-
true
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
class NilClass
|
30
|
-
# +nil+ is not duplicable:
|
31
|
-
#
|
32
|
-
# nil.duplicable? # => false
|
33
|
-
# nil.dup # => TypeError: can't dup NilClass
|
34
|
-
def duplicable?
|
35
|
-
false
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
class FalseClass
|
40
|
-
# +false+ is not duplicable:
|
41
|
-
#
|
42
|
-
# false.duplicable? # => false
|
43
|
-
# false.dup # => TypeError: can't dup FalseClass
|
44
|
-
def duplicable?
|
45
|
-
false
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
class TrueClass
|
50
|
-
# +true+ is not duplicable:
|
51
|
-
#
|
52
|
-
# true.duplicable? # => false
|
53
|
-
# true.dup # => TypeError: can't dup TrueClass
|
54
|
-
def duplicable?
|
55
|
-
false
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
59
|
-
class Symbol
|
60
|
-
# Symbols are not duplicable:
|
61
|
-
#
|
62
|
-
# :my_symbol.duplicable? # => false
|
63
|
-
# :my_symbol.dup # => TypeError: can't dup Symbol
|
64
|
-
def duplicable?
|
65
|
-
false
|
66
|
-
end
|
67
|
-
end
|
68
|
-
|
69
|
-
class Numeric
|
70
|
-
# Numbers are not duplicable:
|
71
|
-
#
|
72
|
-
# 3.duplicable? # => false
|
73
|
-
# 3.dup # => TypeError: can't dup Fixnum
|
74
|
-
def duplicable?
|
75
|
-
false
|
76
|
-
end
|
77
|
-
end
|
78
|
-
|
79
|
-
require 'bigdecimal'
|
80
|
-
class BigDecimal
|
81
|
-
begin
|
82
|
-
BigDecimal('4.56').dup
|
83
|
-
|
84
|
-
def duplicable?
|
85
|
-
true
|
86
|
-
end
|
87
|
-
rescue TypeError
|
88
|
-
# can't dup, so use superclass implementation
|
89
|
-
nil
|
90
|
-
end
|
91
|
-
end
|