elasticsearch-model 5.1.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 (125) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +5 -0
  3. data/README.md +18 -11
  4. data/Rakefile +27 -36
  5. data/elasticsearch-model.gemspec +5 -5
  6. data/examples/activerecord_custom_analyzer.rb +135 -0
  7. data/examples/activerecord_mapping_completion.rb +3 -18
  8. data/examples/datamapper_article.rb +11 -1
  9. data/gemfiles/3.0.gemfile +6 -1
  10. data/gemfiles/4.0.gemfile +7 -1
  11. data/gemfiles/5.0.gemfile +7 -1
  12. data/lib/elasticsearch/model/adapters/active_record.rb +7 -23
  13. data/lib/elasticsearch/model/adapters/mongoid.rb +10 -3
  14. data/lib/elasticsearch/model/importing.rb +1 -1
  15. data/lib/elasticsearch/model/indexing.rb +6 -4
  16. data/lib/elasticsearch/model/naming.rb +9 -2
  17. data/lib/elasticsearch/model/response/aggregations.rb +1 -1
  18. data/lib/elasticsearch/model/response/base.rb +3 -2
  19. data/lib/elasticsearch/model/response/pagination/kaminari.rb +109 -0
  20. data/lib/elasticsearch/model/response/pagination/will_paginate.rb +95 -0
  21. data/lib/elasticsearch/model/response/pagination.rb +2 -192
  22. data/lib/elasticsearch/model/response/result.rb +1 -1
  23. data/lib/elasticsearch/model/response/suggestions.rb +1 -1
  24. data/lib/elasticsearch/model/response.rb +11 -10
  25. data/lib/elasticsearch/model/version.rb +1 -1
  26. data/lib/elasticsearch/model.rb +15 -8
  27. data/spec/elasticsearch/model/adapter_spec.rb +119 -0
  28. data/spec/elasticsearch/model/adapters/active_record/associations_spec.rb +334 -0
  29. data/spec/elasticsearch/model/adapters/active_record/basic_spec.rb +340 -0
  30. data/spec/elasticsearch/model/adapters/active_record/dynamic_index_name_spec.rb +18 -0
  31. data/spec/elasticsearch/model/adapters/active_record/import_spec.rb +187 -0
  32. data/spec/elasticsearch/model/adapters/active_record/multi_model_spec.rb +110 -0
  33. data/spec/elasticsearch/model/adapters/active_record/namespaced_model_spec.rb +38 -0
  34. data/spec/elasticsearch/model/adapters/active_record/pagination_spec.rb +315 -0
  35. data/spec/elasticsearch/model/adapters/active_record/parent_child_spec.rb +75 -0
  36. data/spec/elasticsearch/model/adapters/active_record/serialization_spec.rb +61 -0
  37. data/spec/elasticsearch/model/adapters/active_record_spec.rb +207 -0
  38. data/spec/elasticsearch/model/adapters/default_spec.rb +41 -0
  39. data/spec/elasticsearch/model/adapters/mongoid/basic_spec.rb +267 -0
  40. data/spec/elasticsearch/model/adapters/mongoid/multi_model_spec.rb +66 -0
  41. data/spec/elasticsearch/model/adapters/mongoid_spec.rb +235 -0
  42. data/spec/elasticsearch/model/adapters/multiple_spec.rb +125 -0
  43. data/spec/elasticsearch/model/callbacks_spec.rb +33 -0
  44. data/spec/elasticsearch/model/client_spec.rb +66 -0
  45. data/spec/elasticsearch/model/hash_wrapper_spec.rb +12 -0
  46. data/spec/elasticsearch/model/importing_spec.rb +214 -0
  47. data/spec/elasticsearch/model/indexing_spec.rb +918 -0
  48. data/spec/elasticsearch/model/module_spec.rb +101 -0
  49. data/spec/elasticsearch/model/multimodel_spec.rb +55 -0
  50. data/spec/elasticsearch/model/naming_inheritance_spec.rb +184 -0
  51. data/spec/elasticsearch/model/naming_spec.rb +186 -0
  52. data/spec/elasticsearch/model/proxy_spec.rb +107 -0
  53. data/spec/elasticsearch/model/response/aggregations_spec.rb +66 -0
  54. data/spec/elasticsearch/model/response/base_spec.rb +90 -0
  55. data/spec/elasticsearch/model/response/pagination/kaminari_spec.rb +410 -0
  56. data/spec/elasticsearch/model/response/pagination/will_paginate_spec.rb +262 -0
  57. data/spec/elasticsearch/model/response/records_spec.rb +118 -0
  58. data/spec/elasticsearch/model/response/response_spec.rb +131 -0
  59. data/spec/elasticsearch/model/response/result_spec.rb +122 -0
  60. data/spec/elasticsearch/model/response/results_spec.rb +56 -0
  61. data/spec/elasticsearch/model/searching_search_request_spec.rb +112 -0
  62. data/spec/elasticsearch/model/searching_spec.rb +49 -0
  63. data/spec/elasticsearch/model/serializing_spec.rb +22 -0
  64. data/spec/spec_helper.rb +161 -0
  65. data/spec/support/app/answer.rb +33 -0
  66. data/spec/support/app/article.rb +22 -0
  67. data/spec/support/app/article_for_pagination.rb +12 -0
  68. data/spec/support/app/article_with_custom_serialization.rb +13 -0
  69. data/spec/support/app/article_with_dynamic_index_name.rb +15 -0
  70. data/spec/support/app/author.rb +9 -0
  71. data/spec/support/app/authorship.rb +4 -0
  72. data/spec/support/app/category.rb +3 -0
  73. data/spec/support/app/comment.rb +3 -0
  74. data/spec/support/app/episode.rb +11 -0
  75. data/spec/support/app/image.rb +19 -0
  76. data/spec/support/app/import_article.rb +12 -0
  77. data/spec/support/app/mongoid_article.rb +21 -0
  78. data/spec/support/app/namespaced_book.rb +10 -0
  79. data/spec/support/app/parent_and_child_searchable.rb +24 -0
  80. data/spec/support/app/post.rb +14 -0
  81. data/spec/support/app/question.rb +27 -0
  82. data/spec/support/app/searchable.rb +48 -0
  83. data/spec/support/app/series.rb +11 -0
  84. data/spec/support/app.rb +21 -0
  85. data/spec/support/model.json +1 -0
  86. metadata +134 -90
  87. data/test/integration/active_record_associations_parent_child_test.rb +0 -147
  88. data/test/integration/active_record_associations_test.rb +0 -339
  89. data/test/integration/active_record_basic_test.rb +0 -251
  90. data/test/integration/active_record_custom_serialization_test.rb +0 -67
  91. data/test/integration/active_record_import_test.rb +0 -115
  92. data/test/integration/active_record_namespaced_model_test.rb +0 -54
  93. data/test/integration/active_record_pagination_test.rb +0 -149
  94. data/test/integration/dynamic_index_name_test.rb +0 -52
  95. data/test/integration/mongoid_basic_test.rb +0 -176
  96. data/test/integration/multiple_models_test.rb +0 -176
  97. data/test/support/model.json +0 -1
  98. data/test/test_helper.rb +0 -92
  99. data/test/unit/adapter_active_record_test.rb +0 -157
  100. data/test/unit/adapter_default_test.rb +0 -41
  101. data/test/unit/adapter_mongoid_test.rb +0 -104
  102. data/test/unit/adapter_multiple_test.rb +0 -106
  103. data/test/unit/adapter_test.rb +0 -69
  104. data/test/unit/callbacks_test.rb +0 -31
  105. data/test/unit/client_test.rb +0 -27
  106. data/test/unit/hash_wrapper_test.rb +0 -13
  107. data/test/unit/importing_test.rb +0 -203
  108. data/test/unit/indexing_test.rb +0 -687
  109. data/test/unit/module_test.rb +0 -68
  110. data/test/unit/multimodel_test.rb +0 -38
  111. data/test/unit/naming_inheritance_test.rb +0 -94
  112. data/test/unit/naming_test.rb +0 -103
  113. data/test/unit/proxy_test.rb +0 -98
  114. data/test/unit/response_aggregations_test.rb +0 -46
  115. data/test/unit/response_base_test.rb +0 -40
  116. data/test/unit/response_pagination_kaminari_test.rb +0 -433
  117. data/test/unit/response_pagination_will_paginate_test.rb +0 -398
  118. data/test/unit/response_records_test.rb +0 -91
  119. data/test/unit/response_result_test.rb +0 -90
  120. data/test/unit/response_results_test.rb +0 -31
  121. data/test/unit/response_test.rb +0 -104
  122. data/test/unit/searching_search_request_test.rb +0 -78
  123. data/test/unit/searching_test.rb +0 -41
  124. data/test/unit/serializing_test.rb +0 -17
  125. /data/{test → spec}/support/model.yml +0 -0
@@ -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