riak-client 2.1.0 → 2.2.0.pre1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +3 -1
- data/Guardfile +15 -9
- data/README.markdown +118 -9
- data/lib/riak/bucket.rb +16 -1
- data/lib/riak/bucket_properties.rb +67 -0
- data/lib/riak/bucket_type.rb +48 -0
- data/lib/riak/bucket_typed/bucket.rb +113 -0
- data/lib/riak/client/beefcake/bucket_properties_operator.rb +178 -0
- data/lib/riak/client/beefcake/message_overlay.rb +42 -20
- data/lib/riak/client/beefcake/object_methods.rb +1 -1
- data/lib/riak/client/beefcake/socket.rb +6 -6
- data/lib/riak/client/beefcake_protobuffs_backend.rb +44 -60
- data/lib/riak/client/protobuffs_backend.rb +8 -8
- data/lib/riak/client.rb +7 -2
- data/lib/riak/crdt/base.rb +29 -1
- data/lib/riak/crdt/counter.rb +7 -3
- data/lib/riak/crdt/inner_flag.rb +1 -1
- data/lib/riak/crdt/map.rb +7 -3
- data/lib/riak/crdt/set.rb +7 -4
- data/lib/riak/errors/failed_request.rb +2 -0
- data/lib/riak/errors/search_error.rb +29 -0
- data/lib/riak/locale/en.yml +7 -0
- data/lib/riak/map_reduce.rb +70 -6
- data/lib/riak/robject.rb +19 -3
- data/lib/riak/search/index.rb +73 -0
- data/lib/riak/search/query.rb +133 -0
- data/lib/riak/search/result_collection.rb +91 -0
- data/lib/riak/search/result_document.rb +59 -0
- data/lib/riak/search/schema.rb +65 -0
- data/lib/riak/search.rb +10 -0
- data/lib/riak/secondary_index.rb +6 -0
- data/lib/riak/version.rb +1 -1
- data/spec/fixtures/yz_schema_template.xml +18 -0
- data/spec/integration/riak/bucket_types_spec.rb +218 -39
- data/spec/integration/riak/conflict_resolution_spec.rb +96 -0
- data/spec/integration/riak/crdt_spec.rb +30 -2
- data/spec/integration/riak/properties_spec.rb +69 -0
- data/spec/integration/riak/search_spec.rb +104 -0
- data/spec/integration/riak/secondary_index_spec.rb +18 -0
- data/spec/riak/beefcake_protobuffs_backend/bucket_properties_operator_spec.rb +247 -0
- data/spec/riak/bucket_properties_spec.rb +114 -0
- data/spec/riak/bucket_type_spec.rb +34 -0
- data/spec/riak/bucket_typed/bucket.rb +43 -0
- data/spec/riak/client_spec.rb +16 -8
- data/spec/riak/crdt/counter_spec.rb +2 -1
- data/spec/riak/crdt/map_spec.rb +2 -1
- data/spec/riak/crdt/set_spec.rb +2 -1
- data/spec/riak/map_reduce_spec.rb +169 -124
- data/spec/riak/search/index_spec.rb +62 -0
- data/spec/riak/search/query_spec.rb +88 -0
- data/spec/riak/search/result_collection_spec.rb +87 -0
- data/spec/riak/search/result_document_spec.rb +63 -0
- data/spec/riak/search/schema_spec.rb +63 -0
- data/spec/spec_helper.rb +2 -1
- data/spec/support/search_config.rb +81 -0
- data/spec/support/test_client.yml +14 -7
- metadata +44 -5
@@ -2,60 +2,239 @@ require 'spec_helper'
|
|
2
2
|
require 'riak'
|
3
3
|
|
4
4
|
describe 'Bucket Types', test_client: true, integration: true do
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
object.data = 'hello'
|
13
|
-
object.content_type = 'text/plain'
|
14
|
-
object.store type: bucket_type
|
15
|
-
object
|
5
|
+
|
6
|
+
describe 'nested bucket types API' do
|
7
|
+
let(:bucket_type){ test_client.bucket_type 'yokozuna' }
|
8
|
+
|
9
|
+
it 'exposes bucket type properties' do
|
10
|
+
expect(props = bucket_type.properties).to be_a Hash
|
11
|
+
expect(props[:allow_mult]).to be
|
16
12
|
end
|
17
13
|
|
18
|
-
|
19
|
-
|
20
|
-
|
14
|
+
describe 'performing key-value operations' do
|
15
|
+
let(:bucket){ bucket_type.bucket(random_key) }
|
16
|
+
let(:untyped_bucket){ test_client.bucket bucket.name }
|
17
|
+
|
18
|
+
let(:object) do
|
19
|
+
object = bucket.new random_key
|
20
|
+
object.data = 'hello'
|
21
|
+
object.content_type = 'text/plain'
|
22
|
+
object.store
|
23
|
+
object
|
24
|
+
end
|
25
|
+
|
26
|
+
let(:untyped_object) do
|
27
|
+
untyped_object = untyped_bucket.new object.key
|
28
|
+
untyped_object.data = 'oooops'
|
29
|
+
untyped_object.content_type = 'text/plain'
|
30
|
+
untyped_object.store
|
31
|
+
untyped_object
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'initializes with a bucket type' do
|
35
|
+
o = bucket.new 'lawnmower'
|
36
|
+
o.data = 'reel'
|
37
|
+
o.content_type = 'text/plain'
|
38
|
+
o.store
|
39
|
+
|
40
|
+
expect(bucket.get('lawnmower').data).to eq o.data
|
41
|
+
expect(bucket.exists?('lawnmower')).to be
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'only retrieves with a bucket type' do
|
45
|
+
expect(bucket.get(object.key).data).to eq object.data
|
46
|
+
expect{ untyped_bucket.get object.key }.to raise_error /not_found/
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'reloads with a bucket type' do
|
50
|
+
expect{ object.reload }.to_not raise_error
|
51
|
+
expect(object.data).to eq 'hello'
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'lists keys only for the type' do
|
55
|
+
expect(untyped_bucket).to be # ensure existence
|
56
|
+
expect(object).to be
|
57
|
+
|
58
|
+
expect(untyped_bucket.keys).to be_empty
|
59
|
+
expect(bucket.keys).to include object.key
|
60
|
+
end
|
61
|
+
|
62
|
+
describe 'deletion' do
|
63
|
+
it 'self-deletes with a bucket type' do
|
64
|
+
expect(untyped_object).to be # ensure existence
|
65
|
+
|
66
|
+
expect(object.delete).to be
|
67
|
+
expect{ object.reload }.to raise_error /not_found/
|
68
|
+
expect(untyped_object).to be
|
69
|
+
expect{ untyped_object.reload }.to_not raise_error
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'deletes from the typed bucket' do
|
73
|
+
expect(untyped_object).to be # ensure existence
|
74
|
+
|
75
|
+
expect(bucket.delete object.key).to be
|
76
|
+
expect{ object.reload }.to raise_error /not_found/
|
77
|
+
expect{ untyped_object.reload }.to_not raise_error
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
it 'multigets keys' do
|
82
|
+
results = bucket.get_many [object.key]
|
83
|
+
expect(results[object.key]).to be
|
84
|
+
expect(results[object.key].data).to eq object.data
|
85
|
+
end
|
86
|
+
|
87
|
+
describe 'secondary indexes' do
|
88
|
+
it 'finds the correct object with a SecondaryIndex instance' do
|
89
|
+
expect(untyped_object).to be
|
90
|
+
q = Riak::SecondaryIndex.new bucket, '$key', object.key
|
91
|
+
|
92
|
+
expect(q.keys).to include object.key
|
93
|
+
candidate = q.values.first
|
94
|
+
expect(candidate.data).to eq object.data
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
describe 'map-reduce' do
|
99
|
+
let(:mapred) do
|
100
|
+
Riak::MapReduce.new(test_client) do |mr|
|
101
|
+
mr.map 'function(obj){return [obj.values[0].data];}', keep: true
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
it 'map-reduces correctly with a typed bucket' do
|
106
|
+
expect(object).to be
|
107
|
+
expect(untyped_object).to be
|
108
|
+
|
109
|
+
mapred.add bucket
|
110
|
+
result = mapred.run
|
111
|
+
|
112
|
+
expect(result).to include object.data
|
113
|
+
expect(result).to_not include untyped_object.data
|
114
|
+
end
|
115
|
+
|
116
|
+
it 'map-reduces correctly with a robject in a typed bucket' do
|
117
|
+
expect(object).to be
|
118
|
+
expect(untyped_object).to be
|
119
|
+
|
120
|
+
mapred.add object
|
121
|
+
result = mapred.run
|
122
|
+
|
123
|
+
expect(result).to include object.data
|
124
|
+
expect(result).to_not include untyped_object.data
|
125
|
+
end
|
126
|
+
end
|
21
127
|
end
|
22
128
|
|
23
|
-
|
24
|
-
|
25
|
-
|
129
|
+
describe 'manipulating bucket properties' do
|
130
|
+
let(:bucket_type){ test_client.bucket_type 'yokozuna' }
|
131
|
+
let(:bucket){ bucket_type.bucket random_key }
|
132
|
+
let(:untyped_bucket){ test_client.bucket bucket.name }
|
133
|
+
|
134
|
+
it 'allows reading and writing bucket properties' do
|
135
|
+
expect(test_client.get_bucket_props(bucket, type: 'yokozuna')['last_write_wins']).to_not be
|
136
|
+
expect(test_client.get_bucket_props(untyped_bucket)['last_write_wins']).to_not be
|
137
|
+
|
138
|
+
# test setting
|
139
|
+
expect{ bucket.props = {'last_write_wins' => true} }.to_not raise_error
|
140
|
+
|
141
|
+
# make sure setting doesn't leak to untyped bucket
|
142
|
+
expect(test_client.get_bucket_props(bucket, type: 'yokozuna')['last_write_wins']).to be
|
143
|
+
expect(test_client.get_bucket_props(untyped_bucket)['last_write_wins']).to_not be
|
144
|
+
|
145
|
+
# add canary setting on untyped bucket
|
146
|
+
expect{ untyped_bucket.props = { 'n_val' => 1} }.to_not raise_error
|
26
147
|
|
27
|
-
|
28
|
-
|
148
|
+
# make sure canary setting doesn't leak to typed bucket
|
149
|
+
expect(test_client.get_bucket_props(bucket, type: 'yokozuna')['n_val']).to_not eq 1
|
150
|
+
expect(test_client.get_bucket_props(untyped_bucket)['n_val']).to eq 1
|
151
|
+
|
152
|
+
# test clearing
|
153
|
+
expect{ bucket.clear_props }.to_not raise_error
|
154
|
+
|
155
|
+
# make sure clearing doesn't leak to canary setting on untyped bucket
|
156
|
+
expect(test_client.get_bucket_props(bucket, type: 'yokozuna')['last_write_wins']).to_not be
|
157
|
+
expect(test_client.get_bucket_props(untyped_bucket)['n_val']).to eq 1
|
158
|
+
end
|
29
159
|
end
|
30
160
|
|
31
|
-
|
32
|
-
|
33
|
-
|
161
|
+
describe 'performing CRDT operations' do
|
162
|
+
let(:bucket_type){ test_client.bucket_type 'other_counters' }
|
163
|
+
let(:bucket){ bucket_type.bucket random_key }
|
164
|
+
let(:counter){ Riak::Crdt::Counter.new bucket, random_key }
|
165
|
+
|
166
|
+
let(:untyped_bucket){ test_client.bucket bucket.name }
|
167
|
+
let(:untyped_counter){ Riak::Crdt::Counter.new untyped_bucket, random_key }
|
168
|
+
|
169
|
+
it 'overrides default bucket types for CRDTs' do
|
170
|
+
expect(untyped_counter.value).to eq 0
|
171
|
+
expect(untyped_counter.bucket_type).to eq Riak::Crdt::DEFAULT_BUCKET_TYPES[:counter]
|
34
172
|
|
35
|
-
|
36
|
-
|
173
|
+
untyped_counter.increment
|
174
|
+
counter.reload
|
175
|
+
|
176
|
+
expect(counter.value).to eq 0
|
177
|
+
expect(counter.bucket_type).to eq 'other_counters'
|
178
|
+
end
|
37
179
|
end
|
38
180
|
end
|
39
181
|
|
40
|
-
describe '
|
41
|
-
let(:
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
182
|
+
describe 'option-based bucket types API' do
|
183
|
+
let(:bucket){ random_bucket 'bucket_type_spec' }
|
184
|
+
|
185
|
+
describe 'performing key-value operations' do
|
186
|
+
# for the sake of having a non-default one, not search
|
187
|
+
let(:bucket_type){ 'yokozuna' }
|
188
|
+
let(:object) do
|
189
|
+
object = bucket.new random_key
|
190
|
+
object.data = 'hello'
|
191
|
+
object.content_type = 'text/plain'
|
192
|
+
object.store type: bucket_type
|
193
|
+
object
|
194
|
+
end
|
195
|
+
|
196
|
+
it 'only retrieves with a bucket type' do
|
197
|
+
expect{ bucket.get object.key, type: bucket_type }.to_not raise_error
|
198
|
+
expect{ bucket.get object.key }.to raise_error /not_found/
|
199
|
+
end
|
200
|
+
|
201
|
+
it 'deletes from the bucket only with a bucket type' do
|
202
|
+
expect(bucket.delete object.key).to eq true
|
203
|
+
expect{ bucket.get object.key, type: bucket_type }.to_not raise_error
|
47
204
|
|
48
|
-
|
49
|
-
|
50
|
-
|
205
|
+
expect{ bucket.delete object.key, type: bucket_type }.to_not raise_error
|
206
|
+
expect{ bucket.get object.key, type: bucket_type }.to raise_error /not_found/
|
207
|
+
end
|
208
|
+
|
209
|
+
it 'self-deletes only with a bucket type' do
|
210
|
+
expect(object.delete).to be
|
211
|
+
expect{ object.reload type: bucket_type }.to_not raise_error
|
212
|
+
|
213
|
+
expect(object.delete type: bucket_type).to be
|
214
|
+
expect{ object.reload type: bucket_type }.to raise_error /not_found/
|
215
|
+
end
|
51
216
|
end
|
52
217
|
|
53
|
-
|
54
|
-
|
55
|
-
|
218
|
+
describe 'performing CRDT set operations' do
|
219
|
+
let(:bucket_type){ Riak::Crdt::DEFAULT_BUCKET_TYPES[:set] }
|
220
|
+
let(:set) do
|
221
|
+
set = Riak::Crdt::Set.new bucket, random_key
|
222
|
+
set.add random_key
|
223
|
+
set
|
224
|
+
end
|
225
|
+
|
226
|
+
it 'retrieves the set blob via key-value using a bucket type' do
|
227
|
+
expect{ bucket.get set.key }.to raise_error /not_found/
|
228
|
+
expect(bucket.get set.key, type: bucket_type).to be
|
229
|
+
end
|
230
|
+
|
231
|
+
it 'deletes the set blob through the bucket type' do
|
232
|
+
expect(bucket.delete set.key).to be
|
233
|
+
expect{ bucket.get set.key, type: bucket_type }.to_not raise_error
|
56
234
|
|
57
|
-
|
58
|
-
|
235
|
+
expect(bucket.delete set.key, type: bucket_type).to be
|
236
|
+
expect{ bucket.get set.key, type: bucket_type }.to raise_error /not_found/
|
237
|
+
end
|
59
238
|
end
|
60
239
|
end
|
61
240
|
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'riak'
|
3
|
+
|
4
|
+
describe 'Conflict resolution', integration: true, test_client: true do
|
5
|
+
let(:bucket) do
|
6
|
+
bucket = random_bucket
|
7
|
+
bucket.allow_mult = true
|
8
|
+
|
9
|
+
bucket
|
10
|
+
end
|
11
|
+
|
12
|
+
subject do
|
13
|
+
robj = bucket.new
|
14
|
+
robj.content_type = 'application/json'
|
15
|
+
robj.data = 100
|
16
|
+
robj.store
|
17
|
+
|
18
|
+
robj
|
19
|
+
end
|
20
|
+
|
21
|
+
let(:ten_conflicted_robjects) do
|
22
|
+
10.times.map do |n|
|
23
|
+
t = bucket.new subject.key
|
24
|
+
t.data = rand 50
|
25
|
+
t.store
|
26
|
+
|
27
|
+
t
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
before(:each) do
|
32
|
+
ten_conflicted_robjects
|
33
|
+
subject.reload
|
34
|
+
end
|
35
|
+
|
36
|
+
describe 'on_conflict hooks' do
|
37
|
+
after(:each) do
|
38
|
+
Riak::RObject.on_conflict_hooks.delete_if{ |i| true }
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'resolve ten-sided conflicts' do
|
42
|
+
expect(subject).to be_conflict
|
43
|
+
|
44
|
+
# resolver
|
45
|
+
Riak::RObject.on_conflict do |obj|
|
46
|
+
next nil unless obj.siblings.first.data.is_a? Numeric
|
47
|
+
new_sibling = obj.siblings.inject do |memo, sib|
|
48
|
+
memo.data = [memo.data, sib.data].max
|
49
|
+
|
50
|
+
memo
|
51
|
+
end
|
52
|
+
|
53
|
+
obj.siblings = [new_sibling.dup]
|
54
|
+
|
55
|
+
obj
|
56
|
+
end
|
57
|
+
|
58
|
+
subject.attempt_conflict_resolution
|
59
|
+
subject.reload
|
60
|
+
|
61
|
+
expect(subject).to_not be_conflict
|
62
|
+
expect(subject.data).to eq 100
|
63
|
+
|
64
|
+
end
|
65
|
+
|
66
|
+
it "doesn't resolve impossible conflicts" do
|
67
|
+
expect(subject).to be_conflict
|
68
|
+
|
69
|
+
Riak::RObject.on_conflict do |obj|
|
70
|
+
nil
|
71
|
+
end
|
72
|
+
|
73
|
+
subject.reload
|
74
|
+
|
75
|
+
expect(subject).to be_conflict
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
describe 'clobbering siblings without a hook' do
|
80
|
+
it 'resolves ten-sided conflicts' do
|
81
|
+
expect(subject).to be_conflict
|
82
|
+
expect(subject.siblings.length).to eq 11
|
83
|
+
max_sibling = subject.siblings.inject do |memo, sib|
|
84
|
+
next memo if memo.data > sib.data
|
85
|
+
next sib
|
86
|
+
end
|
87
|
+
|
88
|
+
subject.siblings = [max_sibling]
|
89
|
+
subject.store
|
90
|
+
|
91
|
+
subject.reload
|
92
|
+
expect(subject).to_not be_conflict
|
93
|
+
expect(subject.data).to eq 100
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
@@ -15,8 +15,25 @@ describe "CRDTs", integration: true, test_client: true do
|
|
15
15
|
expect(Riak::Crdt::Set.new(bucket, 'set').bucket_type).to eq 'sets'
|
16
16
|
end
|
17
17
|
|
18
|
-
|
19
|
-
|
18
|
+
describe 'overriding bucket-types' do
|
19
|
+
let(:name){ 'other_counters' }
|
20
|
+
let(:type){ test_client.bucket_type name }
|
21
|
+
let(:typed_bucket){ type.bucket bucket.name }
|
22
|
+
|
23
|
+
it "overrides with a string" do
|
24
|
+
ctr = Riak::Crdt::Counter.new(bucket, 'ctr', name)
|
25
|
+
expect(ctr.bucket_type).to eq name
|
26
|
+
end
|
27
|
+
|
28
|
+
it "overrides with a typed bucket" do
|
29
|
+
ctr = Riak::Crdt::Counter.new(typed_bucket, 'ctr')
|
30
|
+
expect(ctr.bucket_type).to eq name
|
31
|
+
end
|
32
|
+
|
33
|
+
it "overrides with a bucket type object" do
|
34
|
+
ctr = Riak::Crdt::Counter.new(bucket, 'ctr', type)
|
35
|
+
expect(ctr.bucket_type).to eq name
|
36
|
+
end
|
20
37
|
end
|
21
38
|
end
|
22
39
|
|
@@ -146,6 +163,7 @@ describe "CRDTs", integration: true, test_client: true do
|
|
146
163
|
expect(subject.include? 'coffee').to be
|
147
164
|
end
|
148
165
|
end
|
166
|
+
|
149
167
|
describe 'maps' do
|
150
168
|
subject { Riak::Crdt::Map.new bucket, random_key }
|
151
169
|
|
@@ -233,6 +251,11 @@ describe "CRDTs", integration: true, test_client: true do
|
|
233
251
|
|
234
252
|
expect(subject.registers['hkey_local_machine']).to eq 'registry'
|
235
253
|
end
|
254
|
+
|
255
|
+
it "doesn't error on an unset register" do
|
256
|
+
expect{ subject.registers['unset'] }.to_not raise_error
|
257
|
+
expect(subject.registers['other_unset']).to_not be
|
258
|
+
end
|
236
259
|
end
|
237
260
|
|
238
261
|
describe 'containing a flag' do
|
@@ -241,6 +264,11 @@ describe "CRDTs", integration: true, test_client: true do
|
|
241
264
|
|
242
265
|
expect(subject.flags['enable_magic']).to be
|
243
266
|
end
|
267
|
+
|
268
|
+
it "doesn't error on an unset flag" do
|
269
|
+
expect{ subject.flags['unset'] }.to_not raise_error
|
270
|
+
expect(subject.flags['other_unset']).to_not be
|
271
|
+
end
|
244
272
|
end
|
245
273
|
end
|
246
274
|
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'riak/bucket_properties'
|
3
|
+
|
4
|
+
describe Riak::BucketProperties, test_client: true, integration: true do
|
5
|
+
describe 'Bucket Properties objects' do
|
6
|
+
let(:bucket){ random_bucket 'props' }
|
7
|
+
subject{ described_class.new bucket }
|
8
|
+
|
9
|
+
let(:other_bucket) do
|
10
|
+
random_bucket('props-other').tap do |b|
|
11
|
+
p = described_class.new b
|
12
|
+
p['r'] = 1
|
13
|
+
p.store
|
14
|
+
end
|
15
|
+
end
|
16
|
+
let(:other_props){ described_class.new other_bucket }
|
17
|
+
|
18
|
+
before(:example) do
|
19
|
+
bucket.clear_props
|
20
|
+
subject.reload
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'is initializable with a bucket' do
|
24
|
+
expect{ described_class.new bucket }.to_not raise_error
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'works like a hash' do
|
28
|
+
expect(subject['r']).to eq 'quorum'
|
29
|
+
expect{ subject['r'] = 1 }.to_not raise_error
|
30
|
+
subject.store
|
31
|
+
subject.reload
|
32
|
+
|
33
|
+
expect(subject['r']).to eq 1
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'can be merged from a hash' do
|
37
|
+
bulk_props = { r: 1, w: 1, dw: 1 }
|
38
|
+
expect{ subject.merge! bulk_props }.to_not raise_error
|
39
|
+
subject.store
|
40
|
+
subject.reload
|
41
|
+
|
42
|
+
expect(subject['r']).to eq 1
|
43
|
+
expect(subject['w']).to eq 1
|
44
|
+
expect(subject['dw']).to eq 1
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'can be merged from a bucket properties object' do
|
48
|
+
expect(other_props['r']).to eq 1
|
49
|
+
expect(subject['r']).to eq 'quorum'
|
50
|
+
|
51
|
+
expect{ subject.merge! other_props }.to_not raise_error
|
52
|
+
subject.store
|
53
|
+
subject.reload
|
54
|
+
|
55
|
+
expect(subject['r']).to eq 1
|
56
|
+
end
|
57
|
+
|
58
|
+
let(:modfun){ { 'mod' => 'validate_json', 'fun' => 'validate' } }
|
59
|
+
|
60
|
+
it 'works with composite/modfun properties' do
|
61
|
+
expect{ subject['precommit'] = modfun }.to_not raise_error
|
62
|
+
|
63
|
+
subject.store
|
64
|
+
subject.reload
|
65
|
+
|
66
|
+
expect(subject['precommit']).to eq [modfun]
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'riak/search'
|
3
|
+
|
4
|
+
describe 'Object-oriented Search API', test_client: true, integration: true, search_config: true do
|
5
|
+
before :all do
|
6
|
+
create_index
|
7
|
+
configure_bucket
|
8
|
+
load_corpus
|
9
|
+
end
|
10
|
+
|
11
|
+
let(:term){ 'bitcask' }
|
12
|
+
|
13
|
+
describe 'queries' do
|
14
|
+
let(:index){ Riak::Search::Index.new test_client, index_name }
|
15
|
+
subject { Riak::Search::Query.new test_client, index, term }
|
16
|
+
|
17
|
+
it 'performs queries' do
|
18
|
+
results = nil
|
19
|
+
expect{ results = subject.results }.to_not raise_error
|
20
|
+
expect(results.raw).to_not be_empty
|
21
|
+
expect(results).to_not be_empty
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'performs limited and sorted queries' do
|
25
|
+
subject.rows = 5
|
26
|
+
results = nil
|
27
|
+
expect{ results = subject.results }.to_not raise_error
|
28
|
+
expect(results.raw).to_not be_empty
|
29
|
+
expect(results.length).to eq 5
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
describe 'results from queries' do
|
34
|
+
let(:index){ Riak::Search::Index.new test_client, index_name }
|
35
|
+
let(:query){ Riak::Search::Query.new test_client, index, term }
|
36
|
+
subject { query.results }
|
37
|
+
|
38
|
+
it 'exposes search-result documents' do
|
39
|
+
expect(subject).to_not be_empty
|
40
|
+
|
41
|
+
expect(subject.docs).to_not be_empty
|
42
|
+
expect(first = subject.docs.first).to be
|
43
|
+
|
44
|
+
expect(first.score).to be_a Numeric
|
45
|
+
|
46
|
+
expect(first.bucket_type).to be_a Riak::BucketType
|
47
|
+
expect(first.bucket).to be_a Riak::Bucket
|
48
|
+
expect(first.key).to be_a String
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'exposes RObjects' do
|
52
|
+
expect(subject).to_not be_empty
|
53
|
+
|
54
|
+
expect(first = subject.first).to be_a Riak::RObject
|
55
|
+
expect(first).to eq subject.docs.first.robject
|
56
|
+
|
57
|
+
expect(first.key).to eq subject.docs.first.key
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
describe 'indexes' do
|
62
|
+
it 'tests for index existence and content' do
|
63
|
+
existing_index = Riak::Search::Index.new test_client, index_name
|
64
|
+
expect(existing_index).to be_exists # auto predicate matcher
|
65
|
+
|
66
|
+
nonexistent_index = Riak::Search::Index.new(test_client, "nonexist-#{random_key}")
|
67
|
+
expect(nonexistent_index).to_not be_exists
|
68
|
+
end
|
69
|
+
|
70
|
+
it 'creates indexes' do
|
71
|
+
new_index = Riak::Search::Index.new test_client, "search-spec-#{random_key}"
|
72
|
+
expect(new_index).to_not be_exist
|
73
|
+
expect{ new_index.create! }.to_not raise_error
|
74
|
+
|
75
|
+
wait_until{ new_index.exists? }
|
76
|
+
|
77
|
+
expect(new_index).to be_exists
|
78
|
+
|
79
|
+
expect{ new_index.create! }.to raise_error Riak::SearchError::IndexExistsError
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
describe 'schemas' do
|
84
|
+
it 'tests for schema existence and content' do
|
85
|
+
existing_schema = Riak::Search::Schema.new test_client, '_yz_default'
|
86
|
+
expect(existing_schema).to be_exists
|
87
|
+
|
88
|
+
nonexistent_schema = Riak::Search::Schema.new(test_client, "nonexist-#{random_key}")
|
89
|
+
expect(nonexistent_schema).to_not be_exists
|
90
|
+
end
|
91
|
+
|
92
|
+
it 'creates schemas' do
|
93
|
+
new_schema = Riak::Search::Schema.new test_client, "search-spec-#{random_key}"
|
94
|
+
expect(new_schema).to_not be_exist
|
95
|
+
expect{ new_schema.create! schema_xml(new_schema.name) }.to_not raise_error
|
96
|
+
|
97
|
+
wait_until{ new_schema.exists? }
|
98
|
+
|
99
|
+
expect(new_schema).to be_exists
|
100
|
+
|
101
|
+
expect{ new_schema.create! schema_xml(new_schema.name) }.to raise_error Riak::SearchError::SchemaExistsError
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
@@ -51,4 +51,22 @@ describe 'Secondary indexes', test_client: true, integration: true do
|
|
51
51
|
expect(terms['20']).to be
|
52
52
|
expect(terms['19']).to be_empty
|
53
53
|
end
|
54
|
+
|
55
|
+
describe "with symbolized index names" do
|
56
|
+
it "stores and queries indexes correctly" do
|
57
|
+
obj = bucket.new random_key
|
58
|
+
obj.indexes[:coat_pattern_bin] << "tuxedo"
|
59
|
+
obj.data = "tuxedo"
|
60
|
+
|
61
|
+
expect{ obj.store }.to_not raise_error
|
62
|
+
|
63
|
+
results = nil
|
64
|
+
expect do
|
65
|
+
results = bucket.get_index(:coat_pattern_bin,
|
66
|
+
'tuxedo')
|
67
|
+
end.to_not raise_error
|
68
|
+
|
69
|
+
expect(results.first).to eq obj.key
|
70
|
+
end
|
71
|
+
end
|
54
72
|
end
|