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.
Files changed (58) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +3 -1
  3. data/Guardfile +15 -9
  4. data/README.markdown +118 -9
  5. data/lib/riak/bucket.rb +16 -1
  6. data/lib/riak/bucket_properties.rb +67 -0
  7. data/lib/riak/bucket_type.rb +48 -0
  8. data/lib/riak/bucket_typed/bucket.rb +113 -0
  9. data/lib/riak/client/beefcake/bucket_properties_operator.rb +178 -0
  10. data/lib/riak/client/beefcake/message_overlay.rb +42 -20
  11. data/lib/riak/client/beefcake/object_methods.rb +1 -1
  12. data/lib/riak/client/beefcake/socket.rb +6 -6
  13. data/lib/riak/client/beefcake_protobuffs_backend.rb +44 -60
  14. data/lib/riak/client/protobuffs_backend.rb +8 -8
  15. data/lib/riak/client.rb +7 -2
  16. data/lib/riak/crdt/base.rb +29 -1
  17. data/lib/riak/crdt/counter.rb +7 -3
  18. data/lib/riak/crdt/inner_flag.rb +1 -1
  19. data/lib/riak/crdt/map.rb +7 -3
  20. data/lib/riak/crdt/set.rb +7 -4
  21. data/lib/riak/errors/failed_request.rb +2 -0
  22. data/lib/riak/errors/search_error.rb +29 -0
  23. data/lib/riak/locale/en.yml +7 -0
  24. data/lib/riak/map_reduce.rb +70 -6
  25. data/lib/riak/robject.rb +19 -3
  26. data/lib/riak/search/index.rb +73 -0
  27. data/lib/riak/search/query.rb +133 -0
  28. data/lib/riak/search/result_collection.rb +91 -0
  29. data/lib/riak/search/result_document.rb +59 -0
  30. data/lib/riak/search/schema.rb +65 -0
  31. data/lib/riak/search.rb +10 -0
  32. data/lib/riak/secondary_index.rb +6 -0
  33. data/lib/riak/version.rb +1 -1
  34. data/spec/fixtures/yz_schema_template.xml +18 -0
  35. data/spec/integration/riak/bucket_types_spec.rb +218 -39
  36. data/spec/integration/riak/conflict_resolution_spec.rb +96 -0
  37. data/spec/integration/riak/crdt_spec.rb +30 -2
  38. data/spec/integration/riak/properties_spec.rb +69 -0
  39. data/spec/integration/riak/search_spec.rb +104 -0
  40. data/spec/integration/riak/secondary_index_spec.rb +18 -0
  41. data/spec/riak/beefcake_protobuffs_backend/bucket_properties_operator_spec.rb +247 -0
  42. data/spec/riak/bucket_properties_spec.rb +114 -0
  43. data/spec/riak/bucket_type_spec.rb +34 -0
  44. data/spec/riak/bucket_typed/bucket.rb +43 -0
  45. data/spec/riak/client_spec.rb +16 -8
  46. data/spec/riak/crdt/counter_spec.rb +2 -1
  47. data/spec/riak/crdt/map_spec.rb +2 -1
  48. data/spec/riak/crdt/set_spec.rb +2 -1
  49. data/spec/riak/map_reduce_spec.rb +169 -124
  50. data/spec/riak/search/index_spec.rb +62 -0
  51. data/spec/riak/search/query_spec.rb +88 -0
  52. data/spec/riak/search/result_collection_spec.rb +87 -0
  53. data/spec/riak/search/result_document_spec.rb +63 -0
  54. data/spec/riak/search/schema_spec.rb +63 -0
  55. data/spec/spec_helper.rb +2 -1
  56. data/spec/support/search_config.rb +81 -0
  57. data/spec/support/test_client.yml +14 -7
  58. 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
- let(:bucket){ random_bucket 'bucket_type_spec' }
6
-
7
- describe 'performing key-value operations' do
8
- # for the sake of having a non-default one, not search
9
- let(:bucket_type){ 'yokozuna' }
10
- let(:object) do
11
- object = bucket.new random_key
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
- it 'only retrieves with a bucket type' do
19
- expect{ bucket.get object.key, type: bucket_type }.to_not raise_error
20
- expect{ bucket.get object.key }.to raise_error /not_found/
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
- it 'deletes from the bucket only with a bucket type' do
24
- expect(bucket.delete object.key).to eq true
25
- expect{ bucket.get object.key, type: bucket_type }.to_not raise_error
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
- expect{ bucket.delete object.key, type: bucket_type }.to_not raise_error
28
- expect{ bucket.get object.key, type: bucket_type }.to raise_error /not_found/
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
- it 'self-deletes only with a bucket type' do
32
- expect(object.delete).to be
33
- expect{ object.reload type: bucket_type }.to_not raise_error
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
- expect(object.delete type: bucket_type).to be
36
- expect{ object.reload type: bucket_type }.to raise_error /not_found/
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 'performing CRDT set operations' do
41
- let(:bucket_type){ Riak::Crdt::DEFAULT_BUCKET_TYPES[:set] }
42
- let(:set) do
43
- set = Riak::Crdt::Set.new bucket, random_key
44
- set.add random_key
45
- set
46
- end
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
- it 'retrieves the set blob via key-value using a bucket type' do
49
- expect{ bucket.get set.key }.to raise_error /not_found/
50
- expect(bucket.get set.key, type: bucket_type).to be
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
- it 'deletes the set blob through the bucket type' do
54
- expect(bucket.delete set.key).to be
55
- expect{ bucket.get set.key, type: bucket_type }.to_not raise_error
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
- expect(bucket.delete set.key, type: bucket_type).to be
58
- expect{ bucket.get set.key, type: bucket_type }.to raise_error /not_found/
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
- it "allows override bucket-types for instances" do
19
- expect(Riak::Crdt::Set.new(bucket, 'set', 'other_bucket_type').bucket_type).to eq 'other_bucket_type'
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