elasticsearch-model 6.0.0 → 6.1.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 (118) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +5 -0
  3. data/README.md +14 -7
  4. data/Rakefile +27 -36
  5. data/elasticsearch-model.gemspec +1 -1
  6. data/examples/activerecord_mapping_completion.rb +2 -15
  7. data/gemfiles/3.0.gemfile +6 -1
  8. data/gemfiles/4.0.gemfile +7 -1
  9. data/gemfiles/5.0.gemfile +6 -0
  10. data/lib/elasticsearch/model.rb +15 -8
  11. data/lib/elasticsearch/model/adapters/active_record.rb +7 -26
  12. data/lib/elasticsearch/model/indexing.rb +5 -3
  13. data/lib/elasticsearch/model/naming.rb +6 -1
  14. data/lib/elasticsearch/model/response.rb +2 -2
  15. data/lib/elasticsearch/model/response/pagination.rb +2 -192
  16. data/lib/elasticsearch/model/response/pagination/kaminari.rb +109 -0
  17. data/lib/elasticsearch/model/response/pagination/will_paginate.rb +95 -0
  18. data/lib/elasticsearch/model/response/result.rb +1 -1
  19. data/lib/elasticsearch/model/version.rb +1 -1
  20. data/spec/elasticsearch/model/adapter_spec.rb +119 -0
  21. data/spec/elasticsearch/model/adapters/active_record/associations_spec.rb +334 -0
  22. data/spec/elasticsearch/model/adapters/active_record/basic_spec.rb +340 -0
  23. data/spec/elasticsearch/model/adapters/active_record/dynamic_index_name_spec.rb +18 -0
  24. data/spec/elasticsearch/model/adapters/active_record/import_spec.rb +187 -0
  25. data/spec/elasticsearch/model/adapters/active_record/multi_model_spec.rb +110 -0
  26. data/spec/elasticsearch/model/adapters/active_record/namespaced_model_spec.rb +38 -0
  27. data/spec/elasticsearch/model/adapters/active_record/pagination_spec.rb +315 -0
  28. data/spec/elasticsearch/model/adapters/active_record/parent_child_spec.rb +75 -0
  29. data/spec/elasticsearch/model/adapters/active_record/serialization_spec.rb +61 -0
  30. data/spec/elasticsearch/model/adapters/active_record_spec.rb +207 -0
  31. data/spec/elasticsearch/model/adapters/default_spec.rb +41 -0
  32. data/spec/elasticsearch/model/adapters/mongoid/basic_spec.rb +267 -0
  33. data/spec/elasticsearch/model/adapters/mongoid/multi_model_spec.rb +66 -0
  34. data/spec/elasticsearch/model/adapters/mongoid_spec.rb +235 -0
  35. data/spec/elasticsearch/model/adapters/multiple_spec.rb +125 -0
  36. data/spec/elasticsearch/model/callbacks_spec.rb +33 -0
  37. data/spec/elasticsearch/model/client_spec.rb +66 -0
  38. data/spec/elasticsearch/model/hash_wrapper_spec.rb +12 -0
  39. data/spec/elasticsearch/model/importing_spec.rb +214 -0
  40. data/spec/elasticsearch/model/indexing_spec.rb +918 -0
  41. data/spec/elasticsearch/model/module_spec.rb +101 -0
  42. data/spec/elasticsearch/model/multimodel_spec.rb +55 -0
  43. data/spec/elasticsearch/model/naming_inheritance_spec.rb +184 -0
  44. data/spec/elasticsearch/model/naming_spec.rb +186 -0
  45. data/spec/elasticsearch/model/proxy_spec.rb +107 -0
  46. data/spec/elasticsearch/model/response/aggregations_spec.rb +66 -0
  47. data/spec/elasticsearch/model/response/base_spec.rb +90 -0
  48. data/spec/elasticsearch/model/response/pagination/kaminari_spec.rb +410 -0
  49. data/spec/elasticsearch/model/response/pagination/will_paginate_spec.rb +262 -0
  50. data/spec/elasticsearch/model/response/records_spec.rb +118 -0
  51. data/spec/elasticsearch/model/response/response_spec.rb +131 -0
  52. data/spec/elasticsearch/model/response/result_spec.rb +122 -0
  53. data/spec/elasticsearch/model/response/results_spec.rb +56 -0
  54. data/spec/elasticsearch/model/searching_search_request_spec.rb +112 -0
  55. data/spec/elasticsearch/model/searching_spec.rb +49 -0
  56. data/spec/elasticsearch/model/serializing_spec.rb +22 -0
  57. data/spec/spec_helper.rb +161 -0
  58. data/spec/support/app.rb +21 -0
  59. data/spec/support/app/answer.rb +33 -0
  60. data/spec/support/app/article.rb +22 -0
  61. data/spec/support/app/article_for_pagination.rb +12 -0
  62. data/spec/support/app/article_with_custom_serialization.rb +13 -0
  63. data/spec/support/app/article_with_dynamic_index_name.rb +15 -0
  64. data/spec/support/app/author.rb +9 -0
  65. data/spec/support/app/authorship.rb +4 -0
  66. data/spec/support/app/category.rb +3 -0
  67. data/spec/support/app/comment.rb +3 -0
  68. data/spec/support/app/episode.rb +11 -0
  69. data/spec/support/app/image.rb +19 -0
  70. data/spec/support/app/import_article.rb +12 -0
  71. data/spec/support/app/mongoid_article.rb +21 -0
  72. data/spec/support/app/namespaced_book.rb +10 -0
  73. data/spec/support/app/parent_and_child_searchable.rb +24 -0
  74. data/spec/support/app/post.rb +14 -0
  75. data/spec/support/app/question.rb +27 -0
  76. data/spec/support/app/searchable.rb +48 -0
  77. data/spec/support/app/series.rb +11 -0
  78. data/spec/support/model.json +1 -0
  79. data/{test → spec}/support/model.yml +0 -0
  80. metadata +129 -86
  81. data/test/integration/active_record_associations_parent_child_test.rb +0 -188
  82. data/test/integration/active_record_associations_test.rb +0 -339
  83. data/test/integration/active_record_basic_test.rb +0 -255
  84. data/test/integration/active_record_custom_serialization_test.rb +0 -67
  85. data/test/integration/active_record_import_test.rb +0 -168
  86. data/test/integration/active_record_namespaced_model_test.rb +0 -56
  87. data/test/integration/active_record_pagination_test.rb +0 -149
  88. data/test/integration/dynamic_index_name_test.rb +0 -52
  89. data/test/integration/mongoid_basic_test.rb +0 -240
  90. data/test/integration/multiple_models_test.rb +0 -176
  91. data/test/support/model.json +0 -1
  92. data/test/test_helper.rb +0 -92
  93. data/test/unit/adapter_active_record_test.rb +0 -157
  94. data/test/unit/adapter_default_test.rb +0 -41
  95. data/test/unit/adapter_mongoid_test.rb +0 -161
  96. data/test/unit/adapter_multiple_test.rb +0 -106
  97. data/test/unit/adapter_test.rb +0 -69
  98. data/test/unit/callbacks_test.rb +0 -31
  99. data/test/unit/client_test.rb +0 -27
  100. data/test/unit/hash_wrapper_test.rb +0 -13
  101. data/test/unit/importing_test.rb +0 -224
  102. data/test/unit/indexing_test.rb +0 -720
  103. data/test/unit/module_test.rb +0 -68
  104. data/test/unit/multimodel_test.rb +0 -38
  105. data/test/unit/naming_inheritance_test.rb +0 -94
  106. data/test/unit/naming_test.rb +0 -103
  107. data/test/unit/proxy_test.rb +0 -98
  108. data/test/unit/response_aggregations_test.rb +0 -46
  109. data/test/unit/response_base_test.rb +0 -40
  110. data/test/unit/response_pagination_kaminari_test.rb +0 -433
  111. data/test/unit/response_pagination_will_paginate_test.rb +0 -398
  112. data/test/unit/response_records_test.rb +0 -91
  113. data/test/unit/response_result_test.rb +0 -90
  114. data/test/unit/response_results_test.rb +0 -34
  115. data/test/unit/response_test.rb +0 -104
  116. data/test/unit/searching_search_request_test.rb +0 -78
  117. data/test/unit/searching_test.rb +0 -41
  118. data/test/unit/serializing_test.rb +0 -17
@@ -0,0 +1,107 @@
1
+ require 'spec_helper'
2
+
3
+ describe Elasticsearch::Model::Proxy do
4
+
5
+ before(:all) do
6
+ class ::DummyProxyModel
7
+ include Elasticsearch::Model::Proxy
8
+
9
+ def self.foo
10
+ 'classy foo'
11
+ end
12
+
13
+ def bar
14
+ 'insta barr'
15
+ end
16
+
17
+ def as_json(options)
18
+ {foo: 'bar'}
19
+ end
20
+ end
21
+
22
+ class ::DummyProxyModelWithCallbacks
23
+ def self.before_save(&block)
24
+ (@callbacks ||= {})[block.hash] = block
25
+ end
26
+
27
+ def changes_to_save
28
+ {:foo => ['One', 'Two']}
29
+ end
30
+ end
31
+
32
+ DummyProxyModelWithCallbacks.__send__ :include, Elasticsearch::Model::Proxy
33
+ end
34
+
35
+ after(:all) do
36
+ remove_classes(DummyProxyModel, DummyProxyModelWithCallbacks)
37
+ end
38
+
39
+ it 'sets up a proxy method on the class' do
40
+ expect(DummyProxyModel).to respond_to(:__elasticsearch__)
41
+ end
42
+
43
+ it 'sets up a proxy method on instances' do
44
+ expect(DummyProxyModel.new).to respond_to(:__elasticsearch__)
45
+ end
46
+
47
+ it 'sets up hooks for before_save callbacks' do
48
+ expect(DummyProxyModelWithCallbacks).to respond_to(:before_save)
49
+ end
50
+
51
+ it 'delegates methods to the target' do
52
+ expect(DummyProxyModel.__elasticsearch__).to respond_to(:foo)
53
+ expect(DummyProxyModel.__elasticsearch__.foo).to eq('classy foo')
54
+ expect(DummyProxyModel.new.__elasticsearch__).to respond_to(:bar)
55
+ expect(DummyProxyModel.new.__elasticsearch__.bar).to eq('insta barr')
56
+
57
+ expect {
58
+ DummyProxyModel.__elasticsearch__.xoxo
59
+ }.to raise_exception(NoMethodError)
60
+
61
+ expect {
62
+ DummyProxyModel.new.__elasticsearch__.xoxo
63
+ }.to raise_exception(NoMethodError)
64
+ end
65
+
66
+ it 'returns the proxy class from an instance proxy' do
67
+ expect(DummyProxyModel.new.__elasticsearch__.class.class).to eq(Elasticsearch::Model::Proxy::ClassMethodsProxy)
68
+ end
69
+
70
+ it 'returns the origin class from an instance proxy' do
71
+ expect(DummyProxyModel.new.__elasticsearch__.klass).to eq(DummyProxyModel)
72
+ end
73
+
74
+ it 'delegates #as_json from the proxy to the target' do
75
+ expect(DummyProxyModel.new.__elasticsearch__.as_json).to eq(foo: 'bar')
76
+ end
77
+
78
+ it 'includes the proxy in the inspect string' do
79
+ expect(DummyProxyModel.__elasticsearch__.inspect).to match(/PROXY/)
80
+ expect(DummyProxyModel.new.__elasticsearch__.inspect).to match(/PROXY/)
81
+ end
82
+
83
+ context 'when instances are cloned' do
84
+
85
+ let!(:model) do
86
+ DummyProxyModel.new
87
+ end
88
+
89
+ let!(:model_target) do
90
+ model.__elasticsearch__.target
91
+ end
92
+
93
+ let!(:duplicate) do
94
+ model.dup
95
+ end
96
+
97
+ let!(:duplicate_target) do
98
+ duplicate.__elasticsearch__.target
99
+ end
100
+
101
+ it 'resets the proxy target' do
102
+ expect(model).not_to eq(duplicate)
103
+ expect(model).to eq(model_target)
104
+ expect(duplicate).to eq(duplicate_target)
105
+ end
106
+ end
107
+ end
@@ -0,0 +1,66 @@
1
+ require 'spec_helper'
2
+
3
+ describe Elasticsearch::Model::Response::Aggregations do
4
+
5
+ before(:all) do
6
+ class OriginClass
7
+ def self.index_name; 'foo'; end
8
+ def self.document_type; 'bar'; end
9
+ end
10
+ end
11
+
12
+ after(:all) do
13
+ remove_classes(OriginClass)
14
+ end
15
+
16
+ let(:response_document) do
17
+ {
18
+ 'aggregations' => {
19
+ 'foo' => {'bar' => 10 },
20
+ 'price' => { 'doc_count' => 123,
21
+ 'min' => { 'value' => 1.0},
22
+ 'max' => { 'value' => 99 }
23
+ }
24
+ }
25
+ }
26
+ end
27
+
28
+ let(:search) do
29
+ Elasticsearch::Model::Searching::SearchRequest.new(OriginClass, '*').tap do |request|
30
+ allow(request).to receive(:execute!).and_return(response_document)
31
+ end
32
+ end
33
+
34
+ let(:aggregations) do
35
+ Elasticsearch::Model::Response::Response.new(OriginClass, search).aggregations
36
+ end
37
+
38
+ describe 'method delegation' do
39
+
40
+ it 'delegates methods to the response document' do
41
+ expect(aggregations.foo).to be_a(Hashie::Mash)
42
+ expect(aggregations.foo.bar).to be(10)
43
+ end
44
+ end
45
+
46
+ describe '#doc_count' do
47
+
48
+ it 'returns the doc count value from the response document' do
49
+ expect(aggregations.price.doc_count).to eq(123)
50
+ end
51
+ end
52
+
53
+ describe '#min' do
54
+
55
+ it 'returns the min value from the response document' do
56
+ expect(aggregations.price.min.value).to eq(1)
57
+ end
58
+ end
59
+
60
+ describe '#max' do
61
+
62
+ it 'returns the max value from the response document' do
63
+ expect(aggregations.price.max.value).to eq(99)
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,90 @@
1
+ require 'spec_helper'
2
+
3
+ describe Elasticsearch::Model::Response::Base do
4
+
5
+ before(:all) do
6
+ class DummyBaseClass
7
+ include Elasticsearch::Model::Response::Base
8
+ end
9
+
10
+ class OriginClass
11
+ def self.index_name; 'foo'; end
12
+ def self.document_type; 'bar'; end
13
+ end
14
+ end
15
+
16
+ after(:all) do
17
+ remove_classes(DummyBaseClass, OriginClass)
18
+ end
19
+
20
+ let(:response_document) do
21
+ { 'hits' => { 'total' => 123, 'max_score' => 456, 'hits' => [] } }
22
+ end
23
+
24
+ let(:search) do
25
+ Elasticsearch::Model::Searching::SearchRequest.new(OriginClass, '*').tap do |request|
26
+ allow(request).to receive(:execute!).and_return(response_document)
27
+ end
28
+ end
29
+
30
+ let(:response) do
31
+ Elasticsearch::Model::Response::Response.new(OriginClass, search)
32
+ end
33
+
34
+ let(:response_base) do
35
+ DummyBaseClass.new(OriginClass, response)
36
+ end
37
+
38
+ describe '#klass' do
39
+
40
+ it 'returns the class' do
41
+ expect(response.klass).to be(OriginClass)
42
+ end
43
+ end
44
+
45
+ describe '#response' do
46
+
47
+ it 'returns the response object' do
48
+ expect(response_base.response).to eq(response)
49
+ end
50
+ end
51
+
52
+ describe 'response document' do
53
+
54
+ it 'returns the response document' do
55
+ expect(response_base.response.response).to eq(response_document)
56
+ end
57
+ end
58
+
59
+ describe '#total' do
60
+
61
+ it 'returns the total' do
62
+ expect(response_base.total).to eq(123)
63
+ end
64
+ end
65
+
66
+ describe '#max_score' do
67
+
68
+ it 'returns the total' do
69
+ expect(response_base.max_score).to eq(456)
70
+ end
71
+ end
72
+
73
+ describe '#results' do
74
+
75
+ it 'raises a NotImplemented error' do
76
+ expect {
77
+ response_base.results
78
+ }.to raise_exception(Elasticsearch::Model::NotImplemented)
79
+ end
80
+ end
81
+
82
+ describe '#records' do
83
+
84
+ it 'raises a NotImplemented error' do
85
+ expect {
86
+ response_base.records
87
+ }.to raise_exception(Elasticsearch::Model::NotImplemented)
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,410 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'Elasticsearch::Model::Response::Response Kaminari' do
4
+
5
+ before(:all) do
6
+ class ModelClass
7
+ include ::Kaminari::ConfigurationMethods
8
+ def self.index_name; 'foo'; end
9
+ def self.document_type; 'bar'; end
10
+ end
11
+ end
12
+
13
+ after(:all) do
14
+ remove_classes(ModelClass)
15
+ end
16
+
17
+ let(:response_document) do
18
+ { 'took' => '5', 'timed_out' => false, '_shards' => {'one' => 'OK'},
19
+ 'hits' => { 'total' => 100, 'hits' => (1..100).to_a.map { |i| { _id: i } } } }
20
+ end
21
+
22
+ let(:search) do
23
+ Elasticsearch::Model::Searching::SearchRequest.new(model, '*')
24
+ end
25
+
26
+ let(:response) do
27
+ allow(model).to receive(:client).and_return(client)
28
+ Elasticsearch::Model::Response::Response.new(model, search, response_document).tap do |resp|
29
+ allow(resp).to receive(:client).and_return(client)
30
+ end
31
+ end
32
+
33
+ let(:client) do
34
+ double('client')
35
+ end
36
+
37
+ shared_examples_for 'a search request that can be paginated' do
38
+
39
+ describe '#page' do
40
+
41
+ it 'does not set an initial from and size on the search definition' do
42
+ expect(response.search.definition[:from]).to be(nil)
43
+ expect(response.search.definition[:size]).to be(nil)
44
+ end
45
+
46
+ context 'when page is called once' do
47
+
48
+ let(:search_request) do
49
+ { index: index_field, from: 25, size: 25, q: '*', type: type_field}
50
+ end
51
+
52
+ before do
53
+ expect(client).to receive(:search).with(search_request).and_return(response_document)
54
+ response.page(2).to_a
55
+ end
56
+
57
+ it 'advances the from/size in the search request' do
58
+ expect(response.search.definition[:from]).to be(25)
59
+ expect(response.search.definition[:size]).to be(25)
60
+ end
61
+ end
62
+
63
+ context 'when page is called more than once' do
64
+
65
+ let(:search_request_one) do
66
+ { index: index_field, from: 25, size: 25, q: '*', type: type_field}
67
+ end
68
+
69
+ let(:search_request_two) do
70
+ { index: index_field, from: 75, size: 25, q: '*', type: type_field}
71
+ end
72
+
73
+ before do
74
+ expect(client).to receive(:search).with(search_request_one).and_return(response_document)
75
+ response.page(2).to_a
76
+ expect(client).to receive(:search).with(search_request_two).and_return(response_document)
77
+ response.page(4).to_a
78
+ end
79
+
80
+ it 'advances the from/size in the search request' do
81
+ expect(response.search.definition[:from]).to be(75)
82
+ expect(response.search.definition[:size]).to be(25)
83
+ end
84
+ end
85
+
86
+ context 'when limit is also set' do
87
+
88
+ before do
89
+ response.records
90
+ response.results
91
+ end
92
+
93
+ context 'when page is called before limit' do
94
+
95
+ before do
96
+ response.page(3).limit(35)
97
+ end
98
+
99
+ it 'sets the correct values' do
100
+ expect(response.search.definition[:size]).to eq(35)
101
+ expect(response.search.definition[:from]).to eq(70)
102
+ end
103
+
104
+ it 'resets the instance variables' do
105
+ expect(response.instance_variable_get(:@response)).to be(nil)
106
+ expect(response.instance_variable_get(:@records)).to be(nil)
107
+ expect(response.instance_variable_get(:@results)).to be(nil)
108
+ end
109
+ end
110
+
111
+ context 'when limit is called before page' do
112
+
113
+ before do
114
+ response.limit(35).page(3)
115
+ end
116
+
117
+ it 'sets the correct values' do
118
+ expect(response.search.definition[:size]).to eq(35)
119
+ expect(response.search.definition[:from]).to eq(70)
120
+ end
121
+
122
+ it 'resets the instance variables' do
123
+ expect(response.instance_variable_get(:@response)).to be(nil)
124
+ expect(response.instance_variable_get(:@records)).to be(nil)
125
+ expect(response.instance_variable_get(:@results)).to be(nil)
126
+ end
127
+ end
128
+ end
129
+ end
130
+
131
+ describe '#limit_value' do
132
+
133
+ context 'when there is no default set' do
134
+
135
+ it 'uses the limit value from the Kaminari configuration' do
136
+ expect(response.limit_value).to eq(Kaminari.config.default_per_page)
137
+ end
138
+ end
139
+
140
+ context 'when there is a limit in the search definition' do
141
+
142
+ let(:search) do
143
+ Elasticsearch::Model::Searching::SearchRequest.new(model, '*', size: 10)
144
+ end
145
+
146
+ it 'gets the limit from the search definition' do
147
+ expect(response.limit_value).to eq(10)
148
+ end
149
+ end
150
+
151
+ context 'when there is a limit in the search body' do
152
+
153
+ let(:search) do
154
+ Elasticsearch::Model::Searching::SearchRequest.new(model, { query: { match_all: {} }, size: 999 })
155
+ end
156
+
157
+ it 'does not use the limit' do
158
+ expect(response.limit_value).to be(Kaminari.config.default_per_page)
159
+ end
160
+ end
161
+ end
162
+
163
+ describe '#offset_value' do
164
+
165
+ context 'when there is no default set' do
166
+
167
+ it 'uses an offset of 0' do
168
+ expect(response.offset_value).to eq(0)
169
+ end
170
+ end
171
+
172
+ context 'when there is an offset in the search definition' do
173
+
174
+ let(:search) do
175
+ Elasticsearch::Model::Searching::SearchRequest.new(model, '*', from: 50)
176
+ end
177
+
178
+ it 'gets the limit from the search definition' do
179
+ expect(response.offset_value).to eq(50)
180
+ end
181
+ end
182
+
183
+ context 'when there is an offset in the search body' do
184
+
185
+ let(:search) do
186
+ Elasticsearch::Model::Searching::SearchRequest.new(model, { query: { match_all: {} }, from: 333 })
187
+ end
188
+
189
+ it 'does not use the offset' do
190
+ expect(response.offset_value).to be(0)
191
+ end
192
+ end
193
+ end
194
+
195
+ describe '#limit' do
196
+
197
+ context 'when a limit is set' do
198
+
199
+ before do
200
+ response.records
201
+ response.results
202
+ response.limit(35)
203
+ end
204
+
205
+ it 'sets the limit on the search defintiion' do
206
+ expect(response.search.definition[:size]).to eq(35)
207
+ end
208
+
209
+ it 'resets the instance variables' do
210
+ expect(response.instance_variable_get(:@response)).to be(nil)
211
+ expect(response.instance_variable_get(:@records)).to be(nil)
212
+ expect(response.instance_variable_get(:@results)).to be(nil)
213
+ end
214
+
215
+ context 'when the limit is provided as a string' do
216
+
217
+ before do
218
+ response.limit('35')
219
+ end
220
+
221
+ it 'coerces the string to an integer' do
222
+ expect(response.search.definition[:size]).to eq(35)
223
+ end
224
+ end
225
+
226
+ context 'when the limit is an invalid type' do
227
+
228
+ before do
229
+ response.limit('asdf')
230
+ end
231
+
232
+ it 'does not apply the setting' do
233
+ expect(response.search.definition[:size]).to eq(35)
234
+ end
235
+ end
236
+ end
237
+ end
238
+
239
+ describe '#offset' do
240
+
241
+ context 'when an offset is set' do
242
+
243
+ before do
244
+ response.records
245
+ response.results
246
+ response.offset(15)
247
+ end
248
+
249
+ it 'sets the limit on the search defintiion' do
250
+ expect(response.search.definition[:from]).to eq(15)
251
+ end
252
+
253
+ it 'resets the instance variables' do
254
+ expect(response.instance_variable_get(:@response)).to be(nil)
255
+ expect(response.instance_variable_get(:@records)).to be(nil)
256
+ expect(response.instance_variable_get(:@results)).to be(nil)
257
+ end
258
+
259
+ context 'when the offset is provided as a string' do
260
+
261
+ before do
262
+ response.offset('15')
263
+ end
264
+
265
+ it 'coerces the string to an integer' do
266
+ expect(response.search.definition[:from]).to eq(15)
267
+ end
268
+ end
269
+
270
+ context 'when the offset is an invalid type' do
271
+
272
+ before do
273
+ response.offset('asdf')
274
+ end
275
+
276
+ it 'does not apply the setting' do
277
+ expect(response.search.definition[:from]).to eq(0)
278
+ end
279
+ end
280
+ end
281
+ end
282
+
283
+ describe '#total' do
284
+
285
+ before do
286
+ allow(response.results).to receive(:total).and_return(100)
287
+ end
288
+
289
+ it 'returns the total number of hits' do
290
+ expect(response.total_count).to eq(100)
291
+ end
292
+ end
293
+
294
+ context 'results' do
295
+
296
+ before do
297
+ allow(search).to receive(:execute!).and_return(response_document)
298
+ end
299
+
300
+ describe '#current_page' do
301
+
302
+ it 'returns the current page' do
303
+ expect(response.results.current_page).to eq(1)
304
+ end
305
+
306
+ context 'when a particular page is accessed' do
307
+
308
+ it 'returns the correct current page' do
309
+ expect(response.page(5).results.current_page).to eq(5)
310
+ end
311
+ end
312
+ end
313
+
314
+ describe '#prev_page' do
315
+
316
+ it 'returns the previous page' do
317
+ expect(response.page(1).results.prev_page).to be(nil)
318
+ expect(response.page(2).results.prev_page).to be(1)
319
+ expect(response.page(3).results.prev_page).to be(2)
320
+ expect(response.page(4).results.prev_page).to be(3)
321
+ end
322
+ end
323
+
324
+ describe '#next_page' do
325
+
326
+ it 'returns the previous page' do
327
+ expect(response.page(1).results.next_page).to be(2)
328
+ expect(response.page(2).results.next_page).to be(3)
329
+ expect(response.page(3).results.next_page).to be(4)
330
+ expect(response.page(4).results.next_page).to be(nil)
331
+ end
332
+ end
333
+ end
334
+
335
+ context 'records' do
336
+
337
+ before do
338
+ allow(search).to receive(:execute!).and_return(response_document)
339
+ end
340
+
341
+ describe '#current_page' do
342
+
343
+ it 'returns the current page' do
344
+ expect(response.records.current_page).to eq(1)
345
+ end
346
+
347
+ context 'when a particular page is accessed' do
348
+
349
+ it 'returns the correct current page' do
350
+ expect(response.page(5).records.current_page).to eq(5)
351
+ end
352
+ end
353
+ end
354
+
355
+ describe '#prev_page' do
356
+
357
+ it 'returns the previous page' do
358
+ expect(response.page(1).records.prev_page).to be(nil)
359
+ expect(response.page(2).records.prev_page).to be(1)
360
+ expect(response.page(3).records.prev_page).to be(2)
361
+ expect(response.page(4).records.prev_page).to be(3)
362
+ end
363
+ end
364
+
365
+ describe '#next_page' do
366
+
367
+ it 'returns the previous page' do
368
+ expect(response.page(1).records.next_page).to be(2)
369
+ expect(response.page(2).records.next_page).to be(3)
370
+ expect(response.page(3).records.next_page).to be(4)
371
+ expect(response.page(4).records.next_page).to be(nil)
372
+ end
373
+ end
374
+ end
375
+ end
376
+
377
+ context 'when the model is a single one' do
378
+
379
+ let(:model) do
380
+ ModelClass
381
+ end
382
+
383
+ let(:type_field) do
384
+ 'bar'
385
+ end
386
+
387
+ let(:index_field) do
388
+ 'foo'
389
+ end
390
+
391
+ it_behaves_like 'a search request that can be paginated'
392
+ end
393
+
394
+ context 'when the model is a multimodel' do
395
+
396
+ let(:model) do
397
+ Elasticsearch::Model::Multimodel.new(ModelClass)
398
+ end
399
+
400
+ let(:type_field) do
401
+ ['bar']
402
+ end
403
+
404
+ let(:index_field) do
405
+ ['foo']
406
+ end
407
+
408
+ it_behaves_like 'a search request that can be paginated'
409
+ end
410
+ end