cassandra 0.11.0 → 0.11.1

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 (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