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,24 +1,22 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  # TODO: add more specs here later
4
- describe Chewy::Type::Import::Routine do
4
+ describe Chewy::Index::Import::Routine do
5
5
  before { Chewy.massacre }
6
6
  before do
7
7
  stub_index(:cities) do
8
- define_type :city do
9
- field :name
10
- field :object, type: 'object'
11
- end
8
+ field :name
9
+ field :object, type: 'object'
12
10
  end
13
11
  CitiesIndex.create!
14
12
  end
15
13
 
16
- let(:index) { [double(id: 1, name: 'Name', object: {}), double(id: 2, name: 'Name', object: {})] }
17
- 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: {})] }
18
16
 
19
17
  describe '#options' do
20
18
  specify do
21
- expect(described_class.new(CitiesIndex::City).options).to eq(
19
+ expect(described_class.new(CitiesIndex).options).to eq(
22
20
  journal: nil,
23
21
  refresh: true,
24
22
  update_failover: true,
@@ -29,7 +27,7 @@ describe Chewy::Type::Import::Routine do
29
27
 
30
28
  specify do
31
29
  expect(described_class.new(
32
- CitiesIndex::City, batch_size: 100, bulk_size: 1.megabyte, refresh: false
30
+ CitiesIndex, batch_size: 100, bulk_size: 1.megabyte, refresh: false
33
31
  ).options).to eq(
34
32
  journal: nil,
35
33
  refresh: false,
@@ -43,7 +41,7 @@ describe Chewy::Type::Import::Routine do
43
41
  context do
44
42
  before { allow(Chewy).to receive_messages(configuration: Chewy.configuration.merge(journal: true)) }
45
43
  specify do
46
- expect(described_class.new(CitiesIndex::City).options).to eq(
44
+ expect(described_class.new(CitiesIndex).options).to eq(
47
45
  journal: true,
48
46
  refresh: true,
49
47
  update_failover: true,
@@ -55,24 +53,26 @@ describe Chewy::Type::Import::Routine do
55
53
 
56
54
  specify do
57
55
  expect(CitiesIndex.client).to receive(:bulk).with(hash_including(refresh: true))
58
- described_class.new(CitiesIndex::City).process(index: index)
56
+ described_class.new(CitiesIndex).process(index: index)
59
57
  end
60
58
 
61
59
  specify do
62
60
  expect(CitiesIndex.client).to receive(:bulk).with(hash_including(refresh: false))
63
- described_class.new(CitiesIndex::City, refresh: false).process(index: index)
61
+ described_class.new(CitiesIndex, refresh: false).process(index: index)
64
62
  end
65
63
  end
66
64
 
67
65
  describe '#parallel_options' do
68
- specify { expect(described_class.new(CitiesIndex::City).parallel_options).to be_nil }
69
- specify { expect(described_class.new(CitiesIndex::City, parallel: true).parallel_options).to eq({}) }
70
- specify { expect(described_class.new(CitiesIndex::City, parallel: 3).parallel_options).to eq(in_processes: 3) }
71
- specify { expect(described_class.new(CitiesIndex::City, parallel: {in_threads: 2}).parallel_options).to eq(in_threads: 2) }
66
+ specify { expect(described_class.new(CitiesIndex).parallel_options).to be_nil }
67
+ specify { expect(described_class.new(CitiesIndex, parallel: true).parallel_options).to eq({}) }
68
+ specify { expect(described_class.new(CitiesIndex, parallel: 3).parallel_options).to eq(in_processes: 3) }
69
+ specify do
70
+ expect(described_class.new(CitiesIndex, parallel: {in_threads: 2}).parallel_options).to eq(in_threads: 2)
71
+ end
72
72
  end
73
73
 
74
74
  describe '#stats' do
75
- subject { described_class.new(CitiesIndex::City) }
75
+ subject { described_class.new(CitiesIndex) }
76
76
 
77
77
  specify { expect(subject.stats).to eq({}) }
78
78
  specify do
@@ -92,8 +92,8 @@ describe Chewy::Type::Import::Routine do
92
92
  end
93
93
 
94
94
  describe '#errors' do
95
- subject { described_class.new(CitiesIndex::City) }
96
- let(:index) { [double(id: 1, name: 'Name', object: ''), double(id: 2, name: 'Name', object: {})] }
95
+ subject { described_class.new(CitiesIndex) }
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
@@ -1,6 +1,6 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe Chewy::Type::Import do
3
+ describe Chewy::Index::Import do
4
4
  before { Chewy.massacre }
5
5
 
6
6
  before do
@@ -9,14 +9,13 @@ describe Chewy::Type::Import do
9
9
 
10
10
  before do
11
11
  stub_index(:cities) do
12
- define_type City do
13
- field :name
14
- end
12
+ index_scope City
13
+ field :name
15
14
  end
16
15
  end
17
16
 
18
17
  def imported_cities
19
- CitiesIndex::City.all.map do |city|
18
+ CitiesIndex.all.map do |city|
20
19
  city.attributes.except('_score', '_explanation')
21
20
  end
22
21
  end
@@ -37,13 +36,13 @@ describe Chewy::Type::Import do
37
36
  specify 'lazy (default)' do
38
37
  expect(CitiesIndex).to receive(:exists?).and_call_original
39
38
  expect(CitiesIndex).to receive(:create!).and_call_original
40
- CitiesIndex::City.import(dummy_city)
39
+ CitiesIndex.import(dummy_city)
41
40
  end
42
41
 
43
42
  specify 'lazy without objects' do
44
43
  expect(CitiesIndex).not_to receive(:exists?)
45
44
  expect(CitiesIndex).not_to receive(:create!)
46
- CitiesIndex::City.import([])
45
+ CitiesIndex.import([])
47
46
  end
48
47
 
49
48
  context 'skip' do
@@ -58,7 +57,20 @@ describe Chewy::Type::Import do
58
57
  specify do
59
58
  expect(CitiesIndex).not_to receive(:exists?)
60
59
  expect(CitiesIndex).not_to receive(:create!)
61
- CitiesIndex::City.import(dummy_city)
60
+ CitiesIndex.import(dummy_city)
61
+ end
62
+ end
63
+
64
+ context 'skip journal creation on import' do
65
+ before do
66
+ Chewy::Stash::Journal.create!
67
+ Chewy.config.settings[:skip_journal_creation_on_import] = true
68
+ end
69
+ after { Chewy.config.settings[:skip_journal_creation_on_import] = nil }
70
+
71
+ specify do
72
+ expect(Chewy::Stash::Journal).not_to receive(:create!)
73
+ CitiesIndex.import(dummy_city, journal: true)
62
74
  end
63
75
  end
64
76
  end
@@ -69,35 +81,36 @@ describe Chewy::Type::Import do
69
81
  specify { expect(import(dummy_cities)).to eq(true) }
70
82
  specify { expect(import(dummy_cities.map(&:id))).to eq(true) }
71
83
 
72
- specify { expect { import([]) }.not_to update_index(CitiesIndex::City) }
73
- specify { expect { import }.to update_index(CitiesIndex::City).and_reindex(dummy_cities) }
74
- specify { expect { import dummy_cities }.to update_index(CitiesIndex::City).and_reindex(dummy_cities) }
75
- specify { expect { import dummy_cities.map(&:id) }.to update_index(CitiesIndex::City).and_reindex(dummy_cities) }
84
+ specify { expect { import([]) }.not_to update_index(CitiesIndex) }
85
+ specify { expect { import }.to update_index(CitiesIndex).and_reindex(dummy_cities) }
86
+ specify { expect { import dummy_cities }.to update_index(CitiesIndex).and_reindex(dummy_cities) }
87
+ specify { expect { import dummy_cities.map(&:id) }.to update_index(CitiesIndex).and_reindex(dummy_cities) }
76
88
 
77
89
  describe 'criteria-driven importing' do
78
90
  let(:names) { %w[name0 name1] }
79
91
 
80
- context 'mongoid', :mongoid do
81
- specify { expect { import(City.where(:name.in => names)) }.to update_index(CitiesIndex::City).and_reindex(dummy_cities.first(2)) }
82
- specify { expect { import(City.where(:name.in => names).map(&:id)) }.to update_index(CitiesIndex::City).and_reindex(dummy_cities.first(2)) }
83
- end
84
-
85
92
  context 'active record', :active_record do
86
- specify { expect { import(City.where(name: names)) }.to update_index(CitiesIndex::City).and_reindex(dummy_cities.first(2)) }
87
- specify { expect { import(City.where(name: names).map(&:id)) }.to update_index(CitiesIndex::City).and_reindex(dummy_cities.first(2)) }
93
+ specify do
94
+ expect { import(City.where(name: names)) }
95
+ .to update_index(CitiesIndex).and_reindex(dummy_cities.first(2))
96
+ end
97
+ specify do
98
+ expect { import(City.where(name: names).map(&:id)) }
99
+ .to update_index(CitiesIndex).and_reindex(dummy_cities.first(2))
100
+ end
88
101
  end
89
102
  end
90
103
 
91
104
  specify do
92
105
  dummy_cities.first.destroy
93
106
  expect { import dummy_cities }
94
- .to update_index(CitiesIndex::City).and_reindex(dummy_cities.from(1)).and_delete(dummy_cities.first)
107
+ .to update_index(CitiesIndex).and_reindex(dummy_cities.from(1)).and_delete(dummy_cities.first)
95
108
  end
96
109
 
97
110
  specify do
98
111
  dummy_cities.first.destroy
99
112
  expect { import dummy_cities.map(&:id) }
100
- .to update_index(CitiesIndex::City).and_reindex(dummy_cities.from(1)).and_delete(dummy_cities.first)
113
+ .to update_index(CitiesIndex).and_reindex(dummy_cities.from(1)).and_delete(dummy_cities.first)
101
114
  end
102
115
 
103
116
  specify do
@@ -120,7 +133,10 @@ describe Chewy::Type::Import do
120
133
  context ':bulk_size' do
121
134
  let!(:dummy_cities) { Array.new(3) { |i| City.create(id: i + 1, name: "name#{i}" * 20) } }
122
135
 
123
- specify { expect { import(dummy_cities, bulk_size: 1.2.kilobyte) }.to update_index(CitiesIndex::City).and_reindex(dummy_cities) }
136
+ specify do
137
+ expect { import(dummy_cities, bulk_size: 1.2.kilobyte) }
138
+ .to update_index(CitiesIndex).and_reindex(dummy_cities)
139
+ end
124
140
 
125
141
  context do
126
142
  before { expect(Chewy.client).to receive(:bulk).exactly(3).times.and_call_original }
@@ -132,31 +148,29 @@ describe Chewy::Type::Import do
132
148
  before do
133
149
  names = %w[name0 name1]
134
150
 
135
- criteria = case adapter
136
- when :mongoid
137
- {:name.in => names}
138
- else
139
- {name: names}
140
- end
151
+ criteria = {name: names}
141
152
 
142
153
  stub_index(:cities) do
143
- define_type City.where(criteria) do
144
- field :name
145
- end
154
+ index_scope City.where(criteria)
155
+ field :name
146
156
  end
147
157
  end
148
158
 
149
- specify { expect { import }.to update_index(CitiesIndex::City).and_reindex(dummy_cities.first(2)) }
159
+ specify { expect { import }.to update_index(CitiesIndex).and_reindex(dummy_cities.first(2)) }
150
160
 
151
- context 'mongoid', :mongoid do
161
+ context 'active record', :active_record do
152
162
  specify do
153
- expect { import City.where(_id: dummy_cities.first.id) }.to update_index(CitiesIndex::City).and_reindex(dummy_cities.first).only
163
+ expect { import City.where(id: dummy_cities.first.id) }
164
+ .to update_index(CitiesIndex).and_reindex(dummy_cities.first).only
154
165
  end
155
- end
156
166
 
157
- context 'active record', :active_record do
158
167
  specify do
159
- expect { import City.where(id: dummy_cities.first.id) }.to update_index(CitiesIndex::City).and_reindex(dummy_cities.first).only
168
+ allow(CitiesIndex).to receive(:import_linear).and_return(double(present?: false))
169
+ allow(CitiesIndex).to receive(:import_parallel).and_return(double(present?: false))
170
+
171
+ expects_no_query(except: /SELECT\s+1\s+AS\s+one\s+FROM/) do
172
+ import City.where(id: dummy_cities.first.id)
173
+ end
160
174
  end
161
175
  end
162
176
  end
@@ -166,28 +180,27 @@ describe Chewy::Type::Import do
166
180
  payload = subscribe_notification
167
181
  dummy_cities.first.destroy
168
182
  import dummy_cities
169
- expect(payload).to eq(type: CitiesIndex::City, import: {delete: 1, index: 2})
183
+ expect(payload).to eq(index: CitiesIndex, import: {delete: 1, index: 2})
170
184
  end
171
185
 
172
186
  specify do
173
187
  payload = subscribe_notification
174
188
  dummy_cities.first.destroy
175
189
  import dummy_cities, batch_size: 2
176
- expect(payload).to eq(type: CitiesIndex::City, import: {delete: 1, index: 2})
190
+ expect(payload).to eq(index: CitiesIndex, import: {delete: 1, index: 2})
177
191
  end
178
192
 
179
193
  specify do
180
194
  payload = subscribe_notification
181
195
  import dummy_cities, batch_size: 2
182
- expect(payload).to eq(type: CitiesIndex::City, import: {index: 3})
196
+ expect(payload).to eq(index: CitiesIndex, import: {index: 3})
183
197
  end
184
198
 
185
199
  context do
186
200
  before do
187
201
  stub_index(:cities) do
188
- define_type City do
189
- field :name, type: 'object'
190
- end
202
+ index_scope City
203
+ field :name, type: 'object'
191
204
  end
192
205
  end
193
206
 
@@ -201,15 +214,15 @@ describe Chewy::Type::Import do
201
214
  specify do
202
215
  payload = subscribe_notification
203
216
  import dummy_cities, batch_size: 2
204
- expect(payload).to eq(type: CitiesIndex::City,
205
- errors: {index: {mapper_parsing_exception => %w[1 2 3]}},
206
- import: {index: 3})
217
+ expect(payload).to eq(index: CitiesIndex,
218
+ errors: {index: {mapper_parsing_exception => %w[1 2 3]}},
219
+ import: {index: 3})
207
220
  end
208
221
  end
209
222
  end
210
223
 
211
224
  context 'fields' do
212
- before { CitiesIndex::City.import!(dummy_cities.first(2)) }
225
+ before { CitiesIndex.import!(dummy_cities.first(2)) }
213
226
 
214
227
  context do
215
228
  before { expect(Chewy.client).to receive(:bulk).twice.and_call_original }
@@ -217,7 +230,7 @@ describe Chewy::Type::Import do
217
230
  end
218
231
 
219
232
  context do
220
- before { CitiesIndex::City.import!(dummy_cities.last) }
233
+ before { CitiesIndex.import!(dummy_cities.last) }
221
234
  before { expect(Chewy.client).to receive(:bulk).once.and_call_original }
222
235
  specify { expect(import(dummy_cities, update_fields: [:name])).to eq(true) }
223
236
  end
@@ -226,10 +239,8 @@ describe Chewy::Type::Import do
226
239
  context 'fields integrational' do
227
240
  before do
228
241
  stub_index(:cities) do
229
- define_type :city do
230
- field :name
231
- field :object, type: 'object'
232
- end
242
+ field :name
243
+ field :object, type: 'object'
233
244
  end
234
245
  end
235
246
 
@@ -257,9 +268,14 @@ describe Chewy::Type::Import do
257
268
  import(objects, update_fields: %i[name])
258
269
 
259
270
  expect(payload).to eq(
260
- errors: {index: {{'type' => 'mapper_parsing_exception', 'reason' => 'object mapping for [object] tried to parse field [object] as object, but found a concrete value'} => %w[2 4]}},
271
+ errors: {
272
+ index: {{
273
+ 'type' => 'mapper_parsing_exception',
274
+ 'reason' => 'object mapping for [object] tried to parse field [object] as object, but found a concrete value'
275
+ } => %w[2 4]}
276
+ },
261
277
  import: {index: 6},
262
- type: CitiesIndex::City
278
+ index: CitiesIndex
263
279
  )
264
280
  expect(imported_cities).to match_array([
265
281
  {'id' => '1', 'name' => 'Name11', 'object' => {'foo' => 11}},
@@ -275,9 +291,14 @@ describe Chewy::Type::Import do
275
291
  import(objects, batch_size: 2, update_fields: %i[name])
276
292
 
277
293
  expect(payload).to eq(
278
- errors: {index: {{'type' => 'mapper_parsing_exception', 'reason' => 'object mapping for [object] tried to parse field [object] as object, but found a concrete value'} => %w[2 4]}},
294
+ errors: {
295
+ index: {{
296
+ 'type' => 'mapper_parsing_exception',
297
+ 'reason' => 'object mapping for [object] tried to parse field [object] as object, but found a concrete value'
298
+ } => %w[2 4]}
299
+ },
279
300
  import: {index: 6},
280
- type: CitiesIndex::City
301
+ index: CitiesIndex
281
302
  )
282
303
  expect(imported_cities).to match_array([
283
304
  {'id' => '1', 'name' => 'Name11', 'object' => {'foo' => 11}},
@@ -287,7 +308,7 @@ describe Chewy::Type::Import do
287
308
  end
288
309
 
289
310
  context do
290
- before { CitiesIndex::City.import!(objects[4]) }
311
+ before { CitiesIndex.import!(objects[4]) }
291
312
 
292
313
  specify do
293
314
  payload = subscribe_notification
@@ -296,9 +317,14 @@ describe Chewy::Type::Import do
296
317
  import(objects, batch_size: 2, update_fields: %i[name])
297
318
 
298
319
  expect(payload).to eq(
299
- errors: {index: {{'type' => 'mapper_parsing_exception', 'reason' => 'object mapping for [object] tried to parse field [object] as object, but found a concrete value'} => %w[2 4]}},
320
+ errors: {
321
+ index: {{
322
+ 'type' => 'mapper_parsing_exception',
323
+ 'reason' => 'object mapping for [object] tried to parse field [object] as object, but found a concrete value'
324
+ } => %w[2 4]}
325
+ },
300
326
  import: {index: 6},
301
- type: CitiesIndex::City
327
+ index: CitiesIndex
302
328
  )
303
329
  expect(imported_cities).to match_array([
304
330
  {'id' => '1', 'name' => 'Name11', 'object' => {'foo' => 11}},
@@ -309,7 +335,7 @@ describe Chewy::Type::Import do
309
335
  end
310
336
 
311
337
  context do
312
- before { CitiesIndex::City.import!(old_objects[1], old_objects[3], objects[4]) }
338
+ before { CitiesIndex.import!(old_objects[1], old_objects[3], objects[4]) }
313
339
 
314
340
  specify do
315
341
  payload = subscribe_notification
@@ -319,7 +345,7 @@ describe Chewy::Type::Import do
319
345
 
320
346
  expect(payload).to eq(
321
347
  import: {index: 6},
322
- type: CitiesIndex::City
348
+ index: CitiesIndex
323
349
  )
324
350
  expect(imported_cities).to match_array([
325
351
  {'id' => '1', 'name' => 'Name11', 'object' => {'foo' => 11}},
@@ -338,7 +364,7 @@ describe Chewy::Type::Import do
338
364
 
339
365
  expect(payload).to eq(
340
366
  import: {index: 6},
341
- type: CitiesIndex::City
367
+ index: CitiesIndex
342
368
  )
343
369
  expect(imported_cities).to match_array([
344
370
  {'id' => '1', 'name' => 'Name11', 'object' => {'foo' => 11}},
@@ -357,8 +383,8 @@ describe Chewy::Type::Import do
357
383
 
358
384
  # Full match doesn't work here.
359
385
  expect(payload[:errors][:update].keys).to match([
360
- hash_including('type' => 'document_missing_exception', 'reason' => '[city][1]: document missing'),
361
- hash_including('type' => 'document_missing_exception', 'reason' => '[city][3]: document missing')
386
+ hash_including('type' => 'document_missing_exception', 'reason' => '[_doc][1]: document missing'),
387
+ hash_including('type' => 'document_missing_exception', 'reason' => '[_doc][3]: document missing')
362
388
  ])
363
389
  expect(payload[:errors][:update].values).to eq([['1'], ['3']])
364
390
  expect(imported_cities).to match_array([
@@ -370,7 +396,7 @@ describe Chewy::Type::Import do
370
396
  end
371
397
 
372
398
  context do
373
- before { CitiesIndex::City.import!(old_objects) }
399
+ before { CitiesIndex.import!(old_objects) }
374
400
 
375
401
  specify do
376
402
  payload = subscribe_notification
@@ -380,7 +406,7 @@ describe Chewy::Type::Import do
380
406
 
381
407
  expect(payload).to eq(
382
408
  import: {index: 6},
383
- type: CitiesIndex::City
409
+ index: CitiesIndex
384
410
  )
385
411
  expect(imported_cities).to match_array([
386
412
  {'id' => '1', 'name' => 'Name11', 'object' => {'foo' => 1}},
@@ -394,7 +420,7 @@ describe Chewy::Type::Import do
394
420
  end
395
421
 
396
422
  context do
397
- before { CitiesIndex::City.import!(old_objects) }
423
+ before { CitiesIndex.import!(old_objects) }
398
424
 
399
425
  specify do
400
426
  payload = subscribe_notification
@@ -403,9 +429,14 @@ describe Chewy::Type::Import do
403
429
  import(objects, update_fields: %i[object])
404
430
 
405
431
  expect(payload).to eq(
406
- errors: {update: {{'type' => 'mapper_parsing_exception', 'reason' => 'object mapping for [object] tried to parse field [object] as object, but found a concrete value'} => %w[2 4]}},
432
+ errors: {
433
+ update: {{
434
+ 'type' => 'mapper_parsing_exception',
435
+ 'reason' => 'object mapping for [object] tried to parse field [object] as object, but found a concrete value'
436
+ } => %w[2 4]}
437
+ },
407
438
  import: {index: 6},
408
- type: CitiesIndex::City
439
+ index: CitiesIndex
409
440
  )
410
441
  expect(imported_cities).to match_array([
411
442
  {'id' => '1', 'name' => 'Name1', 'object' => {'foo' => 11}},
@@ -423,9 +454,8 @@ describe Chewy::Type::Import do
423
454
  context do
424
455
  before do
425
456
  stub_index(:cities) do
426
- define_type City do
427
- field :name, type: 'object'
428
- end
457
+ index_scope City
458
+ field :name, type: 'object'
429
459
  end
430
460
  end
431
461
 
@@ -437,9 +467,8 @@ describe Chewy::Type::Import do
437
467
  context do
438
468
  before do
439
469
  stub_index(:cities) do
440
- define_type City do
441
- field :name, type: 'object', value: -> { name == 'name1' ? name : {name: name} }
442
- end
470
+ index_scope City
471
+ field :name, type: 'object', value: -> { name == 'name1' ? name : {name: name} }
443
472
  end
444
473
  end
445
474
 
@@ -451,19 +480,19 @@ describe Chewy::Type::Import do
451
480
 
452
481
  context 'default_import_options are set' do
453
482
  before do
454
- CitiesIndex::City.default_import_options(batch_size: 500)
483
+ CitiesIndex.default_import_options(batch_size: 500)
455
484
  end
456
485
 
457
486
  specify do
458
- expect(CitiesIndex::City.adapter).to receive(:import).with(any_args, hash_including(batch_size: 500))
459
- CitiesIndex::City.import
487
+ expect(CitiesIndex.adapter).to receive(:import).with(any_args, hash_including(batch_size: 500))
488
+ CitiesIndex.import
460
489
  end
461
490
  end
462
491
  end
463
492
 
464
493
  describe '.import', :orm do
465
494
  def import(*args)
466
- CitiesIndex::City.import(*args)
495
+ CitiesIndex.import(*args)
467
496
  end
468
497
 
469
498
  it_behaves_like 'importing'
@@ -472,62 +501,99 @@ describe Chewy::Type::Import do
472
501
  def import(*args)
473
502
  options = args.extract_options!
474
503
  options[:parallel] = 0
475
- CitiesIndex::City.import(*args, options)
504
+ CitiesIndex.import(*args, options)
476
505
  end
477
506
 
478
507
  it_behaves_like 'importing'
479
508
  end
509
+
510
+ context 'with parent-child relationship' do
511
+ before do
512
+ stub_model(:comment)
513
+ stub_index(:comments) do
514
+ index_scope Comment
515
+ field :content
516
+ field :comment_type, type: :join, relations: {question: %i[answer comment], answer: :vote}, join: {type: :comment_type, id: :commented_id}
517
+ end
518
+ end
519
+
520
+ let!(:comments) do
521
+ [
522
+ Comment.create!(id: 1, content: 'Where is Nemo?', comment_type: :question),
523
+ Comment.create!(id: 2, content: 'Here.', comment_type: :answer, commented_id: 1),
524
+ Comment.create!(id: 3, content: 'There!', comment_type: :answer, commented_id: 1),
525
+ Comment.create!(id: 4, content: 'Yes, he is here.', comment_type: :vote, commented_id: 2)
526
+ ]
527
+ end
528
+
529
+ def imported_comments
530
+ CommentsIndex.all.map do |comment|
531
+ comment.attributes.except('_score', '_explanation')
532
+ end
533
+ end
534
+
535
+ it 'imports parent and children' do
536
+ CommentsIndex.import!(comments.map(&:id))
537
+
538
+ expect(imported_comments).to match_array([
539
+ {'id' => '1', 'content' => 'Where is Nemo?', 'comment_type' => 'question'},
540
+ {'id' => '2', 'content' => 'Here.', 'comment_type' => {'name' => 'answer', 'parent' => 1}},
541
+ {'id' => '3', 'content' => 'There!', 'comment_type' => {'name' => 'answer', 'parent' => 1}},
542
+ {'id' => '4', 'content' => 'Yes, he is here.', 'comment_type' => {'name' => 'vote', 'parent' => 2}}
543
+ ])
544
+
545
+ answer_ids = CommentsIndex.query(has_parent: {parent_type: 'question', query: {match: {content: 'Where'}}}).pluck(:_id)
546
+ expect(answer_ids).to match_array(%w[2 3])
547
+ end
548
+ end
480
549
  end
481
550
 
482
551
  describe '.import!', :orm do
483
- specify { expect { CitiesIndex::City.import! }.not_to raise_error }
552
+ specify { expect { CitiesIndex.import! }.not_to raise_error }
484
553
 
485
554
  context do
486
555
  before do
487
556
  stub_index(:cities) do
488
- define_type City do
489
- field :name, type: 'object'
490
- end
557
+ index_scope City
558
+ field :name, type: 'object'
491
559
  end
492
560
  end
493
561
 
494
- specify { expect { CitiesIndex::City.import!(dummy_cities) }.to raise_error Chewy::ImportFailed }
562
+ specify { expect { CitiesIndex.import!(dummy_cities) }.to raise_error Chewy::ImportFailed }
495
563
  end
496
564
  end
497
565
 
498
566
  describe '.compose' do
499
567
  before do
500
568
  stub_index(:cities) do
501
- define_type :city do
502
- crutch :names do |collection|
503
- collection.map { |o| [o.name, o.name + '42'] }.to_h
504
- end
505
- field :name, value: ->(o, c) { c.names[o.name] }
506
- field :rating
569
+ crutch :names do |collection|
570
+ collection.to_h { |o| [o.name, "#{o.name}42"] }
507
571
  end
572
+ field :name, value: ->(o, c) { c.names[o.name] }
573
+ field :rating
508
574
  end
509
575
  end
510
576
 
511
577
  specify do
512
- expect(CitiesIndex::City.compose(double(name: 'Name', rating: 42)))
578
+ expect(CitiesIndex.compose(double(name: 'Name', rating: 42)))
513
579
  .to eq('name' => 'Name42', 'rating' => 42)
514
580
  end
515
581
 
516
582
  specify do
517
- expect(CitiesIndex::City.compose(double(name: 'Name', rating: 42), fields: %i[name]))
583
+ expect(CitiesIndex.compose(double(name: 'Name', rating: 42), fields: %i[name]))
518
584
  .to eq('name' => 'Name42')
519
585
  end
520
586
 
521
587
  context 'witchcraft' do
522
- before { CitiesIndex::City.witchcraft! }
588
+ before { CitiesIndex.witchcraft! }
523
589
 
524
590
  specify do
525
- expect(CitiesIndex::City.compose(double(name: 'Name', rating: 42)))
591
+ expect(CitiesIndex.compose(double(name: 'Name', rating: 42)))
526
592
  .to eq('name' => 'Name42', 'rating' => 42)
527
593
  end
528
594
 
529
595
  specify do
530
- expect(CitiesIndex::City.compose(double(name: 'Name', rating: 42), fields: %i[name]))
596
+ expect(CitiesIndex.compose(double(name: 'Name', rating: 42), fields: %i[name]))
531
597
  .to eq('name' => 'Name42')
532
598
  end
533
599
  end
@@ -536,12 +602,12 @@ describe Chewy::Type::Import do
536
602
  let(:crutches) { double(names: {'Name' => 'Name43'}) }
537
603
 
538
604
  specify do
539
- expect(CitiesIndex::City.compose(double(name: 'Name', rating: 42), crutches))
605
+ expect(CitiesIndex.compose(double(name: 'Name', rating: 42), crutches))
540
606
  .to eq('name' => 'Name43', 'rating' => 42)
541
607
  end
542
608
 
543
609
  specify do
544
- expect(CitiesIndex::City.compose(double(name: 'Name', rating: 42), crutches, fields: %i[name]))
610
+ expect(CitiesIndex.compose(double(name: 'Name', rating: 42), crutches, fields: %i[name]))
545
611
  .to eq('name' => 'Name43')
546
612
  end
547
613
  end