elastictastic 0.5.0 → 0.10.2
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +1 -1
- data/README.md +161 -10
- data/lib/elastictastic/adapter.rb +84 -0
- data/lib/elastictastic/association.rb +6 -0
- data/lib/elastictastic/basic_document.rb +213 -0
- data/lib/elastictastic/bulk_persistence_strategy.rb +64 -19
- data/lib/elastictastic/callbacks.rb +18 -12
- data/lib/elastictastic/child_collection_proxy.rb +15 -11
- data/lib/elastictastic/client.rb +47 -24
- data/lib/elastictastic/configuration.rb +59 -4
- data/lib/elastictastic/dirty.rb +43 -28
- data/lib/elastictastic/discrete_persistence_strategy.rb +48 -23
- data/lib/elastictastic/document.rb +1 -85
- data/lib/elastictastic/embedded_document.rb +34 -0
- data/lib/elastictastic/errors.rb +17 -5
- data/lib/elastictastic/field.rb +3 -0
- data/lib/elastictastic/mass_assignment_security.rb +2 -4
- data/lib/elastictastic/middleware.rb +66 -84
- data/lib/elastictastic/multi_get.rb +30 -0
- data/lib/elastictastic/multi_search.rb +70 -0
- data/lib/elastictastic/nested_document.rb +3 -27
- data/lib/elastictastic/new_relic_instrumentation.rb +8 -8
- data/lib/elastictastic/observing.rb +8 -6
- data/lib/elastictastic/optimistic_locking.rb +57 -0
- data/lib/elastictastic/parent_child.rb +56 -54
- data/lib/elastictastic/persistence.rb +16 -16
- data/lib/elastictastic/properties.rb +136 -96
- data/lib/elastictastic/railtie.rb +1 -1
- data/lib/elastictastic/rotor.rb +105 -0
- data/lib/elastictastic/scope.rb +186 -56
- data/lib/elastictastic/server_error.rb +20 -1
- data/lib/elastictastic/test_helpers.rb +152 -97
- data/lib/elastictastic/thrift/constants.rb +12 -0
- data/lib/elastictastic/thrift/rest.rb +83 -0
- data/lib/elastictastic/thrift/types.rb +124 -0
- data/lib/elastictastic/thrift_adapter.rb +61 -0
- data/lib/elastictastic/transport_methods.rb +27 -0
- data/lib/elastictastic/validations.rb +11 -13
- data/lib/elastictastic/version.rb +1 -1
- data/lib/elastictastic.rb +148 -27
- data/spec/environment.rb +1 -1
- data/spec/examples/bulk_persistence_strategy_spec.rb +151 -23
- data/spec/examples/callbacks_spec.rb +65 -34
- data/spec/examples/dirty_spec.rb +160 -1
- data/spec/examples/document_spec.rb +168 -106
- data/spec/examples/middleware_spec.rb +1 -61
- data/spec/examples/multi_get_spec.rb +127 -0
- data/spec/examples/multi_search_spec.rb +113 -0
- data/spec/examples/observing_spec.rb +24 -3
- data/spec/examples/optimistic_locking_spec.rb +417 -0
- data/spec/examples/parent_child_spec.rb +73 -33
- data/spec/examples/properties_spec.rb +53 -0
- data/spec/examples/rotor_spec.rb +132 -0
- data/spec/examples/scope_spec.rb +78 -18
- data/spec/examples/search_spec.rb +26 -0
- data/spec/examples/validation_spec.rb +7 -1
- data/spec/models/author.rb +1 -1
- data/spec/models/blog.rb +2 -0
- data/spec/models/comment.rb +1 -1
- data/spec/models/photo.rb +9 -0
- data/spec/models/post.rb +3 -0
- metadata +97 -78
- data/lib/elastictastic/resource.rb +0 -4
- data/spec/examples/active_model_lint_spec.rb +0 -20
@@ -34,6 +34,59 @@ describe Elastictastic::Properties do
|
|
34
34
|
it 'should map embedded object fields' do
|
35
35
|
properties['author']['properties']['id']['type'].should == 'integer'
|
36
36
|
end
|
37
|
+
|
38
|
+
it 'should set boost field' do
|
39
|
+
mapping['post']['_boost'].should == { 'name' => 'score', 'null_value' => 1.0 }
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'should set routing param if given' do
|
43
|
+
Photo.mapping['photo']['_routing'].should == {
|
44
|
+
'required' => true,
|
45
|
+
'path' => 'post_id'
|
46
|
+
}
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
describe ':preset' do
|
51
|
+
before do
|
52
|
+
Elastictastic.config.presets[:silly] = { :type => 'integer', :store => 'yes', :index => 'no' }
|
53
|
+
end
|
54
|
+
|
55
|
+
let :clazz do
|
56
|
+
Class.new do
|
57
|
+
include Elastictastic::Document
|
58
|
+
|
59
|
+
def self.name
|
60
|
+
'Clazz'
|
61
|
+
end
|
62
|
+
|
63
|
+
field :title, :type => 'string', :preset => 'silly'
|
64
|
+
field :created, :preset => :silly
|
65
|
+
field :multi, :type => 'string' do
|
66
|
+
field :searchable, :preset => 'silly', :index => 'yes'
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
let(:properties) { clazz.mapping.values.first['properties'] }
|
72
|
+
|
73
|
+
it 'should apply preset values' do
|
74
|
+
properties['created'].should == {
|
75
|
+
'type' => 'integer', 'store' => 'yes', 'index' => 'no'
|
76
|
+
}
|
77
|
+
end
|
78
|
+
|
79
|
+
it 'should override preset values with given values' do
|
80
|
+
properties['title'].should == {
|
81
|
+
'type' => 'string', 'store' => 'yes', 'index' => 'no'
|
82
|
+
}
|
83
|
+
end
|
84
|
+
|
85
|
+
it 'should apply presets to field alternates' do
|
86
|
+
properties['multi']['fields']['searchable'].should == {
|
87
|
+
'store' => 'yes', 'index' => 'yes', 'type' => 'integer'
|
88
|
+
}
|
89
|
+
end
|
37
90
|
end
|
38
91
|
|
39
92
|
describe '#elasticsearch_doc' do
|
@@ -0,0 +1,132 @@
|
|
1
|
+
require File.expand_path('../spec_helper', __FILE__)
|
2
|
+
|
3
|
+
describe Elastictastic::Rotor do
|
4
|
+
let(:client) { Elastictastic::Client.new(config) }
|
5
|
+
let(:last_request) { FakeWeb.last_request }
|
6
|
+
|
7
|
+
context 'without backoff' do
|
8
|
+
let(:config) do
|
9
|
+
Elastictastic::Configuration.new.tap do |config|
|
10
|
+
config.hosts = ['http://es1.local', 'http://es2.local']
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'should alternate requests between hosts' do
|
15
|
+
expect do
|
16
|
+
2.times do
|
17
|
+
1.upto 2 do |i|
|
18
|
+
host_status(i => true)
|
19
|
+
client.get('default', 'post', '1')
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end.not_to raise_error # We can't check the hostname of last_request in Fakeweb
|
23
|
+
end
|
24
|
+
|
25
|
+
context 'if one host fails' do
|
26
|
+
let!(:now) { Time.now.tap { |now| Time.stub(:now).and_return(now) }}
|
27
|
+
|
28
|
+
before do
|
29
|
+
host_status(1 => false, 2 => true)
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'should try the next host' do
|
33
|
+
client.get('default', 'post', '1').should == { 'success' => true }
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'should pass PUT body to retry' do
|
37
|
+
client.update('default', 'post', '1', { 'title' => 'pizza' })
|
38
|
+
FakeWeb.last_request.body.should == { 'title' => 'pizza' }.to_json
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
context 'if all hosts fail' do
|
43
|
+
let!(:now) { Time.now.tap { |now| Time.stub(:now).and_return(now) }}
|
44
|
+
|
45
|
+
before do
|
46
|
+
host_status(1 => false, 2 => false)
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'should raise error if no hosts respond' do
|
50
|
+
expect { client.get('default', 'post', '1') }.to(raise_error Elastictastic::NoServerAvailable)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
context 'with backoff' do
|
56
|
+
let(:config) do
|
57
|
+
Elastictastic::Configuration.new.tap do |config|
|
58
|
+
config.hosts = ['http://es1.local', 'http://es2.local']
|
59
|
+
config.backoff_threshold = 2
|
60
|
+
config.backoff_start = 1
|
61
|
+
config.backoff_max = 4
|
62
|
+
end
|
63
|
+
end
|
64
|
+
let!(:time) { Time.now.tap { |time| Time.stub(:now).and_return(time) }}
|
65
|
+
|
66
|
+
before do
|
67
|
+
host_status(1 => false, 2 => true)
|
68
|
+
end
|
69
|
+
|
70
|
+
it 'should retry immediately before reaching initial failure count' do
|
71
|
+
client.get('default', 'post', '1')
|
72
|
+
expect { client.get('default', 'post', '1') }.to change(FakeWeb.requests, :length).by(2)
|
73
|
+
end
|
74
|
+
|
75
|
+
it 'should back off after initial failure count reached' do
|
76
|
+
2.times { client.get('default', 'post', '1') }
|
77
|
+
expect { client.get('default', 'post', '1') }.to change(FakeWeb.requests, :length).by(1)
|
78
|
+
end
|
79
|
+
|
80
|
+
it 'should retry after initial backoff period elapses' do
|
81
|
+
3.times { client.get('default', 'post', '1') }
|
82
|
+
Time.stub(:now).and_return(time + 1)
|
83
|
+
expect { client.get('default', 'post', '1') }.to change(FakeWeb.requests, :length).by(2)
|
84
|
+
end
|
85
|
+
|
86
|
+
it 'should double backoff after another failure' do
|
87
|
+
3.times { client.get('default', 'post', '1') }
|
88
|
+
Time.stub(:now).and_return(time + 1)
|
89
|
+
client.get('default', 'post', '1')
|
90
|
+
Time.stub(:now).and_return(time + 2)
|
91
|
+
expect { client.get('default', 'post', '1') }.to change(FakeWeb.requests, :length).by(1)
|
92
|
+
end
|
93
|
+
|
94
|
+
it 'should cap backoff interval at backoff_max' do
|
95
|
+
2.times { client.get('default', 'post', '1') } # first backoff - 1 second
|
96
|
+
Time.stub(:now).and_return(time + 1)
|
97
|
+
client.get('default', 'post', '1') # second backoff - 2 seconds
|
98
|
+
Time.stub(:now).and_return(time + 3)
|
99
|
+
client.get('default', 'post', '1') # third backoff - 4 seconds
|
100
|
+
Time.stub(:now).and_return(time + 7)
|
101
|
+
client.get('default', 'post', '1') # fourth backoff - 4 seconds again
|
102
|
+
Time.stub(:now).and_return(time + 11)
|
103
|
+
expect { client.get('default', 'post', '1') }.to change(FakeWeb.requests, :length).by(2)
|
104
|
+
end
|
105
|
+
|
106
|
+
it 'should reset backoff after a successful request' do
|
107
|
+
2.times { client.get('default', 'post', '1') } # initial backoff - 1 second
|
108
|
+
host_status(1 => true, 2 => true)
|
109
|
+
Time.stub(:now).and_return(time + 1)
|
110
|
+
2.times { client.get('default', 'post', '1') } # first one will go to es2 because of rotation. second one has success so es1 should reset
|
111
|
+
host_status(1 => false, 2 => true)
|
112
|
+
client.get('default', 'post', '1') # should be willing to immediately retry
|
113
|
+
host_status(1 => true, 2 => false)
|
114
|
+
expect { client.get('default', 'post', '1') }.to_not raise_error # only will succeed if it retries #1
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
private
|
119
|
+
|
120
|
+
def host_status(statuses)
|
121
|
+
FakeWeb.clean_registry
|
122
|
+
statuses.each_pair do |i, healthy|
|
123
|
+
url = %r(^http://es#{i}.local/)
|
124
|
+
if healthy
|
125
|
+
options = { :body => '{"success":true}' }
|
126
|
+
else
|
127
|
+
options = { :exception => Errno::ECONNREFUSED }
|
128
|
+
end
|
129
|
+
FakeWeb.register_uri(:any, url, options)
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
data/spec/examples/scope_spec.rb
CHANGED
@@ -7,7 +7,7 @@ describe Elastictastic::Scope do
|
|
7
7
|
include Elastictastic::TestHelpers
|
8
8
|
|
9
9
|
let(:last_request) { FakeWeb.last_request }
|
10
|
-
let(:last_request_body) {
|
10
|
+
let(:last_request_body) { Elastictastic.json_decode(last_request.body) }
|
11
11
|
let(:last_request_path) { last_request.path.split('?', 2)[0] }
|
12
12
|
let(:last_request_params) { last_request.path.split('?', 2)[1].try(:split, '&') }
|
13
13
|
|
@@ -20,7 +20,7 @@ describe Elastictastic::Scope do
|
|
20
20
|
let(:scroll_requests) { FakeWeb.requests[1..-1] }
|
21
21
|
|
22
22
|
before do
|
23
|
-
@scroll_ids =
|
23
|
+
@scroll_ids = stub_es_scan(
|
24
24
|
'default', 'post', 2, *make_hits(3)
|
25
25
|
)
|
26
26
|
end
|
@@ -41,7 +41,7 @@ describe Elastictastic::Scope do
|
|
41
41
|
end
|
42
42
|
|
43
43
|
it 'should send query in data for initial search' do
|
44
|
-
scan_request.body.should == scope.params
|
44
|
+
scan_request.body.should == Elastictastic.json_encode(scope.params)
|
45
45
|
end
|
46
46
|
|
47
47
|
it 'should send POST request initially' do
|
@@ -81,7 +81,7 @@ describe Elastictastic::Scope do
|
|
81
81
|
let(:scope) { Post.from(10).size(10) }
|
82
82
|
|
83
83
|
before do
|
84
|
-
|
84
|
+
stub_es_search(
|
85
85
|
'default', 'post', 'hits' => {
|
86
86
|
'total' => 2,
|
87
87
|
'hits' => make_hits(2)
|
@@ -109,18 +109,35 @@ describe Elastictastic::Scope do
|
|
109
109
|
end
|
110
110
|
end # context 'with from/size'
|
111
111
|
|
112
|
+
describe 'with page out of range' do
|
113
|
+
let(:scope) { Post.from(10).size(10) }
|
114
|
+
|
115
|
+
before do
|
116
|
+
stub_es_search(
|
117
|
+
'default', 'post', 'hits' => {
|
118
|
+
'total' => 2,
|
119
|
+
'hits' => []
|
120
|
+
}
|
121
|
+
)
|
122
|
+
end
|
123
|
+
|
124
|
+
it 'should return empty array of results' do
|
125
|
+
scope.to_a.should == []
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
112
129
|
context 'with sort but no from/size' do
|
113
130
|
let(:scope) { Post.sort(:title => 'asc') }
|
114
131
|
let(:requests) { FakeWeb.requests }
|
115
132
|
let(:request_bodies) do
|
116
133
|
requests.map do |request|
|
117
|
-
|
134
|
+
Elastictastic.json_decode(request.body)
|
118
135
|
end
|
119
136
|
end
|
120
137
|
|
121
138
|
before do
|
122
139
|
Elastictastic.config.default_batch_size = 2
|
123
|
-
|
140
|
+
stub_es_search(
|
124
141
|
'default', 'post',
|
125
142
|
make_hits(3).each_slice(2).map { |batch| { 'hits' => { 'hits' => batch, 'total' => 3 }} }
|
126
143
|
)
|
@@ -169,6 +186,38 @@ describe Elastictastic::Scope do
|
|
169
186
|
scope.each { |post| post.should be_persisted }
|
170
187
|
end
|
171
188
|
end # context 'with sort but no from/size'
|
189
|
+
|
190
|
+
describe 'with routing' do
|
191
|
+
it 'should send routing param in single-search query' do
|
192
|
+
stub_es_search(
|
193
|
+
'default', 'post',
|
194
|
+
'hits' => {'total' => 2, 'hits' => make_hits(2)}
|
195
|
+
)
|
196
|
+
Post.routing('7').size(10).to_a
|
197
|
+
last_request_uri.query.split('&').should include('routing=7')
|
198
|
+
end
|
199
|
+
|
200
|
+
it 'should send routing param in scan query' do
|
201
|
+
stub_es_scan(
|
202
|
+
'default', 'post', 2, *make_hits(3)
|
203
|
+
)
|
204
|
+
Post.routing('7').to_a
|
205
|
+
URI.parse(FakeWeb.requests.first.path).query.split('&').
|
206
|
+
should include('routing=7')
|
207
|
+
end
|
208
|
+
|
209
|
+
it 'should send routing param in batch-search queries' do
|
210
|
+
Elastictastic.config.default_batch_size = 2
|
211
|
+
stub_es_search(
|
212
|
+
'default', 'post',
|
213
|
+
make_hits(3).each_slice(2).map { |batch| { 'hits' => { 'hits' => batch, 'total' => 3 }} }
|
214
|
+
)
|
215
|
+
Post.routing('7').sort(:score).to_a
|
216
|
+
FakeWeb.requests.each do |request|
|
217
|
+
URI.parse(request.path).query.split('&').should include('routing=7')
|
218
|
+
end
|
219
|
+
end
|
220
|
+
end
|
172
221
|
end # describe '#each'
|
173
222
|
|
174
223
|
describe 'hit metadata' do
|
@@ -203,7 +252,7 @@ describe Elastictastic::Scope do
|
|
203
252
|
let(:scope) { Post }
|
204
253
|
|
205
254
|
before do
|
206
|
-
|
255
|
+
stub_es_scan('default', 'post', 2, *hits)
|
207
256
|
end
|
208
257
|
|
209
258
|
it_should_behave_like 'enumerator with hit metadata'
|
@@ -214,7 +263,7 @@ describe Elastictastic::Scope do
|
|
214
263
|
|
215
264
|
before do
|
216
265
|
batches = hits.each_slice(2).map { |batch| { 'hits' => { 'hits' => batch, 'total' => 3 }} }
|
217
|
-
|
266
|
+
stub_es_search('default', 'post', batches)
|
218
267
|
end
|
219
268
|
|
220
269
|
it_should_behave_like 'enumerator with hit metadata'
|
@@ -224,7 +273,7 @@ describe Elastictastic::Scope do
|
|
224
273
|
let(:scope) { Post.size(3) }
|
225
274
|
|
226
275
|
before do
|
227
|
-
|
276
|
+
stub_es_search('default', 'post', 'hits' => { 'hits' => hits, 'total' => 3 })
|
228
277
|
end
|
229
278
|
|
230
279
|
it_should_behave_like 'enumerator with hit metadata'
|
@@ -234,7 +283,7 @@ describe Elastictastic::Scope do
|
|
234
283
|
describe '#count' do
|
235
284
|
context 'with no operations performed yet' do
|
236
285
|
let!(:count) do
|
237
|
-
|
286
|
+
stub_es_search('default', 'post', 'hits' => { 'total' => 3 })
|
238
287
|
Post.all.count
|
239
288
|
end
|
240
289
|
|
@@ -249,7 +298,7 @@ describe Elastictastic::Scope do
|
|
249
298
|
|
250
299
|
context 'with scan search performed' do
|
251
300
|
let!(:count) do
|
252
|
-
|
301
|
+
stub_es_scan(
|
253
302
|
'default', 'post', 2, *make_hits(3)
|
254
303
|
)
|
255
304
|
scope = Post.all
|
@@ -268,7 +317,7 @@ describe Elastictastic::Scope do
|
|
268
317
|
|
269
318
|
context 'with paginated search performed' do
|
270
319
|
let!(:count) do
|
271
|
-
|
320
|
+
stub_es_search(
|
272
321
|
'default', 'post', 'hits' => {
|
273
322
|
'hits' => make_hits(3),
|
274
323
|
'total' => 3
|
@@ -290,7 +339,7 @@ describe Elastictastic::Scope do
|
|
290
339
|
|
291
340
|
context 'with paginated scan performed' do
|
292
341
|
let!(:count) do
|
293
|
-
|
342
|
+
stub_es_search(
|
294
343
|
'default', 'post', 'hits' => {
|
295
344
|
'hits' => make_hits(2),
|
296
345
|
'total' => 2
|
@@ -309,6 +358,17 @@ describe Elastictastic::Scope do
|
|
309
358
|
FakeWeb.should have(1).request
|
310
359
|
end
|
311
360
|
end # context 'with paginated scan performed'
|
361
|
+
|
362
|
+
context 'with routing specified' do
|
363
|
+
let!(:count) do
|
364
|
+
stub_es_search('default', 'post', 'hits' => { 'total' => 3 })
|
365
|
+
Post.routing(7).count
|
366
|
+
end
|
367
|
+
|
368
|
+
it 'should send routing parameter' do
|
369
|
+
last_request_uri.query.split('&').should include('routing=7')
|
370
|
+
end
|
371
|
+
end
|
312
372
|
end # describe '#count'
|
313
373
|
|
314
374
|
describe '#all_facets' do
|
@@ -327,7 +387,7 @@ describe Elastictastic::Scope do
|
|
327
387
|
|
328
388
|
context 'with no requests performed' do
|
329
389
|
let!(:facets) do
|
330
|
-
|
390
|
+
stub_es_search(
|
331
391
|
'default', 'post',
|
332
392
|
'hits' => { 'hits' => [], 'total' => 2 },
|
333
393
|
'facets' => facet_response
|
@@ -346,7 +406,7 @@ describe Elastictastic::Scope do
|
|
346
406
|
|
347
407
|
context 'with count request performed' do
|
348
408
|
let!(:facets) do
|
349
|
-
|
409
|
+
stub_es_search(
|
350
410
|
'default', 'post',
|
351
411
|
'hits' => { 'hits' => [], 'total' => 2 },
|
352
412
|
'facets' => facet_response
|
@@ -366,7 +426,7 @@ describe Elastictastic::Scope do
|
|
366
426
|
|
367
427
|
context 'with single-page search performed' do
|
368
428
|
let!(:facets) do
|
369
|
-
|
429
|
+
stub_es_search(
|
370
430
|
'default', 'post',
|
371
431
|
'hits' => { 'hits' => make_hits(2), 'total' => 2 },
|
372
432
|
'facets' => facet_response
|
@@ -387,7 +447,7 @@ describe Elastictastic::Scope do
|
|
387
447
|
|
388
448
|
context 'with multi-page search performed' do
|
389
449
|
let!(:facets) do
|
390
|
-
|
450
|
+
stub_es_search(
|
391
451
|
'default', 'post',
|
392
452
|
'hits' => { 'hits' => make_hits(2), 'total' => 2 },
|
393
453
|
'facets' => facet_response
|
@@ -410,7 +470,7 @@ describe Elastictastic::Scope do
|
|
410
470
|
describe '#first' do
|
411
471
|
shared_examples_for 'first method' do
|
412
472
|
before do
|
413
|
-
|
473
|
+
stub_es_search(
|
414
474
|
index, 'post', 'hits' => {
|
415
475
|
'total' => 12,
|
416
476
|
'hits' => make_hits(1)
|
@@ -6,6 +6,8 @@ require File.expand_path('../spec_helper', __FILE__)
|
|
6
6
|
# search and dealing with the results
|
7
7
|
#
|
8
8
|
describe Elastictastic::Search do
|
9
|
+
include Elastictastic::TestHelpers
|
10
|
+
|
9
11
|
describe '#to_params' do
|
10
12
|
{
|
11
13
|
'query' => { 'match_all' => {} },
|
@@ -47,6 +49,30 @@ describe Elastictastic::Search do
|
|
47
49
|
end
|
48
50
|
end
|
49
51
|
|
52
|
+
describe '#[]' do
|
53
|
+
it 'should run #first query with an integer argument' do
|
54
|
+
stub_es_search('default', 'post', 'hits' => {
|
55
|
+
'total' => '2',
|
56
|
+
'hits' => [generate_es_hit('post', :id => '1')]
|
57
|
+
})
|
58
|
+
Post.all[4].id.should == '1'
|
59
|
+
last_request_json['from'].should == 4
|
60
|
+
last_request_json['size'].should == 1
|
61
|
+
end
|
62
|
+
|
63
|
+
it 'should add from/size to scope with a range argument' do
|
64
|
+
params = Post.all[2..4].params
|
65
|
+
params['from'].should == 2
|
66
|
+
params['size'].should == 3
|
67
|
+
end
|
68
|
+
|
69
|
+
it 'should add from/size to scope with an end-excluded range argument' do
|
70
|
+
params = Post.all[2...4].params
|
71
|
+
params['from'].should == 2
|
72
|
+
params['size'].should == 2
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
50
76
|
describe 'merging' do
|
51
77
|
let(:scope) { Post }
|
52
78
|
|
@@ -21,12 +21,18 @@ describe Elastictastic::Validations do
|
|
21
21
|
it 'should raise Elastictastic::RecordInvalid for save!' do
|
22
22
|
expect { post.save! }.to raise_error(Elastictastic::RecordInvalid)
|
23
23
|
end
|
24
|
+
|
25
|
+
it 'should save successfully if validations disabled' do
|
26
|
+
stub_es_create('default', 'post')
|
27
|
+
post.save(:validate => false)
|
28
|
+
FakeWeb.last_request.path.should == '/default/post'
|
29
|
+
end
|
24
30
|
end
|
25
31
|
|
26
32
|
describe 'with valid data' do
|
27
33
|
let(:post) { Post.new }
|
28
34
|
|
29
|
-
before {
|
35
|
+
before { stub_es_create('default', 'post') }
|
30
36
|
|
31
37
|
it 'should be valid' do
|
32
38
|
post.should be_valid
|
data/spec/models/author.rb
CHANGED
data/spec/models/blog.rb
CHANGED
data/spec/models/comment.rb
CHANGED
data/spec/models/post.rb
CHANGED
@@ -3,12 +3,15 @@ class Post
|
|
3
3
|
|
4
4
|
field :title
|
5
5
|
field :comments_count, :type => 'integer'
|
6
|
+
field :score, :type => 'integer'
|
6
7
|
field :tags, :index => 'analyzed' do
|
7
8
|
field :non_analyzed, :index => 'not_analyzed'
|
8
9
|
end
|
9
10
|
field :created_at, :type => 'date'
|
10
11
|
field :published_at, :type => 'date'
|
11
12
|
|
13
|
+
boost :score
|
14
|
+
|
12
15
|
embed :author
|
13
16
|
embed :comments
|
14
17
|
|