chewy 7.2.1 → 7.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/CODEOWNERS +1 -0
- data/.github/dependabot.yml +42 -0
- data/.github/workflows/ruby.yml +28 -26
- data/.rubocop.yml +4 -1
- data/CHANGELOG.md +196 -0
- data/Gemfile +4 -3
- data/README.md +203 -20
- data/chewy.gemspec +4 -18
- data/gemfiles/base.gemfile +12 -0
- data/gemfiles/rails.6.1.activerecord.gemfile +2 -1
- data/gemfiles/{rails.5.2.activerecord.gemfile → rails.7.0.activerecord.gemfile} +6 -3
- data/gemfiles/{rails.6.0.activerecord.gemfile → rails.7.1.activerecord.gemfile} +6 -3
- data/lib/chewy/config.rb +22 -14
- data/lib/chewy/elastic_client.rb +31 -0
- data/lib/chewy/errors.rb +11 -2
- data/lib/chewy/fields/base.rb +69 -13
- data/lib/chewy/fields/root.rb +2 -10
- data/lib/chewy/index/actions.rb +11 -16
- data/lib/chewy/index/adapter/active_record.rb +18 -3
- data/lib/chewy/index/adapter/object.rb +0 -10
- data/lib/chewy/index/adapter/orm.rb +4 -14
- data/lib/chewy/index/crutch.rb +15 -7
- data/lib/chewy/index/import/bulk_builder.rb +219 -32
- data/lib/chewy/index/import/bulk_request.rb +1 -1
- data/lib/chewy/index/import/routine.rb +3 -3
- data/lib/chewy/index/import.rb +45 -31
- data/lib/chewy/index/mapping.rb +2 -2
- data/lib/chewy/index/observe/active_record_methods.rb +87 -0
- data/lib/chewy/index/observe/callback.rb +34 -0
- data/lib/chewy/index/observe.rb +3 -58
- data/lib/chewy/index/syncer.rb +1 -1
- data/lib/chewy/index.rb +25 -0
- data/lib/chewy/journal.rb +17 -6
- data/lib/chewy/log_subscriber.rb +5 -1
- data/lib/chewy/minitest/helpers.rb +77 -0
- data/lib/chewy/minitest/search_index_receiver.rb +3 -1
- data/lib/chewy/rake_helper.rb +92 -11
- data/lib/chewy/rspec/build_query.rb +12 -0
- data/lib/chewy/rspec/helpers.rb +55 -0
- data/lib/chewy/rspec/update_index.rb +14 -7
- data/lib/chewy/rspec.rb +2 -0
- data/lib/chewy/runtime/version.rb +1 -1
- data/lib/chewy/runtime.rb +1 -1
- data/lib/chewy/search/parameters/collapse.rb +16 -0
- data/lib/chewy/search/parameters/ignore_unavailable.rb +27 -0
- data/lib/chewy/search/parameters/indices.rb +1 -1
- data/lib/chewy/search/parameters/knn.rb +16 -0
- data/lib/chewy/search/parameters/order.rb +6 -19
- data/lib/chewy/search/parameters/storage.rb +1 -1
- data/lib/chewy/search/parameters/track_total_hits.rb +16 -0
- data/lib/chewy/search/parameters.rb +4 -4
- data/lib/chewy/search/request.rb +74 -16
- data/lib/chewy/search/scoping.rb +1 -1
- data/lib/chewy/search.rb +5 -2
- data/lib/chewy/stash.rb +3 -3
- data/lib/chewy/strategy/active_job.rb +1 -1
- data/lib/chewy/strategy/atomic_no_refresh.rb +18 -0
- data/lib/chewy/strategy/base.rb +10 -0
- data/lib/chewy/strategy/delayed_sidekiq/scheduler.rb +168 -0
- data/lib/chewy/strategy/delayed_sidekiq/worker.rb +76 -0
- data/lib/chewy/strategy/delayed_sidekiq.rb +30 -0
- data/lib/chewy/strategy/lazy_sidekiq.rb +64 -0
- data/lib/chewy/strategy/sidekiq.rb +1 -1
- data/lib/chewy/strategy.rb +3 -0
- data/lib/chewy/version.rb +1 -1
- data/lib/chewy.rb +21 -14
- data/lib/tasks/chewy.rake +18 -2
- data/migration_guide.md +1 -1
- data/spec/chewy/config_spec.rb +2 -2
- data/spec/chewy/elastic_client_spec.rb +26 -0
- data/spec/chewy/fields/base_spec.rb +39 -18
- data/spec/chewy/index/actions_spec.rb +10 -10
- data/spec/chewy/index/adapter/active_record_spec.rb +88 -0
- data/spec/chewy/index/import/bulk_builder_spec.rb +309 -1
- data/spec/chewy/index/import/routine_spec.rb +5 -5
- data/spec/chewy/index/import_spec.rb +48 -26
- data/spec/chewy/index/observe/active_record_methods_spec.rb +68 -0
- data/spec/chewy/index/observe/callback_spec.rb +139 -0
- data/spec/chewy/index/observe_spec.rb +27 -0
- data/spec/chewy/journal_spec.rb +13 -49
- data/spec/chewy/minitest/helpers_spec.rb +111 -1
- data/spec/chewy/minitest/search_index_receiver_spec.rb +6 -4
- data/spec/chewy/rake_helper_spec.rb +170 -0
- data/spec/chewy/rspec/build_query_spec.rb +34 -0
- data/spec/chewy/rspec/helpers_spec.rb +61 -0
- data/spec/chewy/search/pagination/kaminari_examples.rb +1 -1
- data/spec/chewy/search/pagination/kaminari_spec.rb +1 -1
- data/spec/chewy/search/parameters/collapse_spec.rb +5 -0
- data/spec/chewy/search/parameters/ignore_unavailable_spec.rb +67 -0
- data/spec/chewy/search/parameters/knn_spec.rb +5 -0
- data/spec/chewy/search/parameters/order_spec.rb +18 -11
- data/spec/chewy/search/parameters/track_total_hits_spec.rb +5 -0
- data/spec/chewy/search/parameters_spec.rb +6 -1
- data/spec/chewy/search/request_spec.rb +58 -9
- data/spec/chewy/search_spec.rb +9 -0
- data/spec/chewy/strategy/active_job_spec.rb +8 -8
- data/spec/chewy/strategy/atomic_no_refresh_spec.rb +60 -0
- data/spec/chewy/strategy/delayed_sidekiq_spec.rb +208 -0
- data/spec/chewy/strategy/lazy_sidekiq_spec.rb +214 -0
- data/spec/chewy/strategy/sidekiq_spec.rb +4 -4
- data/spec/chewy_spec.rb +10 -7
- data/spec/spec_helper.rb +1 -2
- data/spec/support/active_record.rb +8 -1
- metadata +45 -264
- data/lib/chewy/backports/deep_dup.rb +0 -46
- data/lib/chewy/backports/duplicable.rb +0 -91
- data/lib/chewy/index/import/thread_safe_progress_bar.rb +0 -40
@@ -1,9 +1,12 @@
|
|
1
1
|
require 'spec_helper'
|
2
|
+
require 'rake'
|
2
3
|
|
3
4
|
describe Chewy::RakeHelper, :orm do
|
4
5
|
before { Chewy.massacre }
|
5
6
|
|
6
7
|
before do
|
8
|
+
described_class.instance_variable_set(:@journal_exists, journal_exists)
|
9
|
+
|
7
10
|
stub_model(:city)
|
8
11
|
stub_model(:country)
|
9
12
|
|
@@ -20,6 +23,7 @@ describe Chewy::RakeHelper, :orm do
|
|
20
23
|
allow(described_class).to receive(:all_indexes) { [CitiesIndex, CountriesIndex, UsersIndex] }
|
21
24
|
end
|
22
25
|
|
26
|
+
let(:journal_exists) { true }
|
23
27
|
let!(:cities) { Array.new(3) { |i| City.create!(name: "Name#{i + 1}") } }
|
24
28
|
let!(:countries) { Array.new(2) { |i| Country.create!(name: "Name#{i + 1}") } }
|
25
29
|
let(:journal) do
|
@@ -92,6 +96,22 @@ Total: \\d+s\\Z
|
|
92
96
|
Total: \\d+s\\Z
|
93
97
|
OUTPUT
|
94
98
|
end
|
99
|
+
|
100
|
+
context 'when journal is missing' do
|
101
|
+
let(:journal_exists) { false }
|
102
|
+
|
103
|
+
specify do
|
104
|
+
output = StringIO.new
|
105
|
+
expect { described_class.reset(only: [CitiesIndex], output: output) }
|
106
|
+
.to update_index(CitiesIndex)
|
107
|
+
expect(output.string).to include(
|
108
|
+
"############################################################\n" \
|
109
|
+
"WARN: You are risking to lose some changes during the reset.\n " \
|
110
|
+
"Please consider enabling journaling.\n " \
|
111
|
+
'See https://github.com/toptal/chewy#journaling'
|
112
|
+
)
|
113
|
+
end
|
114
|
+
end
|
95
115
|
end
|
96
116
|
|
97
117
|
describe '.upgrade' do
|
@@ -407,6 +427,108 @@ Total: \\d+s\\Z
|
|
407
427
|
described_class.journal_clean(except: CitiesIndex, output: output)
|
408
428
|
expect(output.string).to match(Regexp.new(<<-OUTPUT, Regexp::MULTILINE))
|
409
429
|
\\ACleaned up 1 journal entries
|
430
|
+
Total: \\d+s\\Z
|
431
|
+
OUTPUT
|
432
|
+
end
|
433
|
+
|
434
|
+
it 'executes asynchronously' do
|
435
|
+
output = StringIO.new
|
436
|
+
expect(Chewy.client).to receive(:delete_by_query).with(
|
437
|
+
{
|
438
|
+
body: {query: {match_all: {}}},
|
439
|
+
index: ['chewy_journal'],
|
440
|
+
refresh: false,
|
441
|
+
requests_per_second: 10.0,
|
442
|
+
scroll_size: 200,
|
443
|
+
wait_for_completion: false
|
444
|
+
}
|
445
|
+
).and_call_original
|
446
|
+
described_class.journal_clean(
|
447
|
+
output: output,
|
448
|
+
delete_by_query_options: {
|
449
|
+
wait_for_completion: false,
|
450
|
+
requests_per_second: 10.0,
|
451
|
+
scroll_size: 200
|
452
|
+
}
|
453
|
+
)
|
454
|
+
|
455
|
+
expect(output.string).to match(Regexp.new(<<-OUTPUT, Regexp::MULTILINE))
|
456
|
+
\\ATask to cleanup the journal has been created, [^\\n]*
|
457
|
+
Total: \\d+s\\Z
|
458
|
+
OUTPUT
|
459
|
+
end
|
460
|
+
|
461
|
+
context 'execute "chewy:journal:clean" rake task' do
|
462
|
+
subject(:task) { Rake.application['chewy:journal:clean'] }
|
463
|
+
before do
|
464
|
+
Rake::DefaultLoader.new.load('lib/tasks/chewy.rake')
|
465
|
+
Rake::Task.define_task(:environment)
|
466
|
+
end
|
467
|
+
it 'does not raise error' do
|
468
|
+
expect { task.invoke }.to_not raise_error
|
469
|
+
end
|
470
|
+
end
|
471
|
+
end
|
472
|
+
|
473
|
+
describe '.create_missing_indexes!' do
|
474
|
+
before do
|
475
|
+
[CountriesIndex, Chewy::Stash::Specification].map(&:create!)
|
476
|
+
|
477
|
+
# To avoid flaky issues when previous specs were run
|
478
|
+
expect(Chewy::Index).to receive(:descendants).and_return(
|
479
|
+
[
|
480
|
+
UsersIndex,
|
481
|
+
CountriesIndex,
|
482
|
+
CitiesIndex,
|
483
|
+
Chewy::Stash::Specification,
|
484
|
+
Chewy::Stash::Journal
|
485
|
+
]
|
486
|
+
)
|
487
|
+
end
|
488
|
+
|
489
|
+
specify do
|
490
|
+
output = StringIO.new
|
491
|
+
described_class.create_missing_indexes!(output: output)
|
492
|
+
expect(CitiesIndex.exists?).to be_truthy
|
493
|
+
expect(UsersIndex.exists?).to be_truthy
|
494
|
+
expect(Chewy::Stash::Journal.exists?).to be_falsey
|
495
|
+
expect(output.string).to match(Regexp.new(<<-OUTPUT, Regexp::MULTILINE))
|
496
|
+
UsersIndex index successfully created
|
497
|
+
CitiesIndex index successfully created
|
498
|
+
Total: \\d+s\\Z
|
499
|
+
OUTPUT
|
500
|
+
end
|
501
|
+
|
502
|
+
context 'when verbose' do
|
503
|
+
specify do
|
504
|
+
output = StringIO.new
|
505
|
+
described_class.create_missing_indexes!(output: output, env: {'VERBOSE' => '1'})
|
506
|
+
expect(output.string).to match(Regexp.new(<<-OUTPUT, Regexp::MULTILINE))
|
507
|
+
UsersIndex index successfully created
|
508
|
+
CountriesIndex already exists, skipping
|
509
|
+
CitiesIndex index successfully created
|
510
|
+
Chewy::Stash::Specification already exists, skipping
|
511
|
+
Total: \\d+s\\Z
|
512
|
+
OUTPUT
|
513
|
+
end
|
514
|
+
end
|
515
|
+
|
516
|
+
context 'when journaling is enabled' do
|
517
|
+
before { Chewy.config.settings[:journal] = true }
|
518
|
+
after { Chewy.config.settings.delete(:journal) }
|
519
|
+
specify do
|
520
|
+
described_class.create_missing_indexes!(output: StringIO.new)
|
521
|
+
expect(Chewy::Stash::Journal.exists?).to be_truthy
|
522
|
+
end
|
523
|
+
end
|
524
|
+
end
|
525
|
+
|
526
|
+
describe '.journal_create' do
|
527
|
+
specify do
|
528
|
+
output = StringIO.new
|
529
|
+
described_class.journal_create(output: output)
|
530
|
+
expect(Chewy::Stash::Journal.exists?).to be_truthy
|
531
|
+
expect(output.string).to match(Regexp.new(<<-OUTPUT, Regexp::MULTILINE))
|
410
532
|
Total: \\d+s\\Z
|
411
533
|
OUTPUT
|
412
534
|
end
|
@@ -483,4 +605,52 @@ Total: \\d+s\\Z
|
|
483
605
|
end
|
484
606
|
end
|
485
607
|
end
|
608
|
+
|
609
|
+
describe '.delete_by_query_options_from_env' do
|
610
|
+
subject(:options) { described_class.delete_by_query_options_from_env(env) }
|
611
|
+
let(:env) do
|
612
|
+
{
|
613
|
+
'WAIT_FOR_COMPLETION' => 'false',
|
614
|
+
'REQUESTS_PER_SECOND' => '10',
|
615
|
+
'SCROLL_SIZE' => '5000'
|
616
|
+
}
|
617
|
+
end
|
618
|
+
|
619
|
+
it 'parses the options' do
|
620
|
+
expect(options).to eq(
|
621
|
+
wait_for_completion: false,
|
622
|
+
requests_per_second: 10.0,
|
623
|
+
scroll_size: 5000
|
624
|
+
)
|
625
|
+
end
|
626
|
+
|
627
|
+
context 'with different boolean values' do
|
628
|
+
it 'parses the option correctly' do
|
629
|
+
%w[1 t true TRUE on ON].each do |v|
|
630
|
+
expect(described_class.delete_by_query_options_from_env({'WAIT_FOR_COMPLETION' => v}))
|
631
|
+
.to eq(wait_for_completion: true)
|
632
|
+
end
|
633
|
+
|
634
|
+
%w[0 f false FALSE off OFF].each do |v|
|
635
|
+
expect(described_class.delete_by_query_options_from_env({'WAIT_FOR_COMPLETION' => v}))
|
636
|
+
.to eq(wait_for_completion: false)
|
637
|
+
end
|
638
|
+
end
|
639
|
+
end
|
640
|
+
|
641
|
+
context 'with other env' do
|
642
|
+
let(:env) { {'SOME_ENV' => '123', 'REQUESTS_PER_SECOND' => '15'} }
|
643
|
+
|
644
|
+
it 'parses only the options' do
|
645
|
+
expect(options).to eq(requests_per_second: 15.0)
|
646
|
+
end
|
647
|
+
end
|
648
|
+
end
|
649
|
+
|
650
|
+
describe '.subscribed_task_stats' do
|
651
|
+
specify do
|
652
|
+
block_output = described_class.subscribed_task_stats(StringIO.new) { 'expected output' }
|
653
|
+
expect(block_output).to eq('expected output')
|
654
|
+
end
|
655
|
+
end
|
486
656
|
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe :build_query do
|
4
|
+
before do
|
5
|
+
stub_model(:city)
|
6
|
+
stub_index(:cities) { index_scope City }
|
7
|
+
CitiesIndex.create
|
8
|
+
end
|
9
|
+
|
10
|
+
let(:expected_query) do
|
11
|
+
{
|
12
|
+
index: ['cities'],
|
13
|
+
body: {
|
14
|
+
query: {
|
15
|
+
match: {name: 'name'}
|
16
|
+
}
|
17
|
+
}
|
18
|
+
}
|
19
|
+
end
|
20
|
+
let(:dummy_query) { {match: {name: 'name'}} }
|
21
|
+
let(:unexpected_query) { {match: {name: 'name'}} }
|
22
|
+
|
23
|
+
context 'build expected query' do
|
24
|
+
specify do
|
25
|
+
expect(CitiesIndex.query(dummy_query)).to build_query(expected_query)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
context 'not to build unexpected query' do
|
30
|
+
specify do
|
31
|
+
expect(CitiesIndex.query(dummy_query)).not_to build_query(unexpected_query)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe :rspec_helper do
|
4
|
+
include Chewy::Rspec::Helpers
|
5
|
+
|
6
|
+
before do
|
7
|
+
stub_model(:city)
|
8
|
+
stub_index(:cities) { index_scope City }
|
9
|
+
CitiesIndex.create
|
10
|
+
end
|
11
|
+
|
12
|
+
let(:hits) do
|
13
|
+
[
|
14
|
+
{
|
15
|
+
'_index' => 'cities',
|
16
|
+
'_type' => '_doc',
|
17
|
+
'_id' => '1',
|
18
|
+
'_score' => 3.14,
|
19
|
+
'_source' => source
|
20
|
+
}
|
21
|
+
]
|
22
|
+
end
|
23
|
+
|
24
|
+
let(:source) { {'name' => 'some_name'} }
|
25
|
+
let(:sources) { [source] }
|
26
|
+
|
27
|
+
context :mock_elasticsearch_response do
|
28
|
+
let(:raw_response) do
|
29
|
+
{
|
30
|
+
'took' => 4,
|
31
|
+
'timed_out' => false,
|
32
|
+
'_shards' => {
|
33
|
+
'total' => 1,
|
34
|
+
'successful' => 1,
|
35
|
+
'skipped' => 0,
|
36
|
+
'failed' => 0
|
37
|
+
},
|
38
|
+
'hits' => {
|
39
|
+
'total' => {
|
40
|
+
'value' => 1,
|
41
|
+
'relation' => 'eq'
|
42
|
+
},
|
43
|
+
'max_score' => 1.0,
|
44
|
+
'hits' => hits
|
45
|
+
}
|
46
|
+
}
|
47
|
+
end
|
48
|
+
|
49
|
+
specify do
|
50
|
+
mock_elasticsearch_response(CitiesIndex, raw_response)
|
51
|
+
expect(CitiesIndex.query({}).hits).to eq(hits)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
context :mock_elasticsearch_response_sources do
|
56
|
+
specify do
|
57
|
+
mock_elasticsearch_response_sources(CitiesIndex, sources)
|
58
|
+
expect(CitiesIndex.query({}).hits).to eq(hits)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -24,7 +24,7 @@ shared_examples :kaminari do |request_base_class|
|
|
24
24
|
let(:data) { Array.new(10) { |i| {id: i.next.to_s, name: "Name#{i.next}", age: 10 * i.next}.stringify_keys! } }
|
25
25
|
|
26
26
|
before { ProductsIndex.import!(data.map { |h| double(h) }) }
|
27
|
-
before { allow(
|
27
|
+
before { allow(Kaminari.config).to receive_messages(default_per_page: 3) }
|
28
28
|
|
29
29
|
describe '#per, #page' do
|
30
30
|
specify { expect(search.map { |e| e.attributes.except(*except_fields) }).to match_array(data) }
|
@@ -6,7 +6,7 @@ describe Chewy::Search::Pagination::Kaminari do
|
|
6
6
|
let(:data) { Array.new(12) { |i| {id: i.next.to_s, name: "Name#{i.next}", age: 10 * i.next}.stringify_keys! } }
|
7
7
|
|
8
8
|
before { ProductsIndex.import!(data.map { |h| double(h) }) }
|
9
|
-
before { allow(
|
9
|
+
before { allow(Kaminari.config).to receive_messages(default_per_page: 17) }
|
10
10
|
|
11
11
|
specify { expect(search.objects.class).to eq(Kaminari::PaginatableArray) }
|
12
12
|
specify { expect(search.objects.total_count).to eq(12) }
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Chewy::Search::Parameters::IgnoreUnavailable do
|
4
|
+
subject { described_class.new(true) }
|
5
|
+
|
6
|
+
describe '#initialize' do
|
7
|
+
specify { expect(subject.value).to eq(true) }
|
8
|
+
specify { expect(described_class.new.value).to eq(nil) }
|
9
|
+
specify { expect(described_class.new(42).value).to eq(true) }
|
10
|
+
specify { expect(described_class.new(false).value).to eq(false) }
|
11
|
+
end
|
12
|
+
|
13
|
+
describe '#replace!' do
|
14
|
+
specify { expect { subject.replace!(false) }.to change { subject.value }.from(true).to(false) }
|
15
|
+
specify { expect { subject.replace!(nil) }.to change { subject.value }.from(true).to(nil) }
|
16
|
+
end
|
17
|
+
|
18
|
+
describe '#update!' do
|
19
|
+
specify { expect { subject.update!(nil) }.not_to change { subject.value }.from(true) }
|
20
|
+
specify { expect { subject.update!(false) }.to change { subject.value }.from(true).to(false) }
|
21
|
+
specify { expect { subject.update!(true) }.not_to change { subject.value }.from(true) }
|
22
|
+
|
23
|
+
context do
|
24
|
+
subject { described_class.new(false) }
|
25
|
+
|
26
|
+
specify { expect { subject.update!(nil) }.not_to change { subject.value }.from(false) }
|
27
|
+
specify { expect { subject.update!(false) }.not_to change { subject.value }.from(false) }
|
28
|
+
specify { expect { subject.update!(true) }.to change { subject.value }.from(false).to(true) }
|
29
|
+
end
|
30
|
+
|
31
|
+
context do
|
32
|
+
subject { described_class.new }
|
33
|
+
|
34
|
+
specify { expect { subject.update!(nil) }.not_to change { subject.value }.from(nil) }
|
35
|
+
specify { expect { subject.update!(false) }.to change { subject.value }.from(nil).to(false) }
|
36
|
+
specify { expect { subject.update!(true) }.to change { subject.value }.from(nil).to(true) }
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
describe '#merge!' do
|
41
|
+
specify { expect { subject.merge!(described_class.new) }.not_to change { subject.value }.from(true) }
|
42
|
+
specify { expect { subject.merge!(described_class.new(false)) }.to change { subject.value }.from(true).to(false) }
|
43
|
+
specify { expect { subject.merge!(described_class.new(true)) }.not_to change { subject.value }.from(true) }
|
44
|
+
|
45
|
+
context do
|
46
|
+
subject { described_class.new(false) }
|
47
|
+
|
48
|
+
specify { expect { subject.merge!(described_class.new) }.not_to change { subject.value }.from(false) }
|
49
|
+
specify { expect { subject.merge!(described_class.new(false)) }.not_to change { subject.value }.from(false) }
|
50
|
+
specify { expect { subject.merge!(described_class.new(true)) }.to change { subject.value }.from(false).to(true) }
|
51
|
+
end
|
52
|
+
|
53
|
+
context do
|
54
|
+
subject { described_class.new }
|
55
|
+
|
56
|
+
specify { expect { subject.merge!(described_class.new) }.not_to change { subject.value }.from(nil) }
|
57
|
+
specify { expect { subject.merge!(described_class.new(false)) }.to change { subject.value }.from(nil).to(false) }
|
58
|
+
specify { expect { subject.merge!(described_class.new(true)) }.to change { subject.value }.from(nil).to(true) }
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
describe '#render' do
|
63
|
+
specify { expect(described_class.new.render).to be_nil }
|
64
|
+
specify { expect(described_class.new(false).render).to eq(ignore_unavailable: false) }
|
65
|
+
specify { expect(subject.render).to eq(ignore_unavailable: true) }
|
66
|
+
end
|
67
|
+
end
|
@@ -4,26 +4,29 @@ describe Chewy::Search::Parameters::Order do
|
|
4
4
|
subject { described_class.new(%i[foo bar]) }
|
5
5
|
|
6
6
|
describe '#initialize' do
|
7
|
-
specify { expect(described_class.new.value).to eq(
|
8
|
-
specify { expect(described_class.new(nil).value).to eq(
|
9
|
-
specify { expect(described_class.new('').value).to eq(
|
10
|
-
specify { expect(described_class.new(42).value).to eq('42'
|
11
|
-
specify { expect(described_class.new([42, 43]).value).to eq(
|
12
|
-
specify { expect(described_class.new(
|
13
|
-
specify { expect(described_class.new([
|
7
|
+
specify { expect(described_class.new.value).to eq([]) }
|
8
|
+
specify { expect(described_class.new(nil).value).to eq([]) }
|
9
|
+
specify { expect(described_class.new('').value).to eq([]) }
|
10
|
+
specify { expect(described_class.new(42).value).to eq(['42']) }
|
11
|
+
specify { expect(described_class.new([42, 43]).value).to eq(%w[42 43]) }
|
12
|
+
specify { expect(described_class.new([42, 42]).value).to eq(%w[42 42]) }
|
13
|
+
specify { expect(described_class.new([42, [43, 44]]).value).to eq(%w[42 43 44]) }
|
14
|
+
specify { expect(described_class.new(a: 1).value).to eq([{'a' => 1}]) }
|
15
|
+
specify { expect(described_class.new(['a', {a: 1}, {a: 2}]).value).to eq(['a', {'a' => 1}, {'a' => 2}]) }
|
16
|
+
specify { expect(described_class.new(['', 43, {a: 1}]).value).to eq(['43', {'a' => 1}]) }
|
14
17
|
end
|
15
18
|
|
16
19
|
describe '#replace!' do
|
17
20
|
specify do
|
18
21
|
expect { subject.replace!(foo: {}) }
|
19
22
|
.to change { subject.value }
|
20
|
-
.from(
|
23
|
+
.from(%w[foo bar]).to([{'foo' => {}}])
|
21
24
|
end
|
22
25
|
|
23
26
|
specify do
|
24
27
|
expect { subject.replace!(nil) }
|
25
28
|
.to change { subject.value }
|
26
|
-
.from(
|
29
|
+
.from(%w[foo bar]).to([])
|
27
30
|
end
|
28
31
|
end
|
29
32
|
|
@@ -31,7 +34,7 @@ describe Chewy::Search::Parameters::Order do
|
|
31
34
|
specify do
|
32
35
|
expect { subject.update!(foo: {}) }
|
33
36
|
.to change { subject.value }
|
34
|
-
.from(
|
37
|
+
.from(%w[foo bar]).to(['foo', 'bar', {'foo' => {}}])
|
35
38
|
end
|
36
39
|
|
37
40
|
specify { expect { subject.update!(nil) }.not_to change { subject.value } }
|
@@ -41,7 +44,7 @@ describe Chewy::Search::Parameters::Order do
|
|
41
44
|
specify do
|
42
45
|
expect { subject.merge!(described_class.new(foo: {})) }
|
43
46
|
.to change { subject.value }
|
44
|
-
.from(
|
47
|
+
.from(%w[foo bar]).to(['foo', 'bar', {'foo' => {}}])
|
45
48
|
end
|
46
49
|
|
47
50
|
specify { expect { subject.merge!(described_class.new) }.not_to change { subject.value } }
|
@@ -51,6 +54,7 @@ describe Chewy::Search::Parameters::Order do
|
|
51
54
|
specify { expect(described_class.new.render).to be_nil }
|
52
55
|
specify { expect(described_class.new(:foo).render).to eq(sort: ['foo']) }
|
53
56
|
specify { expect(described_class.new([:foo, {bar: 42}, :baz]).render).to eq(sort: ['foo', {'bar' => 42}, 'baz']) }
|
57
|
+
specify { expect(described_class.new([:foo, {bar: 42}, {bar: 43}, :baz]).render).to eq(sort: ['foo', {'bar' => 42}, {'bar' => 43}, 'baz']) }
|
54
58
|
end
|
55
59
|
|
56
60
|
describe '#==' do
|
@@ -59,7 +63,10 @@ describe Chewy::Search::Parameters::Order do
|
|
59
63
|
specify { expect(described_class.new(:foo)).not_to eq(described_class.new(:bar)) }
|
60
64
|
specify { expect(described_class.new(%i[foo bar])).to eq(described_class.new(%i[foo bar])) }
|
61
65
|
specify { expect(described_class.new(%i[foo bar])).not_to eq(described_class.new(%i[bar foo])) }
|
66
|
+
specify { expect(described_class.new(%i[foo foo])).not_to eq(described_class.new(%i[foo])) }
|
62
67
|
specify { expect(described_class.new(foo: {a: 42})).to eq(described_class.new(foo: {a: 42})) }
|
63
68
|
specify { expect(described_class.new(foo: {a: 42})).not_to eq(described_class.new(foo: {b: 42})) }
|
69
|
+
specify { expect(described_class.new(['foo', {'foo' => 42}])).not_to eq(described_class.new([{'foo' => 42}, 'foo'])) }
|
70
|
+
specify { expect(described_class.new([{'foo' => 42}, {'foo' => 43}])).not_to eq(described_class.new([{'foo' => 43}, {'foo' => 42}])) }
|
64
71
|
end
|
65
72
|
end
|
@@ -13,7 +13,7 @@ describe Chewy::Search::Parameters do
|
|
13
13
|
|
14
14
|
specify { expect(subject.storages[:limit]).to equal(limit) }
|
15
15
|
specify { expect(subject.storages[:limit].value).to eq(3) }
|
16
|
-
specify { expect(subject.storages[:order].value).to eq('foo'
|
16
|
+
specify { expect(subject.storages[:order].value).to eq(['foo']) }
|
17
17
|
|
18
18
|
specify { expect { described_class.new(offset: limit) }.to raise_error(TypeError) }
|
19
19
|
end
|
@@ -128,6 +128,11 @@ describe Chewy::Search::Parameters do
|
|
128
128
|
specify { expect(subject.render).to eq(body: {}, allow_partial_search_results: true) }
|
129
129
|
end
|
130
130
|
|
131
|
+
context do
|
132
|
+
subject { described_class.new(ignore_unavailable: true) }
|
133
|
+
specify { expect(subject.render).to eq(body: {}, ignore_unavailable: true) }
|
134
|
+
end
|
135
|
+
|
131
136
|
context do
|
132
137
|
subject { described_class.new(query: {foo: 'bar'}, filter: {moo: 'baz'}) }
|
133
138
|
specify { expect(subject.render).to eq(body: {query: {bool: {must: {foo: 'bar'}, filter: {moo: 'baz'}}}}) }
|
@@ -177,7 +177,7 @@ describe Chewy::Search::Request do
|
|
177
177
|
describe '#order' do
|
178
178
|
specify { expect(subject.order(:foo).render[:body]).to include(sort: ['foo']) }
|
179
179
|
specify { expect(subject.order(foo: 42).order(nil).render[:body]).to include(sort: ['foo' => 42]) }
|
180
|
-
specify { expect(subject.order(foo: 42).order(foo: 43).render[:body]).to include(sort: ['foo' => 43]) }
|
180
|
+
specify { expect(subject.order(foo: 42).order(foo: 43).render[:body]).to include(sort: [{'foo' => 42}, {'foo' => 43}]) }
|
181
181
|
specify { expect(subject.order(:foo).order(:bar, :baz).render[:body]).to include(sort: %w[foo bar baz]) }
|
182
182
|
specify { expect(subject.order(nil).render[:body]).to be_blank }
|
183
183
|
specify { expect { subject.order(:foo) }.not_to change { subject.render } }
|
@@ -192,7 +192,7 @@ describe Chewy::Search::Request do
|
|
192
192
|
specify { expect { subject.reorder(:foo) }.not_to change { subject.render } }
|
193
193
|
end
|
194
194
|
|
195
|
-
%i[track_scores explain version profile].each do |name|
|
195
|
+
%i[track_scores track_total_hits explain version profile].each do |name|
|
196
196
|
describe "##{name}" do
|
197
197
|
specify { expect(subject.send(name).render[:body]).to include(name => true) }
|
198
198
|
specify { expect(subject.send(name).send(name, false).render[:body]).to be_blank }
|
@@ -214,13 +214,18 @@ describe Chewy::Search::Request do
|
|
214
214
|
specify { expect { subject.search_type('foo') }.not_to change { subject.render } }
|
215
215
|
end
|
216
216
|
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
217
|
+
describe '#preference' do
|
218
|
+
specify { expect(subject.preference('foo').render).to include(preference: 'foo') }
|
219
|
+
specify { expect(subject.preference('foo').preference('bar').render).to include(preference: 'bar') }
|
220
|
+
specify { expect(subject.preference('foo').preference(nil).render[:preference]).to be_blank }
|
221
|
+
specify { expect { subject.preference('foo') }.not_to change { subject.render } }
|
222
|
+
end
|
223
|
+
|
224
|
+
describe '#timeout' do
|
225
|
+
specify { expect(subject.timeout(:foo).render[:body]).to include(timeout: 'foo') }
|
226
|
+
specify { expect(subject.timeout(:foo).timeout(:bar).render[:body]).to include(timeout: 'bar') }
|
227
|
+
specify { expect(subject.timeout(:foo).timeout(nil).render[:body]).to be_blank }
|
228
|
+
specify { expect { subject.timeout(:foo) }.not_to change { subject.render } }
|
224
229
|
end
|
225
230
|
|
226
231
|
describe '#source' do
|
@@ -309,6 +314,18 @@ describe Chewy::Search::Request do
|
|
309
314
|
end
|
310
315
|
end
|
311
316
|
|
317
|
+
%i[collapse knn].each do |name|
|
318
|
+
describe "##{name}" do
|
319
|
+
specify { expect(subject.send(name, foo: {bar: 42}).render[:body]).to include(name => {'foo' => {bar: 42}}) }
|
320
|
+
specify do
|
321
|
+
expect(subject.send(name, foo: {bar: 42}).send(name, moo: {baz: 43}).render[:body])
|
322
|
+
.to include(name => {'moo' => {baz: 43}})
|
323
|
+
end
|
324
|
+
specify { expect(subject.send(name, foo: {bar: 42}).send(name, nil).render[:body]).to be_blank }
|
325
|
+
specify { expect { subject.send(name, foo: {bar: 42}) }.not_to change { subject.render } }
|
326
|
+
end
|
327
|
+
end
|
328
|
+
|
312
329
|
describe '#docvalue_fields' do
|
313
330
|
specify { expect(subject.docvalue_fields(:foo).render[:body]).to include(docvalue_fields: ['foo']) }
|
314
331
|
specify do
|
@@ -361,6 +378,13 @@ describe Chewy::Search::Request do
|
|
361
378
|
specify { expect { subject.min_score(1.2) }.not_to change { subject.render } }
|
362
379
|
end
|
363
380
|
|
381
|
+
describe '#ignore_unavailable' do
|
382
|
+
specify { expect(subject.ignore_unavailable(true).render).to include(ignore_unavailable: true) }
|
383
|
+
specify { expect(subject.ignore_unavailable(true).ignore_unavailable(false).render).to include(ignore_unavailable: false) }
|
384
|
+
specify { expect(subject.ignore_unavailable(true).ignore_unavailable(nil).render[:ignore_unavailable]).to be_blank }
|
385
|
+
specify { expect { subject.ignore_unavailable(true) }.not_to change { subject.render } }
|
386
|
+
end
|
387
|
+
|
364
388
|
describe '#search_after' do
|
365
389
|
specify { expect(subject.search_after(:foo, :bar).render[:body]).to include(search_after: %i[foo bar]) }
|
366
390
|
specify do
|
@@ -795,6 +819,31 @@ describe Chewy::Search::Request do
|
|
795
819
|
request: {index: ['products'], body: {query: {match: {name: 'name3'}}}, refresh: false}
|
796
820
|
)
|
797
821
|
end
|
822
|
+
|
823
|
+
it 'delete records asynchronously' do
|
824
|
+
outer_payload = nil
|
825
|
+
ActiveSupport::Notifications.subscribe('delete_query.chewy') do |_name, _start, _finish, _id, payload|
|
826
|
+
outer_payload = payload
|
827
|
+
end
|
828
|
+
subject.query(match: {name: 'name3'}).delete_all(
|
829
|
+
refresh: false,
|
830
|
+
wait_for_completion: false,
|
831
|
+
requests_per_second: 10.0,
|
832
|
+
scroll_size: 2000
|
833
|
+
)
|
834
|
+
expect(outer_payload).to eq(
|
835
|
+
index: ProductsIndex,
|
836
|
+
indexes: [ProductsIndex],
|
837
|
+
request: {
|
838
|
+
index: ['products'],
|
839
|
+
body: {query: {match: {name: 'name3'}}},
|
840
|
+
refresh: false,
|
841
|
+
wait_for_completion: false,
|
842
|
+
requests_per_second: 10.0,
|
843
|
+
scroll_size: 2000
|
844
|
+
}
|
845
|
+
)
|
846
|
+
end
|
798
847
|
end
|
799
848
|
|
800
849
|
describe '#response=' do
|
data/spec/chewy/search_spec.rb
CHANGED
@@ -48,6 +48,10 @@ describe Chewy::Search do
|
|
48
48
|
filter { match name: "Name#{index}" }
|
49
49
|
end
|
50
50
|
|
51
|
+
def self.by_rating_with_kwargs(value, options:) # rubocop:disable Lint/UnusedMethodArgument
|
52
|
+
filter { match rating: value }
|
53
|
+
end
|
54
|
+
|
51
55
|
index_scope City
|
52
56
|
field :name, type: 'keyword'
|
53
57
|
field :rating, type: :integer
|
@@ -114,5 +118,10 @@ describe Chewy::Search do
|
|
114
118
|
specify { expect(CountriesIndex.by_rating(3).by_name(5).map(&:class)).to eq([CountriesIndex]) }
|
115
119
|
specify { expect(CountriesIndex.order(:name).by_rating(3).map(&:rating)).to eq([3]) }
|
116
120
|
specify { expect(CountriesIndex.order(:name).by_rating(3).map(&:class)).to eq([CountriesIndex]) }
|
121
|
+
|
122
|
+
specify 'supports keyword arguments' do
|
123
|
+
expect(CitiesIndex.by_rating_with_kwargs(3, options: 'blah blah blah').map(&:rating)).to eq([3])
|
124
|
+
expect(CitiesIndex.order(:name).by_rating_with_kwargs(3, options: 'blah blah blah').map(&:rating)).to eq([3])
|
125
|
+
end
|
117
126
|
end
|
118
127
|
end
|