elasticsearch-model 5.1.0 → 6.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (127) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -3
  3. data/Gemfile +5 -0
  4. data/README.md +18 -11
  5. data/Rakefile +27 -36
  6. data/elasticsearch-model.gemspec +6 -6
  7. data/examples/activerecord_custom_analyzer.rb +135 -0
  8. data/examples/activerecord_mapping_completion.rb +3 -18
  9. data/examples/datamapper_article.rb +11 -1
  10. data/gemfiles/3.0.gemfile +6 -1
  11. data/gemfiles/4.0.gemfile +8 -1
  12. data/gemfiles/5.0.gemfile +7 -1
  13. data/gemfiles/6.0.gemfile +18 -0
  14. data/lib/elasticsearch/model/adapters/active_record.rb +8 -24
  15. data/lib/elasticsearch/model/adapters/mongoid.rb +10 -3
  16. data/lib/elasticsearch/model/importing.rb +1 -1
  17. data/lib/elasticsearch/model/indexing.rb +6 -4
  18. data/lib/elasticsearch/model/naming.rb +9 -2
  19. data/lib/elasticsearch/model/response/aggregations.rb +1 -1
  20. data/lib/elasticsearch/model/response/base.rb +3 -2
  21. data/lib/elasticsearch/model/response/pagination/kaminari.rb +109 -0
  22. data/lib/elasticsearch/model/response/pagination/will_paginate.rb +95 -0
  23. data/lib/elasticsearch/model/response/pagination.rb +2 -192
  24. data/lib/elasticsearch/model/response/result.rb +1 -1
  25. data/lib/elasticsearch/model/response/suggestions.rb +1 -1
  26. data/lib/elasticsearch/model/response.rb +11 -10
  27. data/lib/elasticsearch/model/version.rb +1 -1
  28. data/lib/elasticsearch/model.rb +15 -8
  29. data/spec/elasticsearch/model/adapter_spec.rb +119 -0
  30. data/spec/elasticsearch/model/adapters/active_record/associations_spec.rb +334 -0
  31. data/spec/elasticsearch/model/adapters/active_record/basic_spec.rb +340 -0
  32. data/spec/elasticsearch/model/adapters/active_record/dynamic_index_name_spec.rb +18 -0
  33. data/spec/elasticsearch/model/adapters/active_record/import_spec.rb +187 -0
  34. data/spec/elasticsearch/model/adapters/active_record/multi_model_spec.rb +110 -0
  35. data/spec/elasticsearch/model/adapters/active_record/namespaced_model_spec.rb +38 -0
  36. data/spec/elasticsearch/model/adapters/active_record/pagination_spec.rb +315 -0
  37. data/spec/elasticsearch/model/adapters/active_record/parent_child_spec.rb +75 -0
  38. data/spec/elasticsearch/model/adapters/active_record/serialization_spec.rb +61 -0
  39. data/spec/elasticsearch/model/adapters/active_record_spec.rb +207 -0
  40. data/spec/elasticsearch/model/adapters/default_spec.rb +41 -0
  41. data/spec/elasticsearch/model/adapters/mongoid/basic_spec.rb +267 -0
  42. data/spec/elasticsearch/model/adapters/mongoid/multi_model_spec.rb +66 -0
  43. data/spec/elasticsearch/model/adapters/mongoid_spec.rb +235 -0
  44. data/spec/elasticsearch/model/adapters/multiple_spec.rb +125 -0
  45. data/spec/elasticsearch/model/callbacks_spec.rb +33 -0
  46. data/spec/elasticsearch/model/client_spec.rb +66 -0
  47. data/spec/elasticsearch/model/hash_wrapper_spec.rb +12 -0
  48. data/spec/elasticsearch/model/importing_spec.rb +214 -0
  49. data/spec/elasticsearch/model/indexing_spec.rb +918 -0
  50. data/spec/elasticsearch/model/module_spec.rb +101 -0
  51. data/spec/elasticsearch/model/multimodel_spec.rb +55 -0
  52. data/spec/elasticsearch/model/naming_inheritance_spec.rb +184 -0
  53. data/spec/elasticsearch/model/naming_spec.rb +186 -0
  54. data/spec/elasticsearch/model/proxy_spec.rb +107 -0
  55. data/spec/elasticsearch/model/response/aggregations_spec.rb +66 -0
  56. data/spec/elasticsearch/model/response/base_spec.rb +90 -0
  57. data/spec/elasticsearch/model/response/pagination/kaminari_spec.rb +410 -0
  58. data/spec/elasticsearch/model/response/pagination/will_paginate_spec.rb +262 -0
  59. data/spec/elasticsearch/model/response/records_spec.rb +118 -0
  60. data/spec/elasticsearch/model/response/response_spec.rb +131 -0
  61. data/spec/elasticsearch/model/response/result_spec.rb +122 -0
  62. data/spec/elasticsearch/model/response/results_spec.rb +56 -0
  63. data/spec/elasticsearch/model/searching_search_request_spec.rb +112 -0
  64. data/spec/elasticsearch/model/searching_spec.rb +49 -0
  65. data/spec/elasticsearch/model/serializing_spec.rb +22 -0
  66. data/spec/spec_helper.rb +161 -0
  67. data/spec/support/app/answer.rb +33 -0
  68. data/spec/support/app/article.rb +22 -0
  69. data/spec/support/app/article_for_pagination.rb +12 -0
  70. data/spec/support/app/article_with_custom_serialization.rb +13 -0
  71. data/spec/support/app/article_with_dynamic_index_name.rb +15 -0
  72. data/spec/support/app/author.rb +9 -0
  73. data/spec/support/app/authorship.rb +4 -0
  74. data/spec/support/app/category.rb +3 -0
  75. data/spec/support/app/comment.rb +3 -0
  76. data/spec/support/app/episode.rb +11 -0
  77. data/spec/support/app/image.rb +19 -0
  78. data/spec/support/app/import_article.rb +12 -0
  79. data/spec/support/app/mongoid_article.rb +21 -0
  80. data/spec/support/app/namespaced_book.rb +10 -0
  81. data/spec/support/app/parent_and_child_searchable.rb +24 -0
  82. data/spec/support/app/post.rb +14 -0
  83. data/spec/support/app/question.rb +27 -0
  84. data/spec/support/app/searchable.rb +48 -0
  85. data/spec/support/app/series.rb +11 -0
  86. data/spec/support/app.rb +21 -0
  87. data/spec/support/model.json +1 -0
  88. data/{test → spec}/support/model.yml +0 -0
  89. metadata +134 -89
  90. data/test/integration/active_record_associations_parent_child_test.rb +0 -147
  91. data/test/integration/active_record_associations_test.rb +0 -339
  92. data/test/integration/active_record_basic_test.rb +0 -251
  93. data/test/integration/active_record_custom_serialization_test.rb +0 -67
  94. data/test/integration/active_record_import_test.rb +0 -115
  95. data/test/integration/active_record_namespaced_model_test.rb +0 -54
  96. data/test/integration/active_record_pagination_test.rb +0 -149
  97. data/test/integration/dynamic_index_name_test.rb +0 -52
  98. data/test/integration/mongoid_basic_test.rb +0 -176
  99. data/test/integration/multiple_models_test.rb +0 -176
  100. data/test/support/model.json +0 -1
  101. data/test/test_helper.rb +0 -92
  102. data/test/unit/adapter_active_record_test.rb +0 -157
  103. data/test/unit/adapter_default_test.rb +0 -41
  104. data/test/unit/adapter_mongoid_test.rb +0 -104
  105. data/test/unit/adapter_multiple_test.rb +0 -106
  106. data/test/unit/adapter_test.rb +0 -69
  107. data/test/unit/callbacks_test.rb +0 -31
  108. data/test/unit/client_test.rb +0 -27
  109. data/test/unit/hash_wrapper_test.rb +0 -13
  110. data/test/unit/importing_test.rb +0 -203
  111. data/test/unit/indexing_test.rb +0 -687
  112. data/test/unit/module_test.rb +0 -68
  113. data/test/unit/multimodel_test.rb +0 -38
  114. data/test/unit/naming_inheritance_test.rb +0 -94
  115. data/test/unit/naming_test.rb +0 -103
  116. data/test/unit/proxy_test.rb +0 -98
  117. data/test/unit/response_aggregations_test.rb +0 -46
  118. data/test/unit/response_base_test.rb +0 -40
  119. data/test/unit/response_pagination_kaminari_test.rb +0 -433
  120. data/test/unit/response_pagination_will_paginate_test.rb +0 -398
  121. data/test/unit/response_records_test.rb +0 -91
  122. data/test/unit/response_result_test.rb +0 -90
  123. data/test/unit/response_results_test.rb +0 -31
  124. data/test/unit/response_test.rb +0 -104
  125. data/test/unit/searching_search_request_test.rb +0 -78
  126. data/test/unit/searching_test.rb +0 -41
  127. 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
@@ -0,0 +1,122 @@
1
+ require 'spec_helper'
2
+ require 'active_support/json/encoding'
3
+
4
+ describe Elasticsearch::Model::Response::Result do
5
+
6
+ let(:result) do
7
+ described_class.new(foo: 'bar', bar: { bam: 'baz' })
8
+ end
9
+
10
+ it 'provides access to the properties' do
11
+ expect(result.foo).to eq('bar')
12
+ expect(result.bar.bam).to eq('baz')
13
+ expect { result.xoxo }.to raise_exception(NoMethodError)
14
+ end
15
+
16
+ describe '#id' do
17
+
18
+ let(:result) do
19
+ described_class.new(foo: 'bar', _id: 42, _source: { id: 12 })
20
+ end
21
+
22
+ it 'returns the _id field' do
23
+ expect(result.id).to eq(42)
24
+ end
25
+
26
+ it 'provides access to the source id field' do
27
+ expect(result._source.id).to eq(12)
28
+ end
29
+ end
30
+
31
+ describe '#type' do
32
+
33
+ let(:result) do
34
+ described_class.new(foo: 'bar', _type: 'baz', _source: { type: 'BAM' })
35
+ end
36
+
37
+ it 'returns the _type field' do
38
+ expect(result.type).to eq('baz')
39
+ end
40
+
41
+ it 'provides access to the source type field' do
42
+ expect(result._source.type).to eq('BAM')
43
+ end
44
+ end
45
+
46
+ describe 'method delegation' do
47
+
48
+ let(:result) do
49
+ described_class.new(foo: 'bar', _source: { bar: { bam: 'baz' } })
50
+ end
51
+
52
+ it 'provides access to the _source field via a method' do
53
+ expect(result._source).to eq('bar' => { 'bam' => 'baz' })
54
+ end
55
+
56
+ it 'is recognized by #method' do
57
+ expect(result.method :bar).to be_a Method
58
+ end
59
+
60
+ it 'respond_to? still works' do
61
+ expect(result.respond_to? :bar).to be true
62
+ end
63
+
64
+ context 'when methods map to keys in subdocuments of the response from Elasticsearch' do
65
+
66
+ it 'provides access to top level fields via a method' do
67
+ expect(result.foo).to eq('bar')
68
+ expect(result.fetch(:foo)).to eq('bar')
69
+ expect(result.fetch(:does_not_exist, 'moo')).to eq('moo')
70
+ end
71
+
72
+ it 'responds to hash methods' do
73
+ expect(result.keys).to eq(['foo', '_source'])
74
+ expect(result.to_hash).to eq('foo' => 'bar', '_source' => { 'bar' => { 'bam' => 'baz' } })
75
+ end
76
+
77
+ it 'provides access to fields in the _source subdocument via a method' do
78
+ expect(result.bar).to eq('bam' => 'baz')
79
+ expect(result.bar.bam).to eq('baz')
80
+ expect(result._source.bar).to eq('bam' => 'baz')
81
+ expect(result._source.bar.bam).to eq('baz')
82
+ end
83
+
84
+ context 'when boolean methods are called' do
85
+
86
+ it 'provides access to top level fields via a method' do
87
+ expect(result.foo?).to eq(true)
88
+ expect(result.boo?).to eq(false)
89
+ end
90
+
91
+ it 'delegates to fields in the _source subdocument via a method' do
92
+ expect(result.bar?).to eq(true)
93
+ expect(result.bar.bam?).to eq(true)
94
+ expect(result.boo?).to eq(false)
95
+ expect(result.bar.boo?).to eq(false)
96
+ expect(result._source.bar?).to eq(true)
97
+ expect(result._source.bar.bam?).to eq(true)
98
+ expect(result._source.boo?).to eq(false)
99
+ expect(result._source.bar.boo?).to eq(false)
100
+ end
101
+ end
102
+ end
103
+
104
+ context 'when methods do not map to keys in subdocuments of the response from Elasticsearch' do
105
+
106
+ it 'raises a NoMethodError' do
107
+ expect { result.does_not_exist }.to raise_exception(NoMethodError)
108
+ end
109
+ end
110
+ end
111
+
112
+ describe '#as_json' do
113
+
114
+ let(:result) do
115
+ described_class.new(foo: 'bar', _source: { bar: { bam: 'baz' } })
116
+ end
117
+
118
+ it 'returns a json string' do
119
+ expect(result.as_json(except: 'foo')).to eq({'_source'=>{'bar'=>{'bam'=>'baz'}}})
120
+ end
121
+ end
122
+ end
@@ -0,0 +1,56 @@
1
+ require 'spec_helper'
2
+
3
+ describe Elasticsearch::Model::Response::Results 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
+ { 'hits' => { 'total' => 123, 'max_score' => 456, 'hits' => [{'foo' => 'bar'}] } }
18
+ end
19
+
20
+ let(:search) do
21
+ Elasticsearch::Model::Searching::SearchRequest.new(OriginClass, '*').tap do |request|
22
+ allow(request).to receive(:execute!).and_return(response_document)
23
+ end
24
+ end
25
+
26
+ let(:response) do
27
+ Elasticsearch::Model::Response::Response.new(OriginClass, search)
28
+ end
29
+
30
+ let(:results) do
31
+ response.results
32
+ end
33
+
34
+ describe '#results' do
35
+
36
+ it 'provides access to the results' do
37
+ expect(results.results.size).to be(1)
38
+ expect(results.results.first.foo).to eq('bar')
39
+ end
40
+ end
41
+
42
+ describe 'Enumerable' do
43
+
44
+ it 'deletebates enumerable methods to the results' do
45
+ expect(results.empty?).to be(false)
46
+ expect(results.first.foo).to eq('bar')
47
+ end
48
+ end
49
+
50
+ describe '#raw_response' do
51
+
52
+ it 'returns the raw response document' do
53
+ expect(response.raw_response).to eq(response_document)
54
+ end
55
+ end
56
+ end