cassandra 0.11.0 → 0.11.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (67) hide show
  1. data/CHANGELOG +10 -0
  2. data/LICENSE +0 -0
  3. data/Manifest +12 -13
  4. data/README.md +344 -0
  5. data/Rakefile +52 -8
  6. data/cassandra.gemspec +5 -5
  7. data/conf/0.6/cassandra.in.sh +0 -0
  8. data/conf/0.6/log4j.properties +0 -0
  9. data/conf/0.6/schema.json +9 -0
  10. data/conf/0.6/storage-conf.xml +10 -0
  11. data/conf/0.7/cassandra.in.sh +0 -0
  12. data/conf/0.7/cassandra.yaml +0 -0
  13. data/conf/0.7/log4j-server.properties +0 -0
  14. data/conf/0.7/schema.json +9 -0
  15. data/conf/0.7/schema.txt +5 -16
  16. data/conf/0.8/cassandra.in.sh +0 -0
  17. data/conf/0.8/cassandra.yaml +1 -1
  18. data/conf/0.8/log4j-server.properties +0 -0
  19. data/conf/0.8/schema.json +19 -1
  20. data/conf/0.8/schema.txt +12 -17
  21. data/lib/cassandra.rb +3 -2
  22. data/lib/cassandra/0.6.rb +0 -0
  23. data/lib/cassandra/0.6/cassandra.rb +57 -6
  24. data/lib/cassandra/0.6/columns.rb +19 -0
  25. data/lib/cassandra/0.6/protocol.rb +2 -1
  26. data/lib/cassandra/0.7.rb +0 -0
  27. data/lib/cassandra/0.7/cassandra.rb +0 -270
  28. data/lib/cassandra/0.7/columns.rb +1 -81
  29. data/lib/cassandra/0.7/protocol.rb +0 -112
  30. data/lib/cassandra/0.8.rb +0 -0
  31. data/lib/cassandra/0.8/cassandra.rb +5 -267
  32. data/lib/cassandra/0.8/columns.rb +1 -81
  33. data/lib/cassandra/0.8/protocol.rb +9 -103
  34. data/lib/cassandra/array.rb +0 -0
  35. data/lib/cassandra/cassandra.rb +715 -92
  36. data/lib/cassandra/{0.7/column_family.rb → column_family.rb} +0 -0
  37. data/lib/cassandra/columns.rb +63 -6
  38. data/lib/cassandra/comparable.rb +0 -0
  39. data/lib/cassandra/constants.rb +0 -0
  40. data/lib/cassandra/debug.rb +0 -0
  41. data/lib/cassandra/helpers.rb +0 -0
  42. data/lib/cassandra/{0.7/keyspace.rb → keyspace.rb} +0 -0
  43. data/lib/cassandra/long.rb +0 -0
  44. data/lib/cassandra/mock.rb +45 -8
  45. data/lib/cassandra/ordered_hash.rb +0 -0
  46. data/lib/cassandra/protocol.rb +119 -0
  47. data/lib/cassandra/time.rb +0 -0
  48. data/test/cassandra_client_test.rb +0 -0
  49. data/test/cassandra_mock_test.rb +3 -0
  50. data/test/cassandra_test.rb +202 -20
  51. data/test/comparable_types_test.rb +0 -0
  52. data/test/eventmachine_test.rb +0 -0
  53. data/test/ordered_hash_test.rb +0 -0
  54. data/test/test_helper.rb +1 -1
  55. data/vendor/0.6/gen-rb/cassandra.rb +0 -0
  56. data/vendor/0.6/gen-rb/cassandra_constants.rb +0 -0
  57. data/vendor/0.6/gen-rb/cassandra_types.rb +0 -0
  58. data/vendor/0.7/gen-rb/cassandra.rb +0 -0
  59. data/vendor/0.7/gen-rb/cassandra_constants.rb +0 -0
  60. data/vendor/0.7/gen-rb/cassandra_types.rb +0 -0
  61. data/vendor/0.8/gen-rb/cassandra.rb +0 -0
  62. data/vendor/0.8/gen-rb/cassandra_constants.rb +0 -0
  63. data/vendor/0.8/gen-rb/cassandra_types.rb +0 -0
  64. metadata +27 -29
  65. data/README.rdoc +0 -99
  66. data/lib/cassandra/0.8/column_family.rb +0 -3
  67. data/lib/cassandra/0.8/keyspace.rb +0 -3
@@ -1,84 +1,4 @@
1
-
2
1
  class Cassandra
3
- # A bunch of crap, mostly related to introspecting on column types
4
2
  module Columns #:nodoc:
5
-
6
- def is_super(column_family)
7
- @is_super[column_family] ||= column_family_property(column_family, 'column_type') == "Super"
8
- end
9
-
10
- def column_name_class(column_family)
11
- @column_name_class[column_family] ||= column_name_class_for_key(column_family, "comparator_type")
12
- end
13
-
14
- def sub_column_name_class(column_family)
15
- @sub_column_name_class[column_family] ||= column_name_class_for_key(column_family, "subcomparator_type")
16
- end
17
-
18
- def column_family_property(column_family, key)
19
- cfdef = schema.cf_defs.find {|cfdef| cfdef.name == column_family }
20
- unless cfdef
21
- raise AccessError, "Invalid column family \"#{column_family}\""
22
- end
23
- cfdef.send(key)
24
- end
25
-
26
- private
27
-
28
- def _standard_insert_mutation(column_family, column_name, value, timestamp, ttl = nil)
29
- CassandraThrift::Mutation.new(
30
- :column_or_supercolumn => CassandraThrift::ColumnOrSuperColumn.new(
31
- :column => CassandraThrift::Column.new(
32
- :name => column_name_class(column_family).new(column_name).to_s,
33
- :value => value,
34
- :timestamp => timestamp,
35
- :ttl => ttl
36
- )
37
- )
38
- )
39
- end
40
-
41
- def _super_insert_mutation(column_family, super_column_name, sub_columns, timestamp, ttl = nil)
42
- CassandraThrift::Mutation.new(:column_or_supercolumn =>
43
- CassandraThrift::ColumnOrSuperColumn.new(
44
- :super_column => CassandraThrift::SuperColumn.new(
45
- :name => column_name_class(column_family).new(super_column_name).to_s,
46
- :columns => sub_columns.collect { |sub_column_name, sub_column_value|
47
- CassandraThrift::Column.new(
48
- :name => sub_column_name_class(column_family).new(sub_column_name).to_s,
49
- :value => sub_column_value.to_s,
50
- :timestamp => timestamp,
51
- :ttl => ttl
52
- )
53
- }
54
- )
55
- )
56
- )
57
- end
58
-
59
- # General info about a deletion object within a mutation
60
- # timestamp - required. If this is the only param, it will cause deletion of the whole key at that TS
61
- # supercolumn - opt. If passed, the deletes will only occur within that supercolumn (only subcolumns
62
- # will be deleted). Otherwise the normal columns will be deleted.
63
- # predicate - opt. Defines how to match the columns to delete. if supercolumn passed, the slice will
64
- # be scoped to subcolumns of that supercolumn.
65
-
66
- # Deletes a single column from the containing key/CF (and possibly supercolumn), at a given timestamp.
67
- # Although mutations (as opposed to 'remove' calls) support deleting slices and lists of columns in one shot, this is not implemented here.
68
- # The main reason being that the batch function takes removes, but removes don't have that capability...so we'd need to change the remove
69
- # methods to use delete mutation calls...although that might have performance implications. We'll leave that refactoring for later.
70
- def _delete_mutation(cf, column, subcolumn, timestamp, options={})
71
- deletion_hash = {:timestamp => timestamp}
72
- if is_super(cf)
73
- deletion_hash[:super_column] = column if column
74
- deletion_hash[:predicate] = CassandraThrift::SlicePredicate.new(:column_names => [subcolumn]) if subcolumn
75
- else
76
- deletion_hash[:predicate] = CassandraThrift::SlicePredicate.new(:column_names => [column]) if column
77
- end
78
- CassandraThrift::Mutation.new(
79
- :deletion => CassandraThrift::Deletion.new(deletion_hash)
80
- )
81
- end
82
-
83
3
  end
84
- end
4
+ end
@@ -1,117 +1,23 @@
1
+ require "#{File.expand_path(File.dirname(__FILE__))}/../0.7/protocol"
1
2
 
2
3
  class Cassandra
3
4
  # Inner methods for actually doing the Thrift calls
4
5
  module Protocol #:nodoc:
5
6
  private
6
7
 
7
- def _mutate(mutation_map, consistency_level)
8
- client.batch_mutate(mutation_map, consistency_level)
8
+ def _remove_counter(key, column_path, consistency_level)
9
+ client.remove_counter(key, column_path, consistency_level)
9
10
  end
10
11
 
11
- def _remove(key, column_path, timestamp, consistency_level)
12
- client.remove(key, column_path, timestamp, consistency_level)
13
- end
14
-
15
- def _count_columns(column_family, key, super_column, consistency)
16
- client.get_count(key,
17
- CassandraThrift::ColumnParent.new(:column_family => column_family, :super_column => super_column),
18
- CassandraThrift::SlicePredicate.new(:slice_range =>
19
- CassandraThrift::SliceRange.new(
20
- :start => '',
21
- :finish => ''
22
- )),
23
- consistency
24
- )
25
- end
26
-
27
- def _get_columns(column_family, key, columns, sub_columns, consistency)
28
- result = if is_super(column_family)
29
- if sub_columns
30
- columns_to_hash(column_family, client.get_slice(key,
31
- CassandraThrift::ColumnParent.new(:column_family => column_family, :super_column => columns),
32
- CassandraThrift::SlicePredicate.new(:column_names => sub_columns),
33
- consistency))
34
- else
35
- columns_to_hash(column_family, client.get_slice(key,
36
- CassandraThrift::ColumnParent.new(:column_family => column_family),
37
- CassandraThrift::SlicePredicate.new(:column_names => columns),
38
- consistency))
39
- end
40
- else
41
- columns_to_hash(column_family, client.get_slice(key,
42
- CassandraThrift::ColumnParent.new(:column_family => column_family),
43
- CassandraThrift::SlicePredicate.new(:column_names => columns),
44
- consistency))
45
- end
46
-
47
- klass = column_name_class(column_family)
48
- (sub_columns || columns).map { |name| result[klass.new(name)] }
49
- end
50
-
51
- def _multiget(column_family, keys, column, sub_column, count, start, finish, reversed, consistency)
52
- # Single values; count and range parameters have no effect
53
- if is_super(column_family) and sub_column
54
- predicate = CassandraThrift::SlicePredicate.new(:column_names => [sub_column])
12
+ def _add(column_family, key, column, sub_column, value, consistency)
13
+ if is_super(column_family)
55
14
  column_parent = CassandraThrift::ColumnParent.new(:column_family => column_family, :super_column => column)
56
- column_hash = multi_sub_columns_to_hash!(column_family, client.multiget_slice(keys, column_parent, predicate, consistency))
57
-
58
- klass = sub_column_name_class(column_family)
59
- keys.inject({}){|hash, key| hash[key] = column_hash[key][klass.new(sub_column)]; hash}
60
- elsif !is_super(column_family) and column
61
- predicate = CassandraThrift::SlicePredicate.new(:column_names => [column])
62
- column_parent = CassandraThrift::ColumnParent.new(:column_family => column_family)
63
- column_hash = multi_columns_to_hash!(column_family, client.multiget_slice(keys, column_parent, predicate, consistency))
64
-
65
- keys.inject({}){|hash, key| hash[key] = column_hash[key][column]; hash}
66
-
67
- # Slices
68
- else
69
- predicate = CassandraThrift::SlicePredicate.new(:slice_range =>
70
- CassandraThrift::SliceRange.new(
71
- :reversed => reversed,
72
- :count => count,
73
- :start => start,
74
- :finish => finish))
75
-
76
- if is_super(column_family) and column
77
- column_parent = CassandraThrift::ColumnParent.new(:column_family => column_family, :super_column => column)
78
- multi_sub_columns_to_hash!(column_family, client.multiget_slice(keys, column_parent, predicate, consistency))
79
- else
80
- column_parent = CassandraThrift::ColumnParent.new(:column_family => column_family)
81
- multi_columns_to_hash!(column_family, client.multiget_slice(keys, column_parent, predicate, consistency))
82
- end
83
- end
84
- end
85
-
86
- def _get_range(column_family, start_key, finish_key, key_count, columns, start, finish, count, consistency)
87
- column_parent = CassandraThrift::ColumnParent.new(:column_family => column_family)
88
- predicate = if columns
89
- CassandraThrift::SlicePredicate.new(:column_names => columns)
90
- else
91
- CassandraThrift::SlicePredicate.new(:slice_range =>
92
- CassandraThrift::SliceRange.new(
93
- :start => start,
94
- :finish => finish,
95
- :count => count))
96
- end
97
- range = CassandraThrift::KeyRange.new(:start_key => start_key, :end_key => finish_key, :count => key_count)
98
- client.get_range_slices(column_parent, predicate, range, consistency)
99
- end
100
-
101
- # TODO: Supercolumn support
102
- def _get_indexed_slices(column_family, idx_clause, column, count, start, finish, reversed, consistency)
103
- column_parent = CassandraThrift::ColumnParent.new(:column_family => column_family)
104
- if column
105
- predicate = CassandraThrift::SlicePredicate.new(:column_names => [column])
15
+ counter_column = CassandraThrift::CounterColumn.new(:name => sub_column, :value => value)
106
16
  else
107
- predicate = CassandraThrift::SlicePredicate.new(:slice_range =>
108
- CassandraThrift::SliceRange.new(
109
- :reversed => reversed,
110
- :count => count,
111
- :start => start,
112
- :finish => finish))
17
+ column_parent = CassandraThrift::ColumnParent.new(:column_family => column_family)
18
+ counter_column = CassandraThrift::CounterColumn.new(:name => column, :value => value)
113
19
  end
114
- client.get_indexed_slices(column_parent, idx_clause, predicate, consistency)
20
+ client.add(key, column_parent, counter_column, consistency)
115
21
  end
116
22
  end
117
23
  end
File without changes
@@ -49,7 +49,7 @@ class Cassandra
49
49
  :timestamp => nil,
50
50
  :consistency => Consistency::ONE,
51
51
  :ttl => nil
52
- }.freeze
52
+ }
53
53
 
54
54
  READ_DEFAULTS = {
55
55
  :count => 100,
@@ -57,15 +57,19 @@ class Cassandra
57
57
  :finish => nil,
58
58
  :reversed => false,
59
59
  :consistency => Consistency::ONE
60
- }.freeze
61
-
60
+ }
61
+
62
62
  THRIFT_DEFAULTS = {
63
63
  :transport_wrapper => Thrift::BufferedTransport,
64
64
  :thrift_client_class => ThriftClient
65
- }.freeze
65
+ }
66
66
 
67
67
  attr_reader :keyspace, :servers, :schema, :thrift_client_options, :thrift_client_class, :auth_request
68
68
 
69
+ def self.DEFAULT_TRANSPORT_WRAPPER
70
+ Thrift::FramedTransport
71
+ end
72
+
69
73
  # Create a new Cassandra instance and open the connection.
70
74
  def initialize(keyspace, servers = "127.0.0.1:9160", thrift_client_options = {})
71
75
  @is_super = {}
@@ -79,10 +83,25 @@ class Cassandra
79
83
  @servers = Array(servers)
80
84
  end
81
85
 
86
+ ##
87
+ # This method will prevent us from trying to auto-discover all the
88
+ # server addresses, and only use the list of servers provided on
89
+ # initialization.
90
+
91
+ # This is primarily helpful when the cassandra cluster is communicating
92
+ # internally on a different ip address than what you are using to connect.
93
+ # A prime example of this would be when using EC2 to host a cluster.
94
+ # Typically, the cluster would be communicating over the local ip
95
+ # addresses issued by Amazon, but any clients connecting from outside EC2
96
+ # would need to use the public ip.
97
+ #
82
98
  def disable_node_auto_discovery!
83
99
  @auto_discover_nodes = false
84
100
  end
85
101
 
102
+ ##
103
+ # Disconnect the current client connection.
104
+ #
86
105
  def disconnect!
87
106
  if @client
88
107
  @client.disconnect!
@@ -90,27 +109,320 @@ class Cassandra
90
109
  end
91
110
  end
92
111
 
93
- def keyspaces
94
- @keyspaces ||= client.describe_keyspaces()
95
- end
96
-
112
+ ##
113
+ # Issues a login attempt using the username and password specified.
114
+ #
115
+ # * username
116
+ # * password
117
+ #
97
118
  def login!(username, password)
98
119
  @auth_request = CassandraThrift::AuthenticationRequest.new
99
120
  @auth_request.credentials = {'username' => username, 'password' => password}
100
- client.login(@keyspace, @auth_request)
121
+ client.login(@auth_request)
101
122
  end
102
-
123
+
103
124
  def inspect
104
125
  "#<Cassandra:#{object_id}, @keyspace=#{keyspace.inspect}, @schema={#{
105
- schema(false).map {|name, hash| ":#{name} => #{hash['type'].inspect}"}.join(', ')
126
+ Array(schema(false).cf_defs).map {|cfdef| ":#{cfdef.name} => #{cfdef.column_type}"}.join(', ')
106
127
  }}, @servers=#{servers.inspect}>"
107
128
  end
108
129
 
109
- ### Write
130
+ ##
131
+ # Set the keyspace to use.
132
+ #
133
+ # Please note that this only works on version 0.7.0 and higher.
134
+ def keyspace=(ks)
135
+ return false if Cassandra.VERSION.to_f < 0.7
136
+
137
+ client.set_keyspace(ks)
138
+ @schema = nil; @keyspace = ks
139
+ end
140
+
141
+ ##
142
+ # Return an array of the keyspace names available.
143
+ #
144
+ # Please note that this only works on version 0.7.0 and higher.
145
+ def keyspaces
146
+ return false if Cassandra.VERSION.to_f < 0.7
147
+
148
+ client.describe_keyspaces.to_a.collect {|ksdef| ksdef.name }
149
+ end
150
+
151
+ ##
152
+ # Return a Cassandra::Keyspace object loaded with the current
153
+ # keyspaces schema.
154
+ #
155
+ # Please note that this only works on version 0.7.0 and higher.
156
+ def schema(load=true)
157
+ return false if Cassandra.VERSION.to_f < 0.7
158
+
159
+ if !load && !@schema
160
+ Cassandra::Keyspace.new
161
+ else
162
+ @schema ||= client.describe_keyspace(@keyspace)
163
+ end
164
+ end
165
+
166
+ ##
167
+ # This returns true if all servers are in agreement on the schema.
168
+ #
169
+ # Please note that this only works on version 0.7.0 and higher.
170
+ def schema_agreement?
171
+ return false if Cassandra.VERSION.to_f < 0.7
172
+
173
+ client.describe_schema_versions().length == 1
174
+ end
175
+
176
+ ##
177
+ # Lists the current cassandra.thrift version.
178
+ #
179
+ # Please note that this only works on version 0.7.0 and higher.
180
+ def version
181
+ return false if Cassandra.VERSION.to_f < 0.7
182
+
183
+ client.describe_version()
184
+ end
185
+
186
+ ##
187
+ # Returns the string name specified for the cluster.
188
+ #
189
+ # Please note that this only works on version 0.7.0 and higher.
190
+ def cluster_name
191
+ return false if Cassandra.VERSION.to_f < 0.7
192
+
193
+ @cluster_name ||= client.describe_cluster_name()
194
+ end
195
+
196
+ ##
197
+ # Returns an array of CassandraThrift::TokenRange objects indicating
198
+ # which servers make up the current ring. What their start and end
199
+ # tokens are, and their list of endpoints.
200
+ #
201
+ # Please note that this only works on version 0.7.0 and higher.
202
+ def ring
203
+ return false if Cassandra.VERSION.to_f < 0.7
204
+
205
+ client.describe_ring(@keyspace)
206
+ end
207
+
208
+ ##
209
+ # Returns a string identifying which partitioner is in use by the
210
+ # current cluster. Typically, this will be RandomPartitioner, but it
211
+ # could be OrderPreservingPartioner as well.
212
+ #
213
+ # Please note that this only works on version 0.7.0 and higher.
214
+ def partitioner
215
+ return false if Cassandra.VERSION.to_f < 0.7
216
+
217
+ client.describe_partitioner()
218
+ end
219
+
220
+ ##
221
+ # Remove all rows in the column family you request.
222
+ #
223
+ # * column_family
224
+ # * options
225
+ # * consitency
226
+ # * timestamp
227
+ #
228
+ def truncate!(column_family)
229
+ client.truncate(column_family.to_s)
230
+ end
231
+ alias clear_column_family! truncate!
232
+
233
+ ##
234
+ # Remove all column families in the keyspace.
235
+ #
236
+ # This method calls Cassandra#truncate! for each column family in the
237
+ # keyspace.
238
+ #
239
+ # Please note that this only works on version 0.7.0 and higher.
240
+ #
241
+ def clear_keyspace!
242
+ return false if Cassandra.VERSION.to_f < 0.7
243
+
244
+ schema.cf_defs.each { |cfdef| truncate!(cfdef.name) }
245
+ end
246
+
247
+ ##
248
+ # Creates a new column family from the passed in
249
+ # Cassandra::ColumnFamily instance, and returns the schema id.
250
+ #
251
+ def add_column_family(cf_def)
252
+ return false if Cassandra.VERSION.to_f < 0.7
253
+
254
+ begin
255
+ res = client.system_add_column_family(cf_def)
256
+ rescue CassandraThrift::TimedOutException => te
257
+ puts "Timed out: #{te.inspect}"
258
+ end
259
+ @schema = nil
260
+ res
261
+ end
262
+
263
+ ##
264
+ # Delete the specified column family. Return the new schema id.
265
+ #
266
+ # * column_family - The column_family name to drop.
267
+ #
268
+ def drop_column_family(column_family)
269
+ return false if Cassandra.VERSION.to_f < 0.7
270
+
271
+ begin
272
+ res = client.system_drop_column_family(column_family)
273
+ rescue CassandraThrift::TimedOutException => te
274
+ puts "Timed out: #{te.inspect}"
275
+ end
276
+ @schema = nil
277
+ res
278
+ end
279
+
280
+ ##
281
+ # Rename a column family. Returns the new schema id.
282
+ #
283
+ # * old_name - The current column_family name.
284
+ # * new_name - The desired column_family name.
285
+ #
286
+ def rename_column_family(old_name, new_name)
287
+ return false if Cassandra.VERSION.to_f < 0.7
288
+
289
+ begin
290
+ res = client.system_rename_column_family(old_name, new_name)
291
+ rescue CassandraThrift::TimedOutException => te
292
+ puts "Timed out: #{te.inspect}"
293
+ end
294
+ @schema = nil
295
+ res
296
+ end
297
+
298
+ ##
299
+ # Update the column family based on the passed in definition.
300
+ #
301
+ def update_column_family(cf_def)
302
+ return false if Cassandra.VERSION.to_f < 0.7
303
+
304
+ begin
305
+ res = client.system_update_column_family(cf_def)
306
+ rescue CassandraThrift::TimedOutException => te
307
+ puts "Timed out: #{te.inspect}"
308
+ end
309
+ @schema = nil
310
+ res
311
+ end
312
+
313
+ ##
314
+ # Add keyspace using the passed in keyspace definition.
315
+ #
316
+ # Returns the new schema id.
317
+ #
318
+ def add_keyspace(ks_def)
319
+ return false if Cassandra.VERSION.to_f < 0.7
320
+
321
+ begin
322
+ res = client.system_add_keyspace(ks_def)
323
+ rescue CassandraThrift::TimedOutException => toe
324
+ puts "Timed out: #{toe.inspect}"
325
+ rescue Thrift::TransportException => te
326
+ puts "Timed out: #{te.inspect}"
327
+ end
328
+ @keyspaces = nil
329
+ res
330
+ end
331
+
332
+ ##
333
+ # Deletes keyspace using the passed in keyspace name.
334
+ #
335
+ # Returns the new schema id.
336
+ #
337
+ def drop_keyspace(keyspace)
338
+ return false if Cassandra.VERSION.to_f < 0.7
110
339
 
111
- # Insert a row for a key. Pass a flat hash for a regular column family, and
112
- # a nested hash for a super column family. Supports the <tt>:consistency</tt>,
113
- # <tt>:timestamp</tt> and <tt>:ttl</tt> options.
340
+ begin
341
+ res = client.system_drop_keyspace(keyspace)
342
+ rescue CassandraThrift::TimedOutException => toe
343
+ puts "Timed out: #{toe.inspect}"
344
+ rescue Thrift::TransportException => te
345
+ puts "Timed out: #{te.inspect}"
346
+ end
347
+ keyspace = "system" if keyspace.eql?(@keyspace)
348
+ @keyspaces = nil
349
+ res
350
+ end
351
+
352
+ ##
353
+ # Renames keyspace.
354
+ #
355
+ # * old_name - Current keyspace name.
356
+ # * new_name - Desired keyspace name.
357
+ #
358
+ # Returns the new schema id
359
+ def rename_keyspace(old_name, new_name)
360
+ return false if Cassandra.VERSION.to_f < 0.7
361
+
362
+ begin
363
+ res = client.system_rename_keyspace(old_name, new_name)
364
+ rescue CassandraThrift::TimedOutException => toe
365
+ puts "Timed out: #{toe.inspect}"
366
+ rescue Thrift::TransportException => te
367
+ puts "Timed out: #{te.inspect}"
368
+ end
369
+ keyspace = new_name if old_name.eql?(@keyspace)
370
+ @keyspaces = nil
371
+ res
372
+ end
373
+
374
+ ##
375
+ # Update the keyspace using the passed in keyspace definition.
376
+ #
377
+ def update_keyspace(ks_def)
378
+ return false if Cassandra.VERSION.to_f < 0.7
379
+
380
+ begin
381
+ res = client.system_update_keyspace(ks_def)
382
+ rescue CassandraThrift::TimedOutException => toe
383
+ puts "Timed out: #{toe.inspect}"
384
+ rescue Thrift::TransportException => te
385
+ puts "Timed out: #{te.inspect}"
386
+ end
387
+ @keyspaces = nil
388
+ res
389
+ end
390
+ ##
391
+ # The initial default consistency is set to ONE, but you can use this method
392
+ # to override the normal default with your specified value. Use this if you
393
+ # do not want to specify a write consistency for each insert statement.
394
+ #
395
+ def default_write_consistency=(value)
396
+ WRITE_DEFAULTS[:consistency] = value
397
+ end
398
+
399
+ ##
400
+ # The initial default consistency is set to ONE, but you can use this method
401
+ # to override the normal default with your specified value. Use this if you
402
+ # do not want to specify a read consistency for each query.
403
+ #
404
+ def default_read_consistency=(value)
405
+ READ_DEFAULTS[:consistency] = value
406
+ end
407
+
408
+ ##
409
+ # This is the main method used to insert rows into cassandra. If the
410
+ # column\_family that you are inserting into is a SuperColumnFamily then
411
+ # the hash passed in should be a nested hash, otherwise it should be a
412
+ # flat hash.
413
+ #
414
+ # This method can also be called while in batch mode. If in batch mode
415
+ # then we queue up the mutations (an insert in this case) and pass them to
416
+ # cassandra in a single batch at the end of the block.
417
+ #
418
+ # * column\_family - The column\_family that you are inserting into.
419
+ # * key - The row key to insert.
420
+ # * hash - The columns or super columns to insert.
421
+ # * options - Valid options are:
422
+ # * :timestamp - Uses the current time if none specified.
423
+ # * :consistency - Uses the default write consistency if none specified.
424
+ # * :ttl - If specified this is the number of seconds after the insert that this value will be available.
425
+ #
114
426
  def insert(column_family, key, hash, options = {})
115
427
  column_family, _, _, options = extract_and_validate_params(column_family, key, [options], WRITE_DEFAULTS)
116
428
 
@@ -133,12 +445,23 @@ class Cassandra
133
445
  end
134
446
 
135
447
 
136
- ## Delete
137
-
138
- # _mutate the element at the column_family:key:[column]:[sub_column]
139
- # path you request. Supports the <tt>:consistency</tt> and <tt>:timestamp</tt>
140
- # options.
448
+ ##
449
+ # This method is used to delete (actually marking them as deleted with a
450
+ # tombstone) columns or super columns.
451
+ #
452
+ # This method can also be used in batch mode. If in batch mode then we
453
+ # queue up the mutations (a deletion in this case)
454
+ #
455
+ # * column\_family - The column\_family that you are inserting into.
456
+ # * key - The row key to insert.
457
+ # * columns - Either a single super_column or a list of columns.
458
+ # * sub_columns - The list of sub\_columns to select.
459
+ # * options - Valid options are:
460
+ # * :timestamp - Uses the current time if none specified.
461
+ # * :consistency - Uses the default write consistency if none specified.
462
+ #
141
463
  # TODO: we could change this function or add another that support multi-column removal (by list or predicate)
464
+ #
142
465
  def remove(column_family, key, *columns_and_options)
143
466
  column_family, column, sub_column, options = extract_and_validate_params(column_family, key, columns_and_options, WRITE_DEFAULTS)
144
467
 
@@ -160,83 +483,197 @@ class Cassandra
160
483
  end
161
484
  end
162
485
 
163
- ### Read
164
-
165
- # Count the elements at the column_family:key:[super_column] path you
166
- # request. Supports the <tt>:consistency</tt> option.
486
+ ##
487
+ # Count the columns for the provided parameters.
488
+ #
489
+ # * column_family - The column_family that you are inserting into.
490
+ # * key - The row key to insert.
491
+ # * columns - Either a single super_column or a list of columns.
492
+ # * sub_columns - The list of sub_columns to select.
493
+ # * options - Valid options are:
494
+ # * :consistency - Uses the default read consistency if none specified.
495
+ #
167
496
  def count_columns(column_family, key, *columns_and_options)
168
497
  column_family, super_column, _, options =
169
498
  extract_and_validate_params(column_family, key, columns_and_options, READ_DEFAULTS)
170
499
  _count_columns(column_family, key, super_column, options[:consistency])
171
500
  end
172
501
 
173
- # Multi-key version of Cassandra#count_columns. Supports options <tt>:count</tt>,
174
- # <tt>:start</tt>, <tt>:finish</tt>, <tt>:reversed</tt>, and <tt>:consistency</tt>.
175
- # FIXME Not real multi; needs server support
502
+ ##
503
+ # Multi-key version of Cassandra#count_columns. Please note that this
504
+ # queries the server for each key passed in.
505
+ #
506
+ # Supports same parameters as Cassandra#count_columns.
507
+ #
508
+ # * column_family - The column_family that you are inserting into.
509
+ # * key - The row key to insert.
510
+ # * columns - Either a single super_column or a list of columns.
511
+ # * sub_columns - The list of sub_columns to select.
512
+ # * options - Valid options are:
513
+ # * :consistency - Uses the default read consistency if none specified.
514
+ #
515
+ # FIXME: Not real multi; needs server support
176
516
  def multi_count_columns(column_family, keys, *options)
177
517
  OrderedHash[*keys.map { |key| [key, count_columns(column_family, key, *options)] }._flatten_once]
178
518
  end
179
519
 
180
- # Return a list of single values for the elements at the
181
- # column_family:key:column[s]:[sub_columns] path you request. Supports the
182
- # <tt>:consistency</tt> option.
520
+ ##
521
+ # Return a hash of column value pairs for the path you request.
522
+ #
523
+ # * column_family - The column_family that you are inserting into.
524
+ # * key - The row key to insert.
525
+ # * columns - Either a single super_column or a list of columns.
526
+ # * sub_columns - The list of sub_columns to select.
527
+ # * options - Valid options are:
528
+ # * :consistency - Uses the default read consistency if none specified.
529
+ #
183
530
  def get_columns(column_family, key, *columns_and_options)
184
531
  column_family, columns, sub_columns, options =
185
532
  extract_and_validate_params(column_family, key, columns_and_options, READ_DEFAULTS)
186
533
  _get_columns(column_family, key, columns, sub_columns, options[:consistency])
187
534
  end
188
535
 
189
- # Multi-key version of Cassandra#get_columns. Supports the <tt>:consistency</tt>
190
- # option.
536
+ ##
537
+ # Multi-key version of Cassandra#get_columns. Please note that this
538
+ # queries the server for each key passed in.
539
+ #
540
+ # Supports same parameters as Cassandra#get_columns
541
+ #
542
+ # * column_family - The column_family that you are inserting into.
543
+ # * key - The row key to insert.
544
+ # * columns - Either a single super_column or a list of columns.
545
+ # * sub_columns - The list of sub_columns to select.
546
+ # * options - Valid options are:
547
+ # * :consistency - Uses the default read consistency if none specified.
548
+ #
191
549
  # FIXME Not real multi; needs to use a Column predicate
192
550
  def multi_get_columns(column_family, keys, *options)
193
551
  OrderedHash[*keys.map { |key| [key, get_columns(column_family, key, *options)] }._flatten_once]
194
552
  end
195
553
 
554
+ ##
196
555
  # Return a hash (actually, a Cassandra::OrderedHash) or a single value
197
556
  # representing the element at the column_family:key:[column]:[sub_column]
198
- # path you request. Supports options <tt>:count</tt>, <tt>:start</tt>,
199
- # <tt>:finish</tt>, <tt>:reversed</tt>, and <tt>:consistency</tt>.
557
+ # path you request.
558
+ #
559
+ # * column_family - The column_family that you are inserting into.
560
+ # * key - The row key to insert.
561
+ # * columns - Either a single super_column or a list of columns.
562
+ # * sub_columns - The list of sub_columns to select.
563
+ # * options - Valid options are:
564
+ # * :count - The number of columns requested to be returned.
565
+ # * :start - The starting value for selecting a range of columns.
566
+ # * :finish - The final value for selecting a range of columns.
567
+ # * :reversed - If set to true the results will be returned in
568
+ # reverse order.
569
+ # * :consistency - Uses the default read consistency if none specified.
570
+ #
200
571
  def get(column_family, key, *columns_and_options)
201
572
  multi_get(column_family, [key], *columns_and_options)[key]
202
573
  end
203
574
 
204
- # Multi-key version of Cassandra#get. Supports options <tt>:count</tt>,
205
- # <tt>:start</tt>, <tt>:finish</tt>, <tt>:reversed</tt>, and <tt>:consistency</tt>.
575
+ ##
576
+ # Multi-key version of Cassandra#get.
577
+ #
578
+ # This method allows you to select multiple rows with a single query.
579
+ # If a key that is passed in doesn't exist an empty hash will be
580
+ # returned.
581
+ #
582
+ # Supports the same parameters as Cassandra#get.
583
+ #
584
+ # * column_family - The column_family that you are inserting into.
585
+ # * key - An array of keys to.
586
+ # * columns - Either a single super_column or a list of columns.
587
+ # * sub_columns - The list of sub_columns to select.
588
+ # * options - Valid options are:
589
+ # * :count - The number of columns requested to be returned.
590
+ # * :start - The starting value for selecting a range of columns.
591
+ # * :finish - The final value for selecting a range of columns.
592
+ # * :reversed - If set to true the results will be returned in reverse order.
593
+ # * :consistency - Uses the default read consistency if none specified.
594
+ #
206
595
  def multi_get(column_family, keys, *columns_and_options)
207
596
  column_family, column, sub_column, options =
208
597
  extract_and_validate_params(column_family, keys, columns_and_options, READ_DEFAULTS)
209
598
 
210
599
  hash = _multiget(column_family, keys, column, sub_column, options[:count], options[:start], options[:finish], options[:reversed], options[:consistency])
600
+
211
601
  # Restore order
212
602
  ordered_hash = OrderedHash.new
213
603
  keys.each { |key| ordered_hash[key] = hash[key] || (OrderedHash.new if is_super(column_family) and !sub_column) }
214
604
  ordered_hash
215
605
  end
216
606
 
607
+ ##
217
608
  # Return true if the column_family:key:[column]:[sub_column] path you
218
- # request exists. Supports the <tt>:consistency</tt> option.
609
+ # request exists.
610
+ #
611
+ # If passed in only a row key it will query for any columns (limiting
612
+ # to 1) for that row key. If a column is passed in it will query for
613
+ # that specific column/super column.
614
+ #
615
+ # This method will return true or false.
616
+ #
617
+ # * column_family - The column_family that you are inserting into.
618
+ # * key - The row key to insert.
619
+ # * columns - Either a single super_column or a list of columns.
620
+ # * sub_columns - The list of sub_columns to select.
621
+ # * options - Valid options are:
622
+ # * :consistency - Uses the default read consistency if none specified.
623
+ #
219
624
  def exists?(column_family, key, *columns_and_options)
220
625
  column_family, column, sub_column, options =
221
626
  extract_and_validate_params(column_family, key, columns_and_options, READ_DEFAULTS)
222
- if column
223
- _multiget(column_family, [key], column, sub_column, 1, nil, nil, nil, options[:consistency])[key]
224
- else
225
- _multiget(column_family, [key], nil, nil, 1, '', '', false, options[:consistency])[key]
226
- end
627
+ result = if column
628
+ _multiget(column_family, [key], column, sub_column, 1, '', '', false, options[:consistency])[key]
629
+ else
630
+ _multiget(column_family, [key], nil, nil, 1, '', '', false, options[:consistency])[key]
631
+ end
632
+
633
+ ![{}, nil].include?(result)
227
634
  end
228
635
 
229
- # Return an OrderedHash containing the columns specified for the given
230
- # range of keys in the column_family you request. Only works well if
231
- # the table is partitioned with OrderPreservingPartitioner. Supports the
232
- # <tt>:key_count</tt>, <tt>:start_key</tt>, <tt>:finish_key</tt>,
233
- # <tt>:columns</tt>, <tt>:start</tt>, <tt>:finish</tt>, <tt>:count</tt>,
234
- # and <tt>:consistency</tt> options. Please note that Cassandra returns
235
- # a row for each row that has existed in the system since gc_grace_seconds.
236
- # This is because deleted row keys are marked as deleted, but left in the
237
- # system until the cluster has had resonable time to replicate the deletion.
636
+ ##
637
+ # Return an Cassandra::OrderedHash containing the columns specified for the given
638
+ # range of keys in the column_family you request.
639
+ #
640
+ # This method is just a convenience wrapper around Cassandra#get_range_single
641
+ # and Cassandra#get_range_batch. If :key_size, :batch_size, or a block
642
+ # is passed in Cassandra#get_range_batch will be called. Otherwise
643
+ # Cassandra#get_range_single will be used.
644
+ #
645
+ # The start_key and finish_key parameters are only useful for iterating of all records
646
+ # as is done in the Cassandra#each and Cassandra#each_key methods if you are using the
647
+ # RandomPartitioner.
648
+ #
649
+ # If the table is partitioned with OrderPreservingPartitioner you may
650
+ # use the start_key and finish_key params to select all records with
651
+ # the same prefix value.
652
+ #
653
+ # If a block is passed in we will yield the row key and columns for
654
+ # each record returned.
655
+ #
656
+ # Please note that Cassandra returns a row for each row that has existed in the
657
+ # system since gc_grace_seconds. This is because deleted row keys are marked as
658
+ # deleted, but left in the system until the cluster has had resonable time to replicate the deletion.
238
659
  # This function attempts to suppress deleted rows (actually any row returned without
239
660
  # columns is suppressed).
661
+ #
662
+ # * column_family - The column_family that you are inserting into.
663
+ # * key - The row key to insert.
664
+ # * columns - Either a single super_column or a list of columns.
665
+ # * sub_columns - The list of sub_columns to select.
666
+ # * options - Valid options are:
667
+ # * :start_key - The starting value for selecting a range of keys (only useful with OPP).
668
+ # * :finish_key - The final value for selecting a range of keys (only useful with OPP).
669
+ # * :key_count - The total number of keys to return from the query. (see note regarding deleted records)
670
+ # * :batch_size - The maximum number of keys to return per query. If specified will loop until :key_count is obtained or all records have been returned.
671
+ # * :count - The number of columns requested to be returned.
672
+ # * :start - The starting value for selecting a range of columns.
673
+ # * :finish - The final value for selecting a range of columns.
674
+ # * :reversed - If set to true the results will be returned in reverse order.
675
+ # * :consistency - Uses the default read consistency if none specified.
676
+ #
240
677
  def get_range(column_family, options = {})
241
678
  if block_given? || options[:key_count] || options[:batch_size]
242
679
  get_range_batch(column_family, options)
@@ -245,6 +682,12 @@ class Cassandra
245
682
  end
246
683
  end
247
684
 
685
+ ##
686
+ # Return an Cassandra::OrderedHash containing the columns specified for the given
687
+ # range of keys in the column_family you request.
688
+ #
689
+ # See Cassandra#get_range for more details.
690
+ #
248
691
  def get_range_single(column_family, options = {})
249
692
  return_empty_rows = options.delete(:return_empty_rows) || false
250
693
 
@@ -270,6 +713,15 @@ class Cassandra
270
713
  multi_key_slices_to_hash(column_family, results, return_empty_rows)
271
714
  end
272
715
 
716
+ ##
717
+ # Return an Cassandra::OrderedHash containing the columns specified for the given
718
+ # range of keys in the column_family you request.
719
+ #
720
+ # If a block is passed in we will yield the row key and columns for
721
+ # each record returned.
722
+ #
723
+ # See Cassandra#get_range for more details.
724
+ #
273
725
  def get_range_batch(column_family, options = {})
274
726
  batch_size = options.delete(:batch_size) || 100
275
727
  count = options.delete(:key_count)
@@ -299,80 +751,221 @@ class Cassandra
299
751
  result
300
752
  end
301
753
 
302
- # Count all rows in the column_family you request. Supports the <tt>:start_key</tt>,
303
- # <tt>:finish_key</tt>, and <tt>:consistency</tt> options. Please note that
304
- # <tt>:start_key</tt> and <tt>:finish_key</tt> only work properly when
305
- # OrderPreservingPartitioner.
754
+ ##
755
+ # Count all rows in the column_family you request.
756
+ #
757
+ # This method just calls Cassandra#get_range_keys and returns the
758
+ # number of records returned.
759
+ #
760
+ # See Cassandra#get_range for options.
761
+ #
306
762
  def count_range(column_family, options = {})
307
763
  get_range_keys(column_family, options).length
308
764
  end
309
765
 
310
- # Return an Array containing all of the keys within a given range. (Only works
311
- # properly if the Cassandra cluster is using the OrderPreservingPartitioner.)
312
- # Supports <tt>:start_key</tt>, <tt>:finish_key</tt>, <tt>:count</tt>, and
313
- # <tt>:consistency</tt> options.
766
+ ##
767
+ # Return an Array containing all of the keys within a given range.
768
+ #
769
+ # This method just calls Cassandra#get_range and returns the
770
+ # row keys for the records returned.
771
+ #
772
+ # See Cassandra#get_range for options.
773
+ #
314
774
  def get_range_keys(column_family, options = {})
315
775
  get_range(column_family,options.merge!(:count => 1)).keys
316
776
  end
317
777
 
778
+ ##
318
779
  # Iterate through each key within the given parameters. This function can be
319
- # used to iterate over each key in the given column family. However, if you
320
- # only want to walk through a range of keys you need to have your cluster
321
- # setup with OrderPreservingPartitioner. Please note that this function walks
322
- # the list of keys in batches using the passed in <tt>:count</tt> option.
323
- # Supports <tt>:start_key</tt>, <tt>:finish_key</tt>, <tt>:count</tt>, and
324
- # <tt>:consistency</tt> options.
780
+ # used to iterate over each key in the given column family.
781
+ #
782
+ # This method just calls Cassandra#get_range and yields each row key.
783
+ #
784
+ # See Cassandra#get_range for options.
785
+ #
325
786
  def each_key(column_family, options = {})
326
787
  get_range_batch(column_family, options) do |key, columns|
327
788
  yield key
328
789
  end
329
790
  end
330
791
 
331
- # Iterate through each row and yields each key and value within the given parameters.
332
- # This function can be used to iterate over each key in the given column family.
333
- # However, if you only want to walk through a range of keys you need to have your
334
- # cluster setup with OrderPreservingPartitioner. Please note that this function walks
335
- # the list of keys in batches using the passed in <tt>:count</tt> option.
336
- # Supports the <tt>:key_count</tt>, <tt>:start_key</tt>, <tt>:finish_key</tt>,
337
- # <tt>:columns</tt>, <tt>:start</tt>, <tt>:finish</tt>, <tt>:count</tt>,
338
- # and <tt>:consistency</tt> options.
792
+ ##
793
+ # Iterate through each row in the given column family
794
+ #
795
+ # This method just calls Cassandra#get_range and yields the key and
796
+ # columns.
797
+ #
798
+ # See Cassandra#get_range for options.
799
+ #
339
800
  def each(column_family, options = {})
340
801
  get_range_batch(column_family, options) do |key, columns|
341
802
  yield key, columns
342
803
  end
343
804
  end
344
805
 
806
+ ##
345
807
  # Open a batch operation and yield self. Inserts and deletes will be queued
346
- # until the block closes, and then sent atomically to the server. Supports
347
- # the <tt>:consistency</tt> option, which overrides the consistency set in
808
+ # until the block closes, and then sent atomically to the server.
809
+ #
810
+ # Supports the :consistency option, which overrides the consistency set in
348
811
  # the individual commands.
812
+ #
349
813
  def batch(options = {})
350
- _, _, _, options =
351
- extract_and_validate_params(schema.keys.first, "", [options], WRITE_DEFAULTS)
352
-
353
- @batch = []
354
- yield(self)
355
- compacted_map,seen_clevels = compact_mutations!
356
- clevel = if options[:consistency] != nil # Override any clevel from individual mutations if
357
- options[:consistency]
358
- elsif seen_clevels.length > 1 # Cannot choose which CLevel to use if there are several ones
359
- raise "Multiple consistency levels used in the batch, and no override...cannot pick one"
360
- else # if no consistency override has been provided but all the clevels in the batch are the same: use that one
361
- seen_clevels.first
362
- end
363
-
364
- _mutate(compacted_map,clevel)
814
+ _, _, _, options =
815
+ extract_and_validate_params(schema.cf_defs.first.name, "", [options], WRITE_DEFAULTS)
816
+
817
+ @batch = []
818
+ yield(self)
819
+ compacted_map,seen_clevels = compact_mutations!
820
+ clevel = if options[:consistency] != nil # Override any clevel from individual mutations if
821
+ options[:consistency]
822
+ elsif seen_clevels.length > 1 # Cannot choose which CLevel to use if there are several ones
823
+ raise "Multiple consistency levels used in the batch, and no override...cannot pick one"
824
+ else # if no consistency override has been provided but all the clevels in the batch are the same: use that one
825
+ seen_clevels.first
826
+ end
827
+
828
+ _mutate(compacted_map,clevel)
365
829
  ensure
366
830
  @batch = nil
367
831
  end
368
-
832
+
833
+ ##
834
+ # Create secondary index.
835
+ #
836
+ # * keyspace
837
+ # * column_family
838
+ # * column_name
839
+ # * validation_class
840
+ #
841
+ def create_index(keyspace, column_family, column_name, validation_class)
842
+ return false if Cassandra.VERSION.to_f < 0.7
843
+
844
+ cf_def = client.describe_keyspace(keyspace).cf_defs.find{|x| x.name == column_family}
845
+ if !cf_def.nil? and !cf_def.column_metadata.find{|x| x.name == column_name}
846
+ c_def = CassandraThrift::ColumnDef.new do |cd|
847
+ cd.name = column_name
848
+ cd.validation_class = "org.apache.cassandra.db.marshal."+validation_class
849
+ cd.index_type = CassandraThrift::IndexType::KEYS
850
+ end
851
+ cf_def.column_metadata.push(c_def)
852
+ update_column_family(cf_def)
853
+ end
854
+ end
855
+
856
+ ##
857
+ # Delete secondary index.
858
+ #
859
+ # * keyspace
860
+ # * column_family
861
+ # * column_name
862
+ #
863
+ def drop_index(keyspace, column_family, column_name)
864
+ return false if Cassandra.VERSION.to_f < 0.7
865
+
866
+ cf_def = client.describe_keyspace(keyspace).cf_defs.find{|x| x.name == column_family}
867
+ if !cf_def.nil? and cf_def.column_metadata.find{|x| x.name == column_name}
868
+ cf_def.column_metadata.delete_if{|x| x.name == column_name}
869
+ update_column_family(cf_def)
870
+ end
871
+ end
872
+
873
+ ##
874
+ # This method is mostly used internally by get_index_slices to create
875
+ # a CassandraThrift::IndexExpression for the given options.
876
+ #
877
+ # * column_name - Column to be compared
878
+ # * value - Value to compare against
879
+ # * comparison - Type of comparison to do.
880
+ #
881
+ def create_index_expression(column_name, value, comparison)
882
+ return false if Cassandra.VERSION.to_f < 0.7
883
+
884
+ CassandraThrift::IndexExpression.new(
885
+ :column_name => column_name,
886
+ :value => value,
887
+ :op => (case comparison
888
+ when nil, "EQ", "eq", "=="
889
+ CassandraThrift::IndexOperator::EQ
890
+ when "GTE", "gte", ">="
891
+ CassandraThrift::IndexOperator::GTE
892
+ when "GT", "gt", ">"
893
+ CassandraThrift::IndexOperator::GT
894
+ when "LTE", "lte", "<="
895
+ CassandraThrift::IndexOperator::LTE
896
+ when "LT", "lt", "<"
897
+ CassandraThrift::IndexOperator::LT
898
+ end ))
899
+ end
900
+ alias :create_idx_expr :create_index_expression
901
+
902
+ ##
903
+ # This method takes an array if CassandraThrift::IndexExpression
904
+ # objects and creates a CassandraThrift::IndexClause for use in the
905
+ # Cassandra#get_index_slices
906
+ #
907
+ # * index_expressions - Array of CassandraThrift::IndexExpressions.
908
+ # * start - The starting row key.
909
+ # * count - The count of items to be returned
910
+ #
911
+ def create_index_clause(index_expressions, start = "", count = 100)
912
+ return false if Cassandra.VERSION.to_f < 0.7
913
+
914
+ CassandraThrift::IndexClause.new(
915
+ :start_key => start,
916
+ :expressions => index_expressions,
917
+ :count => count)
918
+ end
919
+ alias :create_idx_clause :create_index_clause
920
+
921
+ ##
922
+ # This method is used to query a secondary index with a set of
923
+ # provided search parameters
924
+ #
925
+ # Please note that you can either specify a
926
+ # CassandraThrift::IndexClause or an array of hashes with the
927
+ # format as below.
928
+ #
929
+ # * column_family - The Column Family this operation will be run on.
930
+ # * index_clause - This can either be a CassandraThrift::IndexClause or an array of hashes with the following keys:
931
+ # * :column_name - Column to be compared
932
+ # * :value - Value to compare against
933
+ # * :comparison - Type of comparison to do.
934
+ # * options
935
+ # * :key_count - Set maximum number of rows to return. (Only works if CassandraThrift::IndexClause is not passed in.)
936
+ # * :key_start - Set starting row key for search. (Only works if CassandraThrift::IndexClause is not passed in.)
937
+ # * :consistency
938
+ #
939
+ # TODO: Supercolumn support.
940
+ def get_indexed_slices(column_family, index_clause, *columns_and_options)
941
+ return false if Cassandra.VERSION.to_f < 0.7
942
+
943
+ column_family, columns, _, options =
944
+ extract_and_validate_params(column_family, [], columns_and_options, READ_DEFAULTS.merge(:key_count => 100, :key_start => ""))
945
+
946
+ if index_clause.class != CassandraThrift::IndexClause
947
+ index_expressions = index_clause.collect do |expression|
948
+ create_index_expression(expression[:column_name], expression[:value], expression[:comparison])
949
+ end
950
+
951
+ index_clause = create_index_clause(index_expressions, options[:key_start], options[:key_count])
952
+ end
953
+
954
+ key_slices = _get_indexed_slices(column_family, index_clause, columns, options[:count], options[:start],
955
+ options[:finish], options[:reversed], options[:consistency])
956
+
957
+ key_slices.inject({}){|h, key_slice| h[key_slice.key] = key_slice.columns; h}
958
+ end
959
+
369
960
  protected
370
961
 
371
962
  def calling_method
372
963
  "#{self.class}##{caller[0].split('`').last[0..-3]}"
373
964
  end
374
965
 
966
+ ##
375
967
  # Roll up queued mutations, to improve atomicity (and performance).
968
+ #
376
969
  def compact_mutations!
377
970
  used_clevels = {} # hash that lists the consistency levels seen in the batch array. key is the clevel, value is true
378
971
  by_key = Hash.new{|h,k | h[k] = {}}
@@ -404,8 +997,38 @@ class Cassandra
404
997
  [by_key, used_clevels.keys]
405
998
  end
406
999
 
1000
+ ##
1001
+ # Creates a new client as specified by Cassandra.thrift_client_options[:thrift_client_class]
1002
+ #
407
1003
  def new_client
408
1004
  thrift_client_class.new(CassandraThrift::Cassandra::Client, @servers, @thrift_client_options)
409
1005
  end
410
-
1006
+
1007
+ def client
1008
+ if @client.nil? || @client.current_server.nil?
1009
+ reconnect!
1010
+ @client.set_keyspace(@keyspace)
1011
+ end
1012
+ @client
1013
+ end
1014
+
1015
+ def reconnect!
1016
+ @servers = all_nodes
1017
+ @client = new_client
1018
+ end
1019
+
1020
+ def all_nodes
1021
+ if @auto_discover_nodes && !@keyspace.eql?("system")
1022
+ temp_client = new_client
1023
+ begin
1024
+ ips = (temp_client.describe_ring(@keyspace).map {|range| range.endpoints}).flatten.uniq
1025
+ port = @servers.first.split(':').last
1026
+ ips.map{|ip| "#{ip}:#{port}" }
1027
+ ensure
1028
+ temp_client.disconnect!
1029
+ end
1030
+ else
1031
+ @servers
1032
+ end
1033
+ end
411
1034
  end