chewy 0.8.4 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (179) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +34 -0
  3. data/.rubocop_todo.yml +44 -0
  4. data/.travis.yml +20 -60
  5. data/Appraisals +15 -40
  6. data/CHANGELOG.md +42 -0
  7. data/Gemfile +1 -0
  8. data/Guardfile +5 -5
  9. data/README.md +155 -6
  10. data/Rakefile +11 -1
  11. data/chewy.gemspec +5 -7
  12. data/gemfiles/rails.3.2.activerecord.gemfile +1 -0
  13. data/gemfiles/rails.3.2.activerecord.kaminari.gemfile +1 -0
  14. data/gemfiles/rails.3.2.activerecord.will_paginate.gemfile +1 -0
  15. data/gemfiles/rails.4.2.activerecord.gemfile +1 -0
  16. data/gemfiles/rails.4.2.activerecord.kaminari.gemfile +1 -0
  17. data/gemfiles/rails.4.2.activerecord.will_paginate.gemfile +1 -0
  18. data/gemfiles/{rails.4.2.mongoid.4.0.0.gemfile → rails.4.2.mongoid.4.0.gemfile} +2 -1
  19. data/gemfiles/{rails.4.2.mongoid.4.0.0.kaminari.gemfile → rails.4.2.mongoid.4.0.kaminari.gemfile} +2 -1
  20. data/gemfiles/{rails.4.2.mongoid.4.0.0.will_paginate.gemfile → rails.4.2.mongoid.4.0.will_paginate.gemfile} +2 -1
  21. data/gemfiles/{rails.4.2.mongoid.5.1.0.gemfile → rails.4.2.mongoid.5.1.gemfile} +2 -1
  22. data/gemfiles/{rails.4.2.mongoid.5.1.0.kaminari.gemfile → rails.4.2.mongoid.5.1.kaminari.gemfile} +2 -1
  23. data/gemfiles/{rails.4.2.mongoid.5.1.0.will_paginate.gemfile → rails.4.2.mongoid.5.1.will_paginate.gemfile} +2 -1
  24. data/gemfiles/{rails.4.0.activerecord.gemfile → rails.5.0.activerecord.gemfile} +4 -2
  25. data/gemfiles/{rails.4.0.mongoid.4.0.0.kaminari.gemfile → rails.5.0.activerecord.kaminari.gemfile} +4 -2
  26. data/gemfiles/{rails.4.0.mongoid.4.0.0.will_paginate.gemfile → rails.5.0.activerecord.will_paginate.gemfile} +4 -2
  27. data/gemfiles/{sequel.4.31.gemfile → sequel.4.38.gemfile} +3 -2
  28. data/lib/chewy.rb +24 -16
  29. data/lib/chewy/backports/deep_dup.rb +1 -1
  30. data/lib/chewy/backports/duplicable.rb +1 -0
  31. data/lib/chewy/config.rb +13 -7
  32. data/lib/chewy/errors.rb +4 -4
  33. data/lib/chewy/fields/base.rb +19 -14
  34. data/lib/chewy/fields/root.rb +11 -9
  35. data/lib/chewy/index.rb +38 -25
  36. data/lib/chewy/index/actions.rb +17 -17
  37. data/lib/chewy/index/settings.rb +3 -4
  38. data/lib/chewy/journal.rb +107 -0
  39. data/lib/chewy/journal/apply.rb +31 -0
  40. data/lib/chewy/journal/clean.rb +24 -0
  41. data/lib/chewy/journal/entry.rb +83 -0
  42. data/lib/chewy/journal/query.rb +87 -0
  43. data/lib/chewy/log_subscriber.rb +8 -8
  44. data/lib/chewy/minitest.rb +1 -0
  45. data/lib/chewy/minitest/helpers.rb +77 -0
  46. data/lib/chewy/minitest/search_index_receiver.rb +80 -0
  47. data/lib/chewy/query.rb +116 -60
  48. data/lib/chewy/query/compose.rb +5 -6
  49. data/lib/chewy/query/criteria.rb +26 -16
  50. data/lib/chewy/query/filters.rb +9 -9
  51. data/lib/chewy/query/loading.rb +2 -2
  52. data/lib/chewy/query/nodes/and.rb +3 -3
  53. data/lib/chewy/query/nodes/base.rb +1 -1
  54. data/lib/chewy/query/nodes/bool.rb +6 -4
  55. data/lib/chewy/query/nodes/equal.rb +6 -6
  56. data/lib/chewy/query/nodes/exists.rb +2 -2
  57. data/lib/chewy/query/nodes/expr.rb +2 -2
  58. data/lib/chewy/query/nodes/field.rb +35 -31
  59. data/lib/chewy/query/nodes/has_child.rb +1 -0
  60. data/lib/chewy/query/nodes/has_parent.rb +1 -0
  61. data/lib/chewy/query/nodes/has_relation.rb +11 -13
  62. data/lib/chewy/query/nodes/match_all.rb +1 -1
  63. data/lib/chewy/query/nodes/missing.rb +2 -2
  64. data/lib/chewy/query/nodes/not.rb +3 -3
  65. data/lib/chewy/query/nodes/or.rb +3 -3
  66. data/lib/chewy/query/nodes/prefix.rb +4 -3
  67. data/lib/chewy/query/nodes/query.rb +3 -3
  68. data/lib/chewy/query/nodes/range.rb +11 -11
  69. data/lib/chewy/query/nodes/raw.rb +1 -1
  70. data/lib/chewy/query/nodes/regexp.rb +15 -11
  71. data/lib/chewy/query/nodes/script.rb +6 -6
  72. data/lib/chewy/query/pagination/will_paginate.rb +2 -2
  73. data/lib/chewy/railtie.rb +3 -3
  74. data/lib/chewy/rake_helper.rb +51 -30
  75. data/lib/chewy/repository.rb +2 -2
  76. data/lib/chewy/rspec.rb +1 -1
  77. data/lib/chewy/rspec/update_index.rb +46 -47
  78. data/lib/chewy/runtime/version.rb +4 -4
  79. data/lib/chewy/search.rb +7 -5
  80. data/lib/chewy/strategy.rb +10 -8
  81. data/lib/chewy/strategy/atomic.rb +2 -2
  82. data/lib/chewy/strategy/base.rb +4 -4
  83. data/lib/chewy/strategy/bypass.rb +1 -2
  84. data/lib/chewy/strategy/sidekiq.rb +2 -0
  85. data/lib/chewy/strategy/urgent.rb +1 -1
  86. data/lib/chewy/type.rb +51 -45
  87. data/lib/chewy/type/adapter/active_record.rb +23 -12
  88. data/lib/chewy/type/adapter/base.rb +4 -4
  89. data/lib/chewy/type/adapter/mongoid.rb +6 -6
  90. data/lib/chewy/type/adapter/object.rb +15 -12
  91. data/lib/chewy/type/adapter/orm.rb +24 -15
  92. data/lib/chewy/type/adapter/sequel.rb +11 -7
  93. data/lib/chewy/type/crutch.rb +4 -3
  94. data/lib/chewy/type/import.rb +51 -32
  95. data/lib/chewy/type/mapping.rb +17 -17
  96. data/lib/chewy/type/observe.rb +9 -7
  97. data/lib/chewy/type/witchcraft.rb +62 -23
  98. data/lib/chewy/type/wrapper.rb +20 -14
  99. data/lib/chewy/version.rb +1 -1
  100. data/lib/generators/chewy/install_generator.rb +3 -3
  101. data/lib/tasks/chewy.rake +28 -23
  102. data/spec/chewy/config_spec.rb +33 -12
  103. data/spec/chewy/fields/base_spec.rb +143 -154
  104. data/spec/chewy/fields/root_spec.rb +22 -20
  105. data/spec/chewy/fields/time_fields_spec.rb +11 -9
  106. data/spec/chewy/index/actions_spec.rb +27 -4
  107. data/spec/chewy/index/aliases_spec.rb +2 -2
  108. data/spec/chewy/index/settings_spec.rb +72 -50
  109. data/spec/chewy/index_spec.rb +95 -43
  110. data/spec/chewy/journal/apply_spec.rb +120 -0
  111. data/spec/chewy/journal/entry_spec.rb +237 -0
  112. data/spec/chewy/journal_spec.rb +173 -0
  113. data/spec/chewy/minitest/helpers_spec.rb +90 -0
  114. data/spec/chewy/minitest/search_index_receiver_spec.rb +120 -0
  115. data/spec/chewy/query/criteria_spec.rb +504 -237
  116. data/spec/chewy/query/filters_spec.rb +94 -66
  117. data/spec/chewy/query/loading_spec.rb +76 -40
  118. data/spec/chewy/query/nodes/and_spec.rb +3 -7
  119. data/spec/chewy/query/nodes/bool_spec.rb +5 -13
  120. data/spec/chewy/query/nodes/equal_spec.rb +20 -20
  121. data/spec/chewy/query/nodes/exists_spec.rb +7 -7
  122. data/spec/chewy/query/nodes/has_child_spec.rb +42 -23
  123. data/spec/chewy/query/nodes/has_parent_spec.rb +42 -23
  124. data/spec/chewy/query/nodes/match_all_spec.rb +2 -2
  125. data/spec/chewy/query/nodes/missing_spec.rb +6 -5
  126. data/spec/chewy/query/nodes/not_spec.rb +3 -7
  127. data/spec/chewy/query/nodes/or_spec.rb +3 -7
  128. data/spec/chewy/query/nodes/prefix_spec.rb +6 -6
  129. data/spec/chewy/query/nodes/query_spec.rb +3 -3
  130. data/spec/chewy/query/nodes/range_spec.rb +19 -19
  131. data/spec/chewy/query/nodes/raw_spec.rb +2 -2
  132. data/spec/chewy/query/nodes/regexp_spec.rb +31 -19
  133. data/spec/chewy/query/nodes/script_spec.rb +5 -5
  134. data/spec/chewy/query/pagination/kaminari_spec.rb +2 -2
  135. data/spec/chewy/query/pagination/will_paginage_spec.rb +6 -7
  136. data/spec/chewy/query/pagination_spec.rb +2 -3
  137. data/spec/chewy/query_spec.rb +208 -145
  138. data/spec/chewy/repository_spec.rb +8 -8
  139. data/spec/chewy/rspec/update_index_spec.rb +180 -111
  140. data/spec/chewy/search_spec.rb +8 -8
  141. data/spec/chewy/strategy/active_job_spec.rb +2 -2
  142. data/spec/chewy/strategy/atomic_spec.rb +4 -1
  143. data/spec/chewy/strategy/resque_spec.rb +2 -2
  144. data/spec/chewy/strategy/sidekiq_spec.rb +2 -2
  145. data/spec/chewy/type/actions_spec.rb +1 -1
  146. data/spec/chewy/type/adapter/active_record_spec.rb +255 -149
  147. data/spec/chewy/type/adapter/mongoid_spec.rb +169 -108
  148. data/spec/chewy/type/adapter/object_spec.rb +56 -40
  149. data/spec/chewy/type/adapter/sequel_spec.rb +248 -163
  150. data/spec/chewy/type/import_spec.rb +78 -47
  151. data/spec/chewy/type/mapping_spec.rb +6 -6
  152. data/spec/chewy/type/observe_spec.rb +20 -14
  153. data/spec/chewy/type/witchcraft_spec.rb +89 -43
  154. data/spec/chewy/type_spec.rb +4 -3
  155. data/spec/chewy_spec.rb +10 -8
  156. data/spec/spec_helper.rb +3 -0
  157. data/spec/support/active_record.rb +1 -1
  158. data/spec/support/class_helpers.rb +10 -11
  159. data/spec/support/mongoid.rb +2 -2
  160. data/spec/support/sequel.rb +1 -1
  161. metadata +65 -35
  162. data/gemfiles/rails.4.0.activerecord.kaminari.gemfile +0 -14
  163. data/gemfiles/rails.4.0.activerecord.will_paginate.gemfile +0 -14
  164. data/gemfiles/rails.4.0.mongoid.4.0.0.gemfile +0 -15
  165. data/gemfiles/rails.4.0.mongoid.5.1.0.gemfile +0 -15
  166. data/gemfiles/rails.4.0.mongoid.5.1.0.kaminari.gemfile +0 -14
  167. data/gemfiles/rails.4.0.mongoid.5.1.0.will_paginate.gemfile +0 -14
  168. data/gemfiles/rails.4.1.activerecord.gemfile +0 -15
  169. data/gemfiles/rails.4.1.activerecord.kaminari.gemfile +0 -14
  170. data/gemfiles/rails.4.1.activerecord.will_paginate.gemfile +0 -14
  171. data/gemfiles/rails.4.1.mongoid.4.0.0.gemfile +0 -15
  172. data/gemfiles/rails.4.1.mongoid.4.0.0.kaminari.gemfile +0 -14
  173. data/gemfiles/rails.4.1.mongoid.4.0.0.will_paginate.gemfile +0 -14
  174. data/gemfiles/rails.4.1.mongoid.5.1.0.gemfile +0 -15
  175. data/gemfiles/rails.4.1.mongoid.5.1.0.kaminari.gemfile +0 -14
  176. data/gemfiles/rails.4.1.mongoid.5.1.0.will_paginate.gemfile +0 -14
  177. data/gemfiles/rails.5.0.0.beta3.activerecord.gemfile +0 -16
  178. data/gemfiles/rails.5.0.0.beta3.activerecord.kaminari.gemfile +0 -16
  179. data/gemfiles/rails.5.0.0.beta3.activerecord.will_paginate.gemfile +0 -15
@@ -0,0 +1,120 @@
1
+ require 'spec_helper'
2
+
3
+ describe Chewy::Journal::Apply do
4
+ describe '.since' do
5
+ context 'with an index filter' do
6
+ let(:time) { Time.now }
7
+
8
+ before do
9
+ stub_model(:city) do
10
+ update_index 'city', :self
11
+ end
12
+ stub_model(:country) do
13
+ update_index 'country', :self
14
+ end
15
+
16
+ stub_index('city') do
17
+ define_type City do
18
+ default_import_options journal: true
19
+ end
20
+ end
21
+ stub_index('country') do
22
+ define_type Country do
23
+ default_import_options journal: true
24
+ end
25
+ end
26
+
27
+ Chewy.massacre
28
+ Timecop.freeze(time)
29
+ end
30
+
31
+ specify do
32
+ Chewy.strategy(:urgent) do
33
+ Array.new(2) { |i| City.create!(id: i + 1) }
34
+ Array.new(2) { |i| Country.create!(id: i + 1) }
35
+
36
+ # simulate lost data
37
+ Chewy.client.delete(index: 'city', type: 'city', id: 1, refresh: true)
38
+ Chewy.client.delete(index: 'country', type: 'country', id: 1, refresh: true)
39
+ expect(CityIndex.all.to_a.length).to eq 1
40
+ expect(CountryIndex.all.to_a.length).to eq 1
41
+
42
+ # Replay on specific index
43
+ described_class.since(time, only: [CityIndex])
44
+ expect(CityIndex.all.to_a.length).to eq 2
45
+ expect(CountryIndex.all.to_a.length).to eq 1
46
+
47
+ # Replay on both
48
+ Chewy.client.delete(index: 'city', type: 'city', id: 1, refresh: true)
49
+ expect(CityIndex.all.to_a.length).to eq 1
50
+ described_class.since(time, only: [CityIndex, CountryIndex])
51
+ expect(CityIndex.all.to_a.length).to eq 2
52
+ expect(CountryIndex.all.to_a.length).to eq 2
53
+ end
54
+ end
55
+ end
56
+
57
+ context 'retries' do
58
+ let(:time) { Time.now.to_i }
59
+ before do
60
+ stub_model(:city) do
61
+ update_index 'city', :self
62
+ end
63
+ stub_index('city') do
64
+ define_type City do
65
+ default_import_options journal: true
66
+ end
67
+ end
68
+ Chewy.massacre
69
+ Timecop.freeze
70
+ Chewy.strategy(:urgent)
71
+ City.create!(id: 1)
72
+ end
73
+
74
+ after do
75
+ Chewy.strategy.pop
76
+ end
77
+
78
+ specify 'journal was cleaned after the first call' do
79
+ expect(Chewy::Journal::Entry)
80
+ .to receive(:since).exactly(2).and_call_original
81
+ Chewy::Journal::Apply.since(time)
82
+ end
83
+
84
+ context 'endless journal' do
85
+ let(:count_of_checks) { 10 } # default
86
+ let!(:journal_records) do
87
+ record = Chewy::Journal::Entry.since(time).first
88
+ Array.new(count_of_checks) do |i|
89
+ r = record.dup
90
+ r.created_at = time.to_i + i
91
+ r.object_ids = [i]
92
+ r
93
+ end
94
+ end
95
+
96
+ specify '10 retries by default' do
97
+ expect(Chewy::Journal::Entry)
98
+ .to receive(:since).exactly(count_of_checks) { [journal_records.shift].compact }
99
+ Chewy::Journal::Apply.since(time)
100
+ end
101
+
102
+ specify 'with :once parameter set' do
103
+ expect(Chewy::Journal::Entry)
104
+ .to receive(:since).exactly(1) { [journal_records.shift].compact }
105
+ Chewy::Journal::Apply.since(time, once: true)
106
+ end
107
+
108
+ context 'with retries parameter set' do
109
+ let(:retries) { 5 }
110
+
111
+ specify do
112
+ expect(Chewy::Journal::Entry)
113
+ .to receive(:since).exactly(retries) { [journal_records.shift].compact }
114
+ Chewy::Journal::Apply.since(time, retries: retries)
115
+ end
116
+ end
117
+ end
118
+ end
119
+ end
120
+ end
@@ -0,0 +1,237 @@
1
+ require 'spec_helper'
2
+
3
+ describe Chewy::Journal::Entry do
4
+ before do
5
+ stub_model(:city) do
6
+ update_index 'city', :self
7
+ end
8
+ stub_index('city') do
9
+ define_type City do
10
+ default_import_options journal: true
11
+ end
12
+ end
13
+ end
14
+
15
+ describe '.since' do
16
+ let(:time) { Time.now.to_i }
17
+ before do
18
+ Timecop.freeze(time)
19
+ Chewy.massacre
20
+ Chewy.strategy(:urgent) { City.create!(id: 1) }
21
+ end
22
+ subject { described_class.since(time) }
23
+
24
+ its(:length) { is_expected.to eq(1) }
25
+
26
+ context 'with indices parameter provided' do
27
+ subject { described_class.since(time, indices) }
28
+
29
+ context 'it ignores empty array' do
30
+ let(:indices) { [] }
31
+ its(:length) { is_expected.to eq(1) }
32
+ end
33
+
34
+ context do
35
+ let(:indices) { [CityIndex::City2] }
36
+ before do
37
+ stub_index('city') do
38
+ define_type :city2 do
39
+ default_import_options journal: true
40
+ end
41
+ end
42
+ end
43
+
44
+ its(:length) { is_expected.to eq(1) }
45
+ end
46
+ end
47
+ end
48
+
49
+ describe '.group' do
50
+ let(:full_type_name) { 'type' }
51
+ let(:another_full_type_name) { 'type' }
52
+ let(:entry) { described_class.new('object_ids' => [1]) }
53
+ let(:another_entry) { described_class.new('object_ids' => [2]) }
54
+ before do
55
+ allow(entry)
56
+ .to receive(:full_type_name).and_return(full_type_name)
57
+ allow(another_entry)
58
+ .to receive(:full_type_name).and_return(another_full_type_name)
59
+ end
60
+ subject { described_class.group([entry, another_entry]) }
61
+
62
+ specify do
63
+ expect(subject.length).to eq(1)
64
+ expect(subject.first.object_ids).to eq([1, 2])
65
+ end
66
+
67
+ context do
68
+ let(:another_full_type_name) { 'whatever' }
69
+
70
+ specify do
71
+ expect(subject.length).to eq(2)
72
+ expect(subject.first.object_ids).to eq(entry.object_ids)
73
+ expect(subject.last.object_ids).to eq(another_entry.object_ids)
74
+ end
75
+ end
76
+ end
77
+
78
+ describe '.recent_timestamp' do
79
+ let(:time) { Time.now.to_i }
80
+ let(:entry) { described_class.new('created_at' => time) }
81
+ let(:another_entry) { described_class.new('created_at' => time + 1) }
82
+ subject { described_class.recent_timestamp([entry, another_entry]) }
83
+
84
+ it { is_expected.to eq(time + 1) }
85
+ end
86
+
87
+ describe '.subtract' do
88
+ let(:full_type_name) { 'type' }
89
+ let(:another_full_type_name) { 'type' }
90
+ let(:entry) { described_class.new('object_ids' => [1]) }
91
+ let(:another_entry) { described_class.new('object_ids' => [2]) }
92
+ before do
93
+ allow(entry)
94
+ .to receive(:full_type_name).and_return(full_type_name)
95
+ allow(another_entry)
96
+ .to receive(:full_type_name).and_return(another_full_type_name)
97
+ end
98
+ let(:from) { [entry] }
99
+ let(:what) { [another_entry] }
100
+ subject { described_class.subtract(from, what) }
101
+
102
+ specify { expect { subject }.not_to change { from.length } }
103
+ specify { expect(entry.object_ids).to eq([1]) }
104
+
105
+ context 'object_ids have same elements' do
106
+ let(:another_entry) { described_class.new('object_ids' => [1, 2]) }
107
+
108
+ specify { expect { subject }.to change { from.length }.from(1).to(0) }
109
+ specify do
110
+ subject
111
+ expect(entry.object_ids).to eq([])
112
+ end
113
+
114
+ context 'not all elements are covered by subtracting array' do
115
+ let(:entry) { described_class.new('object_ids' => [1, 3]) }
116
+
117
+ specify { expect { subject }.not_to change { from.length } }
118
+ specify do
119
+ subject
120
+ expect(entry.object_ids).to eq([3])
121
+ end
122
+ end
123
+ end
124
+ end
125
+
126
+ describe '#merge' do
127
+ let(:time) { Time.now.to_i }
128
+ let(:index_name) { 'index' }
129
+ let(:type_name) { 'type' }
130
+ let(:object_ids) { [1] }
131
+ let(:entry) do
132
+ described_class.new('index_name' => index_name,
133
+ 'type_name' => type_name,
134
+ 'object_ids' => object_ids,
135
+ 'created_at' => time)
136
+ end
137
+ let(:another_index_name) { 'index' }
138
+ let(:another_type_name) { 'type' }
139
+ let(:another_object_ids) { [2] }
140
+ let(:another_time) { time + 1 }
141
+ let(:another_entry) do
142
+ described_class.new('index_name' => another_index_name,
143
+ 'type_name' => another_type_name,
144
+ 'object_ids' => another_object_ids,
145
+ 'created_at' => another_time)
146
+ end
147
+ subject { entry.merge(another_entry) }
148
+
149
+ specify do
150
+ expect(subject).to eq(entry)
151
+ expect(entry.created_at).to eq(another_entry.created_at)
152
+ expect(entry.object_ids).to eq([1, 2])
153
+ end
154
+
155
+ shared_examples_for 'merge does not change the entry' do
156
+ it { is_expected.to eq(entry) }
157
+ specify { expect { subject }.not_to change(entry, :created_at) }
158
+ specify { expect { subject }.not_to change(entry, :object_ids) }
159
+ end
160
+
161
+ context 'different types' do
162
+ let(:another_type_name) { 'whatever' }
163
+ include_examples 'merge does not change the entry'
164
+ end
165
+
166
+ context 'merge with nil' do
167
+ let(:another_entry) { nil }
168
+ include_examples 'merge does not change the entry'
169
+ end
170
+
171
+ context 'original entry has more recent time' do
172
+ let(:another_time) { time - 1 }
173
+ specify { expect { subject }.not_to change(entry, :created_at) }
174
+ end
175
+ end
176
+
177
+ describe '#empty?' do
178
+ let(:object_ids) { [] }
179
+ subject { described_class.new('object_ids' => object_ids).empty? }
180
+
181
+ it { is_expected.to eq(true) }
182
+
183
+ context do
184
+ let(:object_ids) { nil }
185
+ it { is_expected.to eq(true) }
186
+ end
187
+
188
+ context do
189
+ let(:object_ids) { [1] }
190
+ it { is_expected.to eq(false) }
191
+ end
192
+ end
193
+
194
+ describe '#full_type_name' do
195
+ subject do
196
+ described_class.new('index_name' => 'index', 'type_name' => 'type')
197
+ .full_type_name
198
+ end
199
+ it { is_expected.to eq('index#type') }
200
+ end
201
+
202
+ describe '#index' do
203
+ let(:index_name) { 'wrong_index_name' }
204
+ let(:type_name) { 'city' }
205
+ subject do
206
+ described_class
207
+ .new('index_name' => index_name, 'type_name' => type_name).index
208
+ end
209
+
210
+ specify { expect { subject }.to raise_error(Chewy::UnderivableType) }
211
+
212
+ context do
213
+ let(:index_name) { 'city' }
214
+ it { is_expected.to eq(CityIndex::City) }
215
+ end
216
+ end
217
+
218
+ describe '#==' do
219
+ let(:full_type_name) { 'type' }
220
+ let(:another_full_type_name) { 'type' }
221
+ let(:entry) { described_class.new }
222
+ let(:another_entry) { described_class.new }
223
+ before do
224
+ expect(entry).to receive(:full_type_name).and_return(full_type_name)
225
+ expect(another_entry)
226
+ .to receive(:full_type_name).and_return(another_full_type_name)
227
+ end
228
+ subject { entry == another_entry }
229
+
230
+ it { is_expected.to eq(true) }
231
+
232
+ context do
233
+ let(:another_full_type_name) { 'whatever' }
234
+ it { is_expected.to eq(false) }
235
+ end
236
+ end
237
+ end
@@ -0,0 +1,173 @@
1
+ require 'spec_helper'
2
+
3
+ describe Chewy::Journal do
4
+ context 'journaling', orm: true do
5
+ ['', 'namespace/'].each do |namespace|
6
+ context namespace.present? ? 'with namespace' : 'without namespace' do
7
+ before do
8
+ stub_model(:city) do
9
+ update_index "#{namespace}places#city", :self
10
+ end
11
+ stub_model(:country) do
12
+ update_index "#{namespace}places#country", :self
13
+ end
14
+
15
+ stub_index("#{namespace}places") do
16
+ define_type City do
17
+ default_import_options journal: true
18
+ end
19
+ define_type Country do
20
+ default_import_options journal: true
21
+ end
22
+ end
23
+
24
+ Chewy.massacre
25
+ begin
26
+ Chewy.client.indices.delete(index: Chewy::Journal.index_name)
27
+ rescue Elasticsearch::Transport::Transport::Errors::NotFound
28
+ nil
29
+ end
30
+ Chewy.settings[:prefix] = 'some_prefix'
31
+ Timecop.freeze(time)
32
+ end
33
+
34
+ after do
35
+ Chewy.settings[:prefix] = nil
36
+ Timecop.return
37
+ end
38
+
39
+ let(:time) { Time.now }
40
+ let(:import_time) { time + 1 }
41
+ let(:update_time) { time + 2 }
42
+ let(:destroy_time) { time + 3 }
43
+
44
+ def timestamp(time)
45
+ time.to_i
46
+ end
47
+
48
+ specify do
49
+ places_index = namespace.present? ? Namespace::PlacesIndex : PlacesIndex
50
+ Chewy.strategy(:urgent) do
51
+ cities = Array.new(2) { |i| City.create!(id: i + 1) }
52
+ countries = Array.new(2) { |i| Country.create!(id: i + 1) }
53
+ Country.create!(id: 3)
54
+
55
+ Timecop.freeze(import_time)
56
+
57
+ places_index.import
58
+
59
+ expect(Chewy.client.indices.exists?(index: Chewy::Journal.index_name)).to eq true
60
+
61
+ Timecop.freeze(update_time)
62
+ cities.first.update_attributes!(name: 'Supername')
63
+
64
+ Timecop.freeze(destroy_time)
65
+ countries.last.destroy
66
+
67
+ journal_records = Chewy.client.search(index: Chewy::Journal.index_name, type: Chewy::Journal.type_name, sort: 'created_at')['hits']['hits'].map { |r| r['_source'] }
68
+
69
+ expected_journal = [
70
+ {
71
+ 'index_name' => "#{namespace}places",
72
+ 'type_name' => 'city',
73
+ 'action' => 'index',
74
+ 'object_ids' => [1],
75
+ 'created_at' => time.to_i
76
+ },
77
+ {
78
+ 'index_name' => "#{namespace}places",
79
+ 'type_name' => 'city',
80
+ 'action' => 'index',
81
+ 'object_ids' => [2],
82
+ 'created_at' => time.to_i
83
+ },
84
+ {
85
+ 'index_name' => "#{namespace}places",
86
+ 'type_name' => 'country',
87
+ 'action' => 'index',
88
+ 'object_ids' => [1],
89
+ 'created_at' => time.to_i
90
+ },
91
+ {
92
+ 'index_name' => "#{namespace}places",
93
+ 'type_name' => 'country',
94
+ 'action' => 'index',
95
+ 'object_ids' => [2],
96
+ 'created_at' => time.to_i
97
+ },
98
+ {
99
+ 'index_name' => "#{namespace}places",
100
+ 'type_name' => 'country',
101
+ 'action' => 'index',
102
+ 'object_ids' => [3],
103
+ 'created_at' => time.to_i
104
+ },
105
+ {
106
+ 'index_name' => "#{namespace}places",
107
+ 'type_name' => 'city',
108
+ 'action' => 'index',
109
+ 'object_ids' => [1, 2],
110
+ 'created_at' => import_time.to_i
111
+ },
112
+ {
113
+ 'index_name' => "#{namespace}places",
114
+ 'type_name' => 'country',
115
+ 'action' => 'index',
116
+ 'object_ids' => [1, 2, 3],
117
+ 'created_at' => import_time.to_i
118
+ },
119
+ {
120
+ 'index_name' => "#{namespace}places",
121
+ 'type_name' => 'city',
122
+ 'action' => 'index',
123
+ 'object_ids' => [1],
124
+ 'created_at' => update_time.to_i
125
+ },
126
+ {
127
+ 'index_name' => "#{namespace}places",
128
+ 'type_name' => 'country',
129
+ 'action' => 'delete',
130
+ 'object_ids' => [2],
131
+ 'created_at' => destroy_time.to_i
132
+ }
133
+ ]
134
+
135
+ expect(Chewy.client.count(index: Chewy::Journal.index_name)['count']).to eq 9
136
+ expect(journal_records).to eq expected_journal
137
+
138
+ journal_entries = Chewy::Journal::Entry.since(import_time)
139
+ expect(journal_entries.length).to eq 4
140
+ # we have only 2 types, so we can group all journal entries(4) into 2
141
+ expect(Chewy::Journal::Entry.group(journal_entries)).to eq [
142
+ Chewy::Journal::Entry.new('index_name' => "#{namespace}places",
143
+ 'type_name' => 'city',
144
+ 'action' => nil,
145
+ 'object_ids' => [1, 2],
146
+ 'created_at' => nil),
147
+ Chewy::Journal::Entry.new('index_name' => "#{namespace}places",
148
+ 'type_name' => 'country',
149
+ 'action' => 'delete',
150
+ 'object_ids' => [1, 2, 3],
151
+ 'created_at' => destroy_time.to_i)
152
+ ]
153
+
154
+ # simulate lost data
155
+ Chewy.client.delete(index: "#{Chewy.settings[:prefix]}_places", type: 'city', id: 1, refresh: true)
156
+ expect(places_index::City.all.to_a.length).to eq 1
157
+
158
+ Chewy::Journal::Apply.since(time)
159
+ expect(places_index::City.all.to_a.length).to eq 2
160
+
161
+ expect(Chewy::Journal::Clean.until(import_time)).to eq 7
162
+ expect(Chewy.client.count(index: Chewy::Journal.index_name)['count']).to eq 2
163
+
164
+ expect(Chewy::Journal.delete!).to be_truthy
165
+ expect { Chewy::Journal.delete! }.to raise_error(Elasticsearch::Transport::Transport::Errors::NotFound)
166
+ expect(Chewy::Journal.delete).to eq false
167
+ expect(Chewy::Journal.exists?).to eq false
168
+ end
169
+ end
170
+ end
171
+ end
172
+ end
173
+ end