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,262 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'Elasticsearch::Model::Response::Response WillPaginate' do
4
+
5
+ before(:all) do
6
+ class ModelClass
7
+ def self.index_name; 'foo'; end
8
+ def self.document_type; 'bar'; end
9
+
10
+ def self.per_page
11
+ 33
12
+ end
13
+ end
14
+
15
+ # Subclass Response so we can include WillPaginate module without conflicts with Kaminari.
16
+ class WillPaginateResponse < Elasticsearch::Model::Response::Response
17
+ include Elasticsearch::Model::Response::Pagination::WillPaginate
18
+ end
19
+ end
20
+
21
+ after(:all) do
22
+ remove_classes(ModelClass, WillPaginateResponse)
23
+ end
24
+
25
+ let(:response_document) do
26
+ { 'took' => '5', 'timed_out' => false, '_shards' => {'one' => 'OK'},
27
+ 'hits' => { 'total' => 100, 'hits' => (1..100).to_a.map { |i| { _id: i } } } }
28
+ end
29
+
30
+ let(:search) do
31
+ Elasticsearch::Model::Searching::SearchRequest.new(model, '*')
32
+ end
33
+
34
+ let(:response) do
35
+ allow(model).to receive(:client).and_return(client)
36
+ WillPaginateResponse.new(model, search, response_document).tap do |resp|
37
+ allow(resp).to receive(:client).and_return(client)
38
+ end
39
+ end
40
+
41
+ let(:client) do
42
+ double('client')
43
+ end
44
+
45
+ shared_examples_for 'a search request that can be paginated' do
46
+
47
+ describe '#offset' do
48
+
49
+ context 'when per_page and page are set' do
50
+
51
+ before do
52
+ response.per_page(3).page(3)
53
+ end
54
+
55
+ it 'sets the correct offset' do
56
+ expect(response.offset).to eq(6)
57
+ end
58
+ end
59
+ end
60
+
61
+ describe '#length' do
62
+
63
+ context 'when per_page and page are set' do
64
+
65
+ before do
66
+ response.per_page(3).page(3)
67
+ end
68
+
69
+ it 'sets the correct offset' do
70
+ expect(response.length).to eq(3)
71
+ end
72
+ end
73
+ end
74
+
75
+ describe '#paginate' do
76
+
77
+ context 'when there are no settings' do
78
+
79
+ context 'when page is set to nil' do
80
+
81
+ before do
82
+ response.paginate(page: nil)
83
+ end
84
+
85
+ it 'uses the defaults' do
86
+ expect(response.search.definition[:size]).to eq(default_per_page)
87
+ expect(response.search.definition[:from]).to eq(0)
88
+ end
89
+ end
90
+
91
+ context 'when page is set to a value' do
92
+
93
+ before do
94
+ response.paginate(page: 2)
95
+ end
96
+
97
+ it 'uses the defaults' do
98
+ expect(response.search.definition[:size]).to eq(default_per_page)
99
+ expect(response.search.definition[:from]).to eq(default_per_page)
100
+ end
101
+ end
102
+
103
+ context 'when a custom page and per_page is set' do
104
+
105
+ before do
106
+ response.paginate(page: 3, per_page: 9)
107
+ end
108
+
109
+ it 'uses the custom values' do
110
+ expect(response.search.definition[:size]).to eq(9)
111
+ expect(response.search.definition[:from]).to eq(18)
112
+ end
113
+ end
114
+
115
+ context 'fall back to first page if invalid value is provided' do
116
+
117
+ before do
118
+ response.paginate(page: -1)
119
+ end
120
+
121
+ it 'uses the custom values' do
122
+ expect(response.search.definition[:size]).to eq(default_per_page)
123
+ expect(response.search.definition[:from]).to eq(0)
124
+ end
125
+ end
126
+ end
127
+ end
128
+
129
+ describe '#page' do
130
+
131
+ context 'when a value is provided for page' do
132
+
133
+ before do
134
+ response.page(5)
135
+ end
136
+
137
+ it 'calculates the correct :size and :from' do
138
+ expect(response.search.definition[:size]).to eq(default_per_page)
139
+ expect(response.search.definition[:from]).to eq(default_per_page * 4)
140
+ end
141
+ end
142
+
143
+ context 'when a value is provided for page and per_page' do
144
+
145
+ before do
146
+ response.page(5).per_page(3)
147
+ end
148
+
149
+ it 'calculates the correct :size and :from' do
150
+ expect(response.search.definition[:size]).to eq(3)
151
+ expect(response.search.definition[:from]).to eq(12)
152
+ end
153
+ end
154
+
155
+ context 'when a value is provided for per_page and page' do
156
+
157
+ before do
158
+ response.per_page(3).page(5)
159
+ end
160
+
161
+ it 'calculates the correct :size and :from' do
162
+ expect(response.search.definition[:size]).to eq(3)
163
+ expect(response.search.definition[:from]).to eq(12)
164
+ end
165
+ end
166
+ end
167
+
168
+ describe '#current_page' do
169
+
170
+ context 'when no values are set' do
171
+
172
+ before do
173
+ response.paginate({})
174
+ end
175
+
176
+ it 'returns the first page' do
177
+ expect(response.current_page).to eq(1)
178
+ end
179
+ end
180
+
181
+ context 'when values are provided for per_page and page' do
182
+
183
+ before do
184
+ response.paginate(page: 3, per_page: 9)
185
+ end
186
+
187
+ it 'calculates the correct current page' do
188
+ expect(response.current_page).to eq(3)
189
+ end
190
+ end
191
+
192
+ context 'when #paginate has not been called on the response' do
193
+
194
+ it 'returns nil' do
195
+ expect(response.current_page).to be_nil
196
+ end
197
+ end
198
+ end
199
+
200
+ describe '#per_page' do
201
+
202
+ context 'when a value is set via the #paginate method' do
203
+
204
+ before do
205
+ response.paginate(per_page: 8)
206
+ end
207
+
208
+ it 'returns the per_page value' do
209
+ expect(response.per_page).to eq(8)
210
+ end
211
+ end
212
+
213
+ context 'when a value is set via the #per_page method' do
214
+
215
+ before do
216
+ response.per_page(8)
217
+ end
218
+
219
+ it 'returns the per_page value' do
220
+ expect(response.per_page).to eq(8)
221
+ end
222
+ end
223
+ end
224
+
225
+ describe '#total_entries' do
226
+
227
+ before do
228
+ allow(response).to receive(:results).and_return(double('results', total: 100))
229
+ end
230
+
231
+ it 'returns the total results' do
232
+ expect(response.total_entries).to eq(100)
233
+ end
234
+ end
235
+ end
236
+
237
+ context 'when the model is a single one' do
238
+
239
+ let(:model) do
240
+ ModelClass
241
+ end
242
+
243
+ let(:default_per_page) do
244
+ 33
245
+ end
246
+
247
+ it_behaves_like 'a search request that can be paginated'
248
+ end
249
+
250
+ context 'when the model is a multimodel' do
251
+
252
+ let(:model) do
253
+ Elasticsearch::Model::Multimodel.new(ModelClass)
254
+ end
255
+
256
+ let(:default_per_page) do
257
+ ::WillPaginate.per_page
258
+ end
259
+
260
+ it_behaves_like 'a search request that can be paginated'
261
+ end
262
+ end
@@ -0,0 +1,118 @@
1
+ require 'spec_helper'
2
+
3
+ describe Elasticsearch::Model::Response::Records do
4
+
5
+ before(:all) do
6
+ class DummyCollection
7
+ include Enumerable
8
+
9
+ def each(&block); ['FOO'].each(&block); end
10
+ def size; ['FOO'].size; end
11
+ def empty?; ['FOO'].empty?; end
12
+ def foo; 'BAR'; end
13
+ end
14
+
15
+ class DummyModel
16
+ def self.index_name; 'foo'; end
17
+ def self.document_type; 'bar'; end
18
+
19
+ def self.find(*args)
20
+ DummyCollection.new
21
+ end
22
+ end
23
+ end
24
+
25
+ after(:all) do
26
+ remove_classes(DummyCollection, DummyModel)
27
+ end
28
+
29
+ let(:response_document) do
30
+ { 'hits' => { 'total' => 123, 'max_score' => 456, 'hits' => [{'_id' => '1', 'foo' => 'bar'}] } }
31
+ end
32
+
33
+ let(:results) do
34
+ Elasticsearch::Model::Response::Results.new(DummyModel, response_document)
35
+ end
36
+
37
+ let(:search) do
38
+ Elasticsearch::Model::Searching::SearchRequest.new(DummyModel, '*').tap do |request|
39
+ allow(request).to receive(:execute!).and_return(response_document)
40
+ end
41
+ end
42
+
43
+ let(:response) do
44
+ Elasticsearch::Model::Response::Response.new(DummyModel, search)
45
+ end
46
+
47
+ let(:records) do
48
+ described_class.new(DummyModel, response)
49
+ end
50
+
51
+ context 'when the records are accessed' do
52
+
53
+ it 'returns the records' do
54
+ expect(records.records.size).to eq(1)
55
+ expect(records.records.first).to eq('FOO')
56
+ end
57
+
58
+ it 'delegates methods to records' do
59
+ expect(records.foo).to eq('BAR')
60
+ end
61
+ end
62
+
63
+ describe '#each_with_hit' do
64
+
65
+ it 'returns each record with its Elasticsearch hit' do
66
+ records.each_with_hit do |record, hit|
67
+ expect(record).to eq('FOO')
68
+ expect(hit.foo).to eq('bar')
69
+ end
70
+ end
71
+ end
72
+
73
+ describe '#map_with_hit' do
74
+
75
+ let(:value) do
76
+ records.map_with_hit { |record, hit| "#{record}---#{hit.foo}" }
77
+ end
78
+
79
+ it 'returns each record with its Elasticsearch hit' do
80
+ expect(value).to eq(['FOO---bar'])
81
+ end
82
+ end
83
+
84
+ describe '#ids' do
85
+
86
+ it 'returns the ids' do
87
+ expect(records.ids).to eq(['1'])
88
+ end
89
+ end
90
+
91
+ context 'when an adapter is used' do
92
+
93
+ before do
94
+ module DummyAdapter
95
+ module RecordsMixin
96
+ def records
97
+ ['FOOBAR']
98
+ end
99
+ end
100
+
101
+ def records_mixin
102
+ RecordsMixin
103
+ end; module_function :records_mixin
104
+ end
105
+
106
+ allow(Elasticsearch::Model::Adapter).to receive(:from_class).and_return(DummyAdapter)
107
+ end
108
+
109
+ after do
110
+ Elasticsearch::Model::Adapter::Adapter.adapters.delete(DummyAdapter)
111
+ Object.send(:remove_const, :DummyAdapter) if defined?(DummyAdapter)
112
+ end
113
+
114
+ it 'delegates the records method to the adapter' do
115
+ expect(records.records).to eq(['FOOBAR'])
116
+ end
117
+ end
118
+ end
@@ -0,0 +1,131 @@
1
+ require 'spec_helper'
2
+
3
+ describe Elasticsearch::Model::Response::Response 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
+ { 'took' => '5', 'timed_out' => false, '_shards' => {'one' => 'OK'}, 'hits' => { 'hits' => [] },
18
+ 'aggregations' => {'foo' => {'bar' => 10}},
19
+ 'suggest' => {'my_suggest' => [ { 'text' => 'foo', 'options' => [ { 'text' => 'Foo', 'score' => 2.0 },
20
+ { 'text' => 'Bar', 'score' => 1.0 } ] } ]}}
21
+
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
+ it 'performs the Elasticsearch request lazily' do
35
+ expect(search).not_to receive(:execute!)
36
+ response
37
+ end
38
+
39
+ describe '#klass' do
40
+
41
+ it 'returns the class' do
42
+ expect(response.klass).to be(OriginClass)
43
+ end
44
+ end
45
+
46
+ describe '#search' do
47
+
48
+ it 'returns the search object' do
49
+ expect(response.search).to eq(search)
50
+ end
51
+ end
52
+
53
+ describe '#took' do
54
+
55
+ it 'returns the took field' do
56
+ expect(response.took).to eq('5')
57
+ end
58
+ end
59
+
60
+ describe '#timed_out' do
61
+
62
+ it 'returns the timed_out field' do
63
+ expect(response.timed_out).to eq(false)
64
+ end
65
+ end
66
+
67
+ describe '#shards' do
68
+
69
+ it 'returns a Hashie::Mash' do
70
+ expect(response.shards.one).to eq('OK')
71
+ end
72
+ end
73
+
74
+ describe '#response' do
75
+
76
+ it 'returns the response document' do
77
+ expect(response.response).to eq(response_document)
78
+ end
79
+ end
80
+
81
+ describe '#results' do
82
+
83
+ it 'provides access to the results' do
84
+ expect(response.results).to be_a(Elasticsearch::Model::Response::Results)
85
+ expect(response.size).to be(0)
86
+ end
87
+ end
88
+
89
+ describe '#records' do
90
+
91
+ it 'provides access to the records' do
92
+ expect(response.records).to be_a(Elasticsearch::Model::Response::Records)
93
+ expect(response.size).to be(0)
94
+ end
95
+ end
96
+
97
+ describe 'enumerable methods' do
98
+
99
+ it 'delegates the methods to the results' do
100
+ expect(response.empty?).to be(true)
101
+ end
102
+ end
103
+
104
+ describe 'aggregations' do
105
+
106
+ it 'provides access to the aggregations' do
107
+ expect(response.aggregations).to be_a(Hashie::Mash)
108
+ expect(response.aggregations.foo.bar).to eq(10)
109
+ end
110
+ end
111
+
112
+ describe 'suggestions' do
113
+
114
+ it 'provides access to the suggestions' do
115
+ expect(response.suggestions).to be_a(Hashie::Mash)
116
+ expect(response.suggestions.my_suggest.first.options.first.text).to eq('Foo')
117
+ expect(response.suggestions.terms).to eq([ 'Foo', 'Bar' ])
118
+ end
119
+
120
+ context 'when there are no suggestions' do
121
+
122
+ let(:response_document) do
123
+ { }
124
+ end
125
+
126
+ it 'returns an empty list' do
127
+ expect(response.suggestions.terms).to eq([ ])
128
+ end
129
+ end
130
+ end
131
+ end