riak-client 2.1.0 → 2.2.0.pre1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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