cassandra 0.11.4 → 0.12.0

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG CHANGED
@@ -1,3 +1,5 @@
1
+ v0.12.0 Changed thrift_client dependency to 0.7.0
2
+
1
3
  v0.11.4
2
4
  - Fix get_range to invoke blocks
3
5
  - Fix current distribution urls in Rakfile. Resolves Issue# 97.
data/README.md CHANGED
@@ -1,7 +1,7 @@
1
- # cassandra
1
+ # cassandra
2
2
  A Ruby client for the Cassandra distributed database.
3
3
 
4
- Supports 1.8.7, 1.9.2, and rubinius on Cassandra 0.6.13, 0.7.5, 0.8.0.
4
+ Supports 1.8.7, 1.9.2, and rubinius on Cassandra 0.6.13, 0.7.8, 0.8.4.
5
5
 
6
6
  ## Getting Started
7
7
 
@@ -14,7 +14,7 @@ API below):
14
14
 
15
15
  ## License
16
16
 
17
- Copyright 2009, 2010 Twitter, Inc. See included LICENSE file. Portions copyright 2004-2009 David Heinemeier Hansson, and used with permission.
17
+ Copyright 2009-2011 Twitter, Inc. See included LICENSE file. Portions copyright 2004-2009 David Heinemeier Hansson, and used with permission.
18
18
 
19
19
  ## Cassandra Version
20
20
 
@@ -24,14 +24,14 @@ with. We have set up an easy sure fire mechanism for selecting the
24
24
  specific version that you are connecting to while requiring the gem.
25
25
 
26
26
  #### Require Method
27
- The default version is the currently stable release of cassandra. (0.7
28
- at this time, but 0.8 is looming in the near future.)
27
+ The default version is the currently stable release of cassandra. (0.8
28
+ at this time.)
29
29
 
30
30
  To use the default version simply use a normal require:
31
31
 
32
32
  require 'cassandra'
33
33
 
34
- To use a specific version (0.7 in this example) you would use a
34
+ To use a specific version (0.7 in this example) you would use a
35
35
  slightly differently formatted require:
36
36
 
37
37
  require 'cassandra/0.7'
@@ -42,13 +42,13 @@ own projects or irb, but if you would rather not hard code your app to a
42
42
  specific version you can always specify an environment variable with the
43
43
  version you are using:
44
44
 
45
- export CASSANDRA_VERSION=0.7
45
+ export CASSANDRA_VERSION=0.8
46
46
 
47
47
  Then you would use the default require as listed above:
48
48
 
49
49
  require 'cassandra'
50
50
 
51
- ## Read/Write API Method Reference
51
+ ## Read/Write API Method Reference
52
52
 
53
53
  ### insert
54
54
 
@@ -79,16 +79,18 @@ Example:
79
79
 
80
80
  ### remove
81
81
 
82
- * column\_family - The column\_family that you are inserting into.
83
- * key - The row key to insert.
84
- * columns - Either a single super_column or a list of columns.
85
- * sub_columns - The list of sub\_columns to select.
82
+ * column\_family - The column\_family that you are working with.
83
+ * key - The row key to remove (or remove columns from).
84
+ * columns - Either a single super_column or a list of columns to remove.
85
+ * sub_columns - The list of sub\_columns to remove.
86
86
  * options - Valid options are:
87
87
  * :timestamp - Uses the current time if none specified.
88
88
  * :consistency - Uses the default write consistency if none specified.
89
89
 
90
90
  This method is used to delete (actually marking them as deleted with a
91
- tombstone) columns or super columns.
91
+ tombstone) rows, columns, or super columns depending on the parameters
92
+ passed. If only a key is passed the entire row will be marked as deleted.
93
+ If a column name is passed in that column will be deleted.
92
94
 
93
95
  Example:
94
96
 
@@ -101,11 +103,14 @@ Example:
101
103
 
102
104
  Count the columns for the provided parameters.
103
105
 
104
- * column\_family - The column\_family that you are inserting into.
105
- * key - The row key to insert.
106
+ * column\_family - The column\_family that you are working with.
107
+ * key - The row key.
106
108
  * columns - Either a single super_column or a list of columns.
107
109
  * sub_columns - The list of sub\_columns to select.
108
110
  * options - Valid options are:
111
+ * :start - The column name to start from.
112
+ * :stop - The column name to stop at.
113
+ * :count - The maximum count of columns to return. (By default cassandra will count up to 100 columns)
109
114
  * :consistency - Uses the default read consistency if none specified.
110
115
 
111
116
  Example:
@@ -117,10 +122,10 @@ Example:
117
122
 
118
123
  Return a hash (actually, a Cassandra::OrderedHash) or a single value
119
124
  representing the element at the column_family:key:[column]:[sub_column]
120
- path you request.
125
+ path you request.
121
126
 
122
- * column\_family - The column\_family that you are inserting into.
123
- * key - The row key to insert.
127
+ * column\_family - The column\_family that you are working with.
128
+ * key - The row key to select.
124
129
  * columns - Either a single super\_column or a list of columns.
125
130
  * sub\_columns - The list of sub\_columns to select.
126
131
  * options - Valid options are:
@@ -146,8 +151,8 @@ returned.
146
151
 
147
152
  Supports the same parameters as Cassandra#get.
148
153
 
149
- * column_family - The column_family that you are inserting into.
150
- * key - An array of keys to.
154
+ * column_family - The column_family that you are working with.
155
+ * key - An array of keys to select.
151
156
  * columns - Either a single super_column or a list of columns.
152
157
  * sub_columns - The list of sub\_columns to select.
153
158
  * options - Valid options are:
@@ -163,8 +168,8 @@ Example:
163
168
  @client.insert(:Users, '2', {'body' => 'v2', 'user' => 'v2'})
164
169
 
165
170
  expected = OrderedHash[
166
- '1', {'body' => 'v1', 'user' => 'v1'},
167
- '2', {'body' => 'v2', 'user' => 'v2'},
171
+ '1', {'body' => 'v1', 'user' => 'v1'},
172
+ '2', {'body' => 'v2', 'user' => 'v2'},
168
173
  'bogus', {}
169
174
  ]
170
175
  result = @client.multi_get(:Users, ['1', '2', 'bogus'])
@@ -180,10 +185,10 @@ that specific column/super column.
180
185
 
181
186
  This method will return true or false.
182
187
 
183
- * column\_family - The column\_family that you are inserting into.
184
- * key - The row key to insert.
188
+ * column\_family - The column\_family that you are working with.
189
+ * key - The row key to check.
185
190
  * columns - Either a single super\_column or a list of columns.
186
- * sub\_columns - The list of sub\_columns to select.
191
+ * sub\_columns - The list of sub\_columns to check.
187
192
  * options - Valid options are:
188
193
  * :consistency - Uses the default read consistency if none specified.
189
194
 
@@ -205,7 +210,7 @@ is passed in Cassandra#get\_range\_batch will be called. Otherwise
205
210
  Cassandra#get\_range\_single will be used.
206
211
 
207
212
  The start\_key and finish\_key parameters are only useful for iterating of all records
208
- as is done in the Cassandra#each and Cassandra#each\_key methods if you are using the
213
+ as is done in the Cassandra#each and Cassandra#each\_key methods if you are using the
209
214
  RandomPartitioner.
210
215
 
211
216
  If the table is partitioned with OrderPreservingPartitioner you may
@@ -216,20 +221,18 @@ If a block is passed in we will yield the row key and columns for
216
221
  each record returned.
217
222
 
218
223
  Please note that Cassandra returns a row for each row that has existed in the
219
- system since gc\_grace\_seconds. This is because deleted row keys are marked as
224
+ system since gc\_grace\_seconds. This is because deleted row keys are marked as
220
225
  deleted, but left in the system until the cluster has had resonable time to replicate the deletion.
221
226
  This function attempts to suppress deleted rows (actually any row returned without
222
227
  columns is suppressed).
223
228
 
224
- * column\_family - The column\_family that you are inserting into.
225
- * key - The row key to insert.
226
- * columns - Either a single super\_column or a list of columns.
227
- * sub\_columns - The list of sub\_columns to select.
229
+ * column\_family - The column\_family that you are working with.
228
230
  * options - Valid options are:
229
231
  * :start\_key - The starting value for selecting a range of keys (only useful with OPP).
230
232
  * :finish\_key - The final value for selecting a range of keys (only useful with OPP).
231
233
  * :key\_count - The total number of keys to return from the query. (see note regarding deleted records)
232
234
  * :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.
235
+ * :columns - A list of columns to return.
233
236
  * :count - The number of columns requested to be returned.
234
237
  * :start - The starting value for selecting a range of columns.
235
238
  * :finish - The final value for selecting a range of columns.
@@ -335,9 +338,9 @@ Example:
335
338
  # verify that GT and LT queries perform properly
336
339
  expressions = [
337
340
  { :column_name => 'x',
338
- :value => [0,20].pack("NN"),
341
+ :value => [0,20].pack("NN"),
339
342
  :comparison => "=="},
340
- { :column_name => 'non_indexed',
343
+ { :column_name => 'non_indexed',
341
344
  :value => [5].pack("N*"),
342
345
  :comparison => ">"}
343
346
  ]
data/Rakefile CHANGED
@@ -9,7 +9,7 @@ unless ENV['FROM_BIN_CASSANDRA_HELPER']
9
9
  p.project = "fauna"
10
10
  p.summary = "A Ruby client for the Cassandra distributed database."
11
11
  p.rubygems_version = ">= 0.8"
12
- p.dependencies = ['thrift_client >=0.6.3', 'json', 'rake', 'simple_uuid >=0.1.0']
12
+ p.dependencies = ['thrift_client >=0.7.0', 'json', 'rake', 'simple_uuid >=0.1.0']
13
13
  p.ignore_pattern = /^(data|vendor\/cassandra|cassandra|vendor\/thrift|.*\.rbc)/
14
14
  p.rdoc_pattern = /^(lib|bin|tasks|ext)|^README|^CHANGELOG|^TODO|^LICENSE|^COPYING$/
15
15
  p.retain_gemspec = true
@@ -17,13 +17,13 @@ unless ENV['FROM_BIN_CASSANDRA_HELPER']
17
17
  end
18
18
 
19
19
  CassandraBinaries = {
20
- '0.6' => 'http://www.apache.org/dist/cassandra/0.6.13/apache-cassandra-0.6.13-bin.tar.gz',
21
- '0.7' => 'http://www.apache.org/dist/cassandra/0.7.7/apache-cassandra-0.7.7-bin.tar.gz',
22
- '0.8' => 'http://www.apache.org/dist/cassandra/0.8.1/apache-cassandra-0.8.1-bin.tar.gz'
20
+ '0.6' => 'http://archive.apache.org/dist/cassandra/0.6.13/apache-cassandra-0.6.13-bin.tar.gz',
21
+ '0.7' => 'http://archive.apache.org/dist/cassandra/0.7.8/apache-cassandra-0.7.8-bin.tar.gz',
22
+ '0.8' => 'http://archive.apache.org/dist/cassandra/0.8.4/apache-cassandra-0.8.4-bin.tar.gz'
23
23
  }
24
24
 
25
25
  CASSANDRA_HOME = ENV['CASSANDRA_HOME'] || "#{ENV['HOME']}/cassandra"
26
- CASSANDRA_VERSION = ENV['CASSANDRA_VERSION'] || '0.7'
26
+ CASSANDRA_VERSION = ENV['CASSANDRA_VERSION'] || '0.8'
27
27
  CASSANDRA_PIDFILE = ENV['CASSANDRA_PIDFILE'] || "#{CASSANDRA_HOME}/cassandra.pid"
28
28
 
29
29
  def setup_cassandra_version(version = CASSANDRA_VERSION)
@@ -77,7 +77,7 @@ end
77
77
 
78
78
  namespace :cassandra do
79
79
  desc "Start Cassandra"
80
- task :start, :daemonize, :needs => :java do |t, args|
80
+ task :start, [:daemonize] => :java do |t, args|
81
81
  args.with_defaults(:daemonize => true)
82
82
 
83
83
  setup_cassandra_version
@@ -2,11 +2,11 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = %q{cassandra}
5
- s.version = "0.11.4"
5
+ s.version = "0.12.0"
6
6
 
7
7
  s.required_rubygems_version = Gem::Requirement.new(">= 0.8") if s.respond_to? :required_rubygems_version=
8
8
  s.authors = [%q{Evan Weaver, Ryan King}]
9
- s.date = %q{2011-07-22}
9
+ s.date = %q{2011-08-22}
10
10
  s.description = %q{A Ruby client for the Cassandra distributed database.}
11
11
  s.email = %q{}
12
12
  s.executables = [%q{cassandra_helper}]
@@ -24,18 +24,18 @@ Gem::Specification.new do |s|
24
24
  s.specification_version = 3
25
25
 
26
26
  if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
27
- s.add_runtime_dependency(%q<thrift_client>, [">= 0.6.3"])
27
+ s.add_runtime_dependency(%q<thrift_client>, [">= 0.7.0"])
28
28
  s.add_runtime_dependency(%q<json>, [">= 0"])
29
29
  s.add_runtime_dependency(%q<rake>, [">= 0"])
30
30
  s.add_runtime_dependency(%q<simple_uuid>, [">= 0.1.0"])
31
31
  else
32
- s.add_dependency(%q<thrift_client>, [">= 0.6.3"])
32
+ s.add_dependency(%q<thrift_client>, [">= 0.7.0"])
33
33
  s.add_dependency(%q<json>, [">= 0"])
34
34
  s.add_dependency(%q<rake>, [">= 0"])
35
35
  s.add_dependency(%q<simple_uuid>, [">= 0.1.0"])
36
36
  end
37
37
  else
38
- s.add_dependency(%q<thrift_client>, [">= 0.6.3"])
38
+ s.add_dependency(%q<thrift_client>, [">= 0.7.0"])
39
39
  s.add_dependency(%q<json>, [">= 0"])
40
40
  s.add_dependency(%q<rake>, [">= 0"])
41
41
  s.add_dependency(%q<simple_uuid>, [">= 0.1.0"])
@@ -22,7 +22,7 @@
22
22
  "subcomparator_type":"org.apache.cassandra.db.marshal.TimeUUIDType",
23
23
  "comparator_type":"org.apache.cassandra.db.marshal.UTF8Type",
24
24
  "column_type":"Super"},
25
- "Index":{
25
+ "Indexes":{
26
26
  "comparator_type":"org.apache.cassandra.db.marshal.UTF8Type",
27
27
  "column_type":"Super"},
28
28
  "TimelinishThings":{
@@ -15,7 +15,7 @@ create column family StatusRelationships with
15
15
  comparator = 'UTF8Type' and
16
16
  column_type = 'Super' and
17
17
  subcomparator = 'TimeUUIDType';
18
- create column family Index with
18
+ create column family Indexes with
19
19
  comparator = 'UTF8Type' and
20
20
  column_type = 'Super';
21
21
  create column family TimelinishThings with
@@ -31,7 +31,7 @@
31
31
  "subcomparator_type":"org.apache.cassandra.db.marshal.TimeUUIDType",
32
32
  "comparator_type":"org.apache.cassandra.db.marshal.UTF8Type",
33
33
  "column_type":"Super"},
34
- "Index":{
34
+ "Indexes":{
35
35
  "comparator_type":"org.apache.cassandra.db.marshal.UTF8Type",
36
36
  "column_type":"Super"},
37
37
  "TimelinishThings":{
@@ -21,7 +21,7 @@ create column family StatusRelationships with
21
21
  comparator = 'UTF8Type' and
22
22
  column_type = 'Super' and
23
23
  subcomparator = 'TimeUUIDType';
24
- create column family Index with
24
+ create column family Indexes with
25
25
  comparator = 'UTF8Type' and
26
26
  column_type = 'Super';
27
27
  create column family TimelinishThings with
@@ -1,5 +1,5 @@
1
1
  require 'rubygems'
2
- gem 'thrift_client', '~> 0.6.3'
2
+ gem 'thrift_client', '~> 0.7.0'
3
3
  require 'thrift_client'
4
4
  gem 'simple_uuid' , '~> 0.1.0'
5
5
  require 'simple_uuid'
@@ -10,7 +10,7 @@ here = File.expand_path(File.dirname(__FILE__))
10
10
 
11
11
  class Cassandra ; end
12
12
  unless Cassandra.respond_to?(:VERSION)
13
- require "#{here}/cassandra/0.7"
13
+ require "#{here}/cassandra/0.8"
14
14
  end
15
15
 
16
16
  $LOAD_PATH << "#{here}/../vendor/#{Cassandra.VERSION}/gen-rb"
@@ -60,8 +60,8 @@ class Cassandra
60
60
  }
61
61
 
62
62
  THRIFT_DEFAULTS = {
63
- :transport_wrapper => Thrift::BufferedTransport,
64
- :thrift_client_class => ThriftClient
63
+ :transport_wrapper => Thrift::FramedTransport,
64
+ :thrift_client_class => ThriftClient
65
65
  }
66
66
 
67
67
  attr_reader :keyspace, :servers, :schema, :thrift_client_options, :thrift_client_class, :auth_request
@@ -154,6 +154,15 @@ class Cassandra
154
154
  client.describe_keyspaces.to_a.collect {|ksdef| ksdef.name }
155
155
  end
156
156
 
157
+ ##
158
+ # Return a hash of column_family definitions indexed by their
159
+ # names
160
+ def column_families
161
+ return false if Cassandra.VERSION.to_f < 0.7
162
+
163
+ schema.cf_defs.inject(Hash.new){|memo, cf_def| memo[cf_def.name] = cf_def; memo;}
164
+ end
165
+
157
166
  ##
158
167
  # Return a Cassandra::Keyspace object loaded with the current
159
168
  # keyspaces schema.
@@ -290,7 +299,7 @@ class Cassandra
290
299
  # * new_name - The desired column_family name.
291
300
  #
292
301
  def rename_column_family(old_name, new_name)
293
- return false if Cassandra.VERSION.to_f < 0.7
302
+ return false if Cassandra.VERSION.to_f != 0.7
294
303
 
295
304
  begin
296
305
  res = client.system_rename_column_family(old_name, new_name)
@@ -453,7 +462,9 @@ class Cassandra
453
462
 
454
463
  ##
455
464
  # This method is used to delete (actually marking them as deleted with a
456
- # tombstone) columns or super columns.
465
+ # tombstone) rows, columns, or super columns depending on the parameters
466
+ # passed. If only a key is passed the entire row will be marked as deleted.
467
+ # If a column name is passed in that column will be deleted.
457
468
  #
458
469
  # This method can also be used in batch mode. If in batch mode then we
459
470
  # queue up the mutations (a deletion in this case)
@@ -497,12 +508,15 @@ class Cassandra
497
508
  # * columns - Either a single super_column or a list of columns.
498
509
  # * sub_columns - The list of sub_columns to select.
499
510
  # * options - Valid options are:
511
+ # * :start - The column name to start from.
512
+ # * :stop - The column name to stop at.
513
+ # * :count - The maximum count of columns to return. (By default cassandra will count up to 100 columns)
500
514
  # * :consistency - Uses the default read consistency if none specified.
501
515
  #
502
516
  def count_columns(column_family, key, *columns_and_options)
503
517
  column_family, super_column, _, options =
504
518
  extract_and_validate_params(column_family, key, columns_and_options, READ_DEFAULTS)
505
- _count_columns(column_family, key, super_column, options[:consistency])
519
+ _count_columns(column_family, key, super_column, options[:start], options[:stop], options[:count], options[:consistency])
506
520
  end
507
521
 
508
522
  ##
@@ -587,16 +601,16 @@ class Cassandra
587
601
  #
588
602
  # Supports the same parameters as Cassandra#get.
589
603
  #
590
- # * column_family - The column_family that you are inserting into.
591
- # * key - An array of keys to.
592
- # * columns - Either a single super_column or a list of columns.
593
- # * sub_columns - The list of sub_columns to select.
594
- # * options - Valid options are:
595
- # * :count - The number of columns requested to be returned.
596
- # * :start - The starting value for selecting a range of columns.
597
- # * :finish - The final value for selecting a range of columns.
598
- # * :reversed - If set to true the results will be returned in reverse order.
599
- # * :consistency - Uses the default read consistency if none specified.
604
+ # * column_family - The column_family that you are inserting into.
605
+ # * keys - An array of keys to select.
606
+ # * columns - Either a single super_column or a list of columns.
607
+ # * sub_columns - The list of sub_columns to select.
608
+ # * options - Valid options are:
609
+ # * :count - The number of columns requested to be returned.
610
+ # * :start - The starting value for selecting a range of columns.
611
+ # * :finish - The final value for selecting a range of columns.
612
+ # * :reversed - If set to true the results will be returned in reverse order.
613
+ # * :consistency - Uses the default read consistency if none specified.
600
614
  #
601
615
  def multi_get(column_family, keys, *columns_and_options)
602
616
  column_family, column, sub_column, options =
@@ -664,16 +678,17 @@ class Cassandra
664
678
  # deleted, but left in the system until the cluster has had resonable time to replicate the deletion.
665
679
  # This function attempts to suppress deleted rows (actually any row returned without
666
680
  # columns is suppressed).
681
+ #
682
+ # Please note that when enabling the :reversed option, :start and :finish should be swapped (e.g.
683
+ # reversal happens before selecting the range).
667
684
  #
668
685
  # * column_family - The column_family that you are inserting into.
669
- # * key - The row key to insert.
670
- # * columns - Either a single super_column or a list of columns.
671
- # * sub_columns - The list of sub_columns to select.
672
686
  # * options - Valid options are:
673
687
  # * :start_key - The starting value for selecting a range of keys (only useful with OPP).
674
688
  # * :finish_key - The final value for selecting a range of keys (only useful with OPP).
675
689
  # * :key_count - The total number of keys to return from the query. (see note regarding deleted records)
676
690
  # * :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.
691
+ # * :columns - A list of columns to return.
677
692
  # * :count - The number of columns requested to be returned.
678
693
  # * :start - The starting value for selecting a range of columns.
679
694
  # * :finish - The final value for selecting a range of columns.
@@ -700,9 +715,10 @@ class Cassandra
700
715
  column_family, _, _, options =
701
716
  extract_and_validate_params(column_family, "", [options],
702
717
  READ_DEFAULTS.merge(:start_key => '',
703
- :end_key => '',
718
+ :finish_key => '',
704
719
  :key_count => 100,
705
- :columns => nil
720
+ :columns => nil,
721
+ :reversed => false
706
722
  )
707
723
  )
708
724
 
@@ -714,7 +730,8 @@ class Cassandra
714
730
  options[:start].to_s,
715
731
  options[:finish].to_s,
716
732
  options[:count],
717
- options[:consistency] )
733
+ options[:consistency],
734
+ options[:reversed] )
718
735
 
719
736
  multi_key_slices_to_hash(column_family, results, return_empty_rows)
720
737
  end
@@ -13,6 +13,8 @@ class Cassandra
13
13
  include ::Cassandra::Helpers
14
14
  include ::Cassandra::Columns
15
15
 
16
+ attr_reader :keyspace
17
+
16
18
  def initialize(keyspace, schema)
17
19
  @is_super = {}
18
20
  @keyspace = keyspace
@@ -100,7 +102,7 @@ class Cassandra
100
102
  row[column]
101
103
  else
102
104
  row = apply_range(row, column_family, options[:start], options[:finish])
103
- apply_count(row, options[:count], options[:reversed])
105
+ row = apply_count(row, options[:count], options[:reversed])
104
106
  end
105
107
  end
106
108
 
@@ -119,7 +121,7 @@ class Cassandra
119
121
  else
120
122
  row = row[column] || OrderedHash.new
121
123
  row = apply_range(row, column_family, options[:start], options[:finish], false)
122
- apply_count(row, options[:count], options[:reversed])
124
+ row = apply_count(row, options[:count], options[:reversed])
123
125
  end
124
126
  else
125
127
  row
@@ -148,9 +150,9 @@ class Cassandra
148
150
  else
149
151
  if column
150
152
  if sub_column
151
- cf(column_family)[key][column].delete(sub_column.to_s)
153
+ cf(column_family)[key][column].delete(sub_column.to_s) if cf(column_family)[key][column]
152
154
  else
153
- cf(column_family)[key].delete(column.to_s)
155
+ cf(column_family)[key].delete(column.to_s) if cf(column_family)[key]
154
156
  end
155
157
  else
156
158
  cf(column_family).delete(key)
@@ -173,8 +175,10 @@ class Cassandra
173
175
  end
174
176
  end
175
177
 
176
- def count_columns(column_family, key, column=nil)
177
- get(column_family, key, column).keys.length
178
+ def count_columns(column_family, key, *columns_and_options)
179
+ column_family, columns, sub_columns, options = extract_and_validate_params_for_real(column_family, key, columns_and_options, READ_DEFAULTS)
180
+
181
+ get(column_family, key, columns, options).keys.length
178
182
  end
179
183
 
180
184
  def multi_get_columns(column_family, keys, columns)
@@ -192,11 +196,12 @@ class Cassandra
192
196
  end
193
197
 
194
198
  def get_range(column_family, options = {}, &blk)
195
- column_family, _, _, options = extract_and_validate_params_for_real(column_family, "", [options],
196
- READ_DEFAULTS.merge(:start_key => '',
197
- :end_key => '',
199
+ column_family, _, _, options = extract_and_validate_params_for_real(column_family, "", [options],
200
+ READ_DEFAULTS.merge(:start_key => nil,
201
+ :finish_key => nil,
198
202
  :key_count => 100,
199
- :columns => nil
203
+ :columns => nil,
204
+ :reversed => false
200
205
  )
201
206
  )
202
207
  _get_range(column_family,
@@ -207,7 +212,8 @@ class Cassandra
207
212
  options[:start],
208
213
  options[:finish],
209
214
  options[:count],
210
- options[:consistency], &blk)
215
+ options[:consistency],
216
+ options[:reversed], &blk)
211
217
  end
212
218
 
213
219
  def get_range_keys(column_family, options = {})
@@ -248,7 +254,7 @@ class Cassandra
248
254
  def create_index(ks_name, cf_name, c_name, v_class)
249
255
  if @indexes[ks_name] &&
250
256
  @indexes[ks_name][cf_name] &&
251
- @indexes[ks_name][cf_name][c_name]
257
+ @indexes[ks_name][cf_name][c_name]
252
258
  nil
253
259
 
254
260
  else
@@ -261,7 +267,7 @@ class Cassandra
261
267
  def drop_index(ks_name, cf_name, c_name)
262
268
  if @indexes[ks_name] &&
263
269
  @indexes[ks_name][cf_name] &&
264
- @indexes[ks_name][cf_name][c_name]
270
+ @indexes[ks_name][cf_name][c_name]
265
271
 
266
272
  @indexes[ks_name][cf_name].delete(c_name)
267
273
  else
@@ -323,6 +329,21 @@ class Cassandra
323
329
  nil
324
330
  end
325
331
 
332
+ def column_families
333
+ cf_defs = {}
334
+ schema.each do |key, value|
335
+ cf_def = Cassandra::ColumnFamily.new
336
+
337
+ value.each do |property, property_value|
338
+ cf_def.send(:"#{property}=", property_value)
339
+ end
340
+
341
+ cf_defs[key] = cf_def
342
+ end
343
+
344
+ cf_defs
345
+ end
346
+
326
347
  def schema(load=true)
327
348
  @schema
328
349
  end
@@ -331,13 +352,33 @@ class Cassandra
331
352
  schema[column_family.to_s][key]
332
353
  end
333
354
 
355
+ def add_column_family(cf)
356
+ @schema[cf.name.to_s] ||= OrderedHash.new
357
+
358
+ cf.instance_variables.each do |var|
359
+ @schema[cf.name.to_s][var.slice(1..-1)] = cf.instance_variable_get(var)
360
+ end
361
+ end
362
+
363
+ def update_column_family(cf)
364
+ return false unless @schema.include?(cf.name.to_s)
365
+
366
+ cf.instance_variables.each do |var|
367
+ @schema[cf.name.to_s][var.slice(1..-1)] = cf.instance_variable_get(var)
368
+ end
369
+ end
370
+
371
+ def drop_column_family(column_family_name)
372
+ @schema.delete(column_family_name)
373
+ end
374
+
334
375
  private
335
376
 
336
377
  def schema_for_keyspace(keyspace)
337
378
  @schema
338
379
  end
339
380
 
340
- def _get_range(column_family, start_key, finish_key, key_count, columns, start, finish, count, consistency, &blk)
381
+ def _get_range(column_family, start_key, finish_key, key_count, columns, start, finish, count, consistency, reversed, &blk)
341
382
  ret = OrderedHash.new
342
383
  start = to_compare_with_type(start, column_family)
343
384
  finish = to_compare_with_type(finish, column_family)
@@ -347,10 +388,12 @@ class Cassandra
347
388
  if columns
348
389
  #ret[key] = columns.inject(OrderedHash.new){|hash, column_name| hash[column_name] = cf(column_family)[key][column_name]; hash;}
349
390
  ret[key] = columns_to_hash(column_family, cf(column_family)[key].select{|k,v| columns.include?(k)})
391
+ ret[key] = apply_count(ret[key], count, reversed)
350
392
  blk.call(key,ret[key]) unless blk.nil?
351
393
  else
352
394
  #ret[key] = apply_range(cf(column_family)[key], column_family, start, finish, !is_super(column_family))
353
395
  ret[key] = apply_range(columns_to_hash(column_family, cf(column_family)[key]), column_family, start, finish)
396
+ ret[key] = apply_count(ret[key], count, reversed)
354
397
  blk.call(key,ret[key]) unless blk.nil?
355
398
  end
356
399
  end
@@ -122,6 +122,10 @@ class Cassandra
122
122
  @keys = other.keys
123
123
  self
124
124
  end
125
+
126
+ def reverse
127
+ OrderedHashInt[self.to_a.reverse]
128
+ end
125
129
 
126
130
  private
127
131
 
@@ -11,14 +11,14 @@ class Cassandra
11
11
  client.remove(key, column_path, timestamp, consistency_level)
12
12
  end
13
13
 
14
- # FIXME: Add support for start, stop, count
15
- def _count_columns(column_family, key, super_column, consistency)
14
+ def _count_columns(column_family, key, super_column, start, stop, count, consistency)
16
15
  client.get_count(key,
17
16
  CassandraThrift::ColumnParent.new(:column_family => column_family, :super_column => super_column),
18
17
  CassandraThrift::SlicePredicate.new(:slice_range =>
19
18
  CassandraThrift::SliceRange.new(
20
- :start => '',
21
- :finish => ''
19
+ :start => start || '',
20
+ :finish => stop || '',
21
+ :count => count || 100
22
22
  )),
23
23
  consistency
24
24
  )
@@ -85,7 +85,7 @@ class Cassandra
85
85
  end
86
86
  end
87
87
 
88
- def _get_range(column_family, start_key, finish_key, key_count, columns, start, finish, count, consistency)
88
+ def _get_range(column_family, start_key, finish_key, key_count, columns, start, finish, count, consistency, reversed=false)
89
89
  column_parent = CassandraThrift::ColumnParent.new(:column_family => column_family)
90
90
  predicate = if columns
91
91
  CassandraThrift::SlicePredicate.new(:column_names => columns)
@@ -94,7 +94,8 @@ class Cassandra
94
94
  CassandraThrift::SliceRange.new(
95
95
  :start => start,
96
96
  :finish => finish,
97
- :count => count))
97
+ :count => count,
98
+ :reversed => reversed))
98
99
  end
99
100
  range = CassandraThrift::KeyRange.new(:start_key => start_key, :end_key => finish_key, :count => key_count)
100
101
  client.get_range_slices(column_parent, predicate, range, consistency)
@@ -43,6 +43,29 @@ class CassandraMockTest < CassandraTest
43
43
  @twitter.insert(:Statuses, 'a', {:text => 'foo'})
44
44
  assert_equal ['a'], @twitter.get_range(:Statuses, :key_count => 1).keys
45
45
  end
46
+
47
+ def test_get_range_reversed
48
+ data = 3.times.map { |i| ["body-#{i.to_s}", "v"] }
49
+ hash = Cassandra::OrderedHash[data]
50
+ reversed_hash = Cassandra::OrderedHash[data.reverse]
51
+
52
+ @twitter.insert(:Statuses, "all-keys", hash)
53
+
54
+ columns = @twitter.get_range(:Statuses, :reversed => true)["all-keys"]
55
+ columns.each do |column|
56
+ assert_equal reversed_hash.shift, column
57
+ end
58
+ end
59
+
60
+ def test_get_range_count
61
+ data = 3.times.map { |i| ["body-#{i.to_s}", "v"] }
62
+ hash = Cassandra::OrderedHash[data]
63
+
64
+ @twitter.insert(:Statuses, "all-keys", hash)
65
+
66
+ columns = @twitter.get_range(:Statuses, :count => 2)["all-keys"]
67
+ assert_equal 2, columns.count
68
+ end
46
69
 
47
70
  def test_inserting_array_for_indices
48
71
  @twitter.insert(:TimelinishThings, 'a', ['1','2'])
@@ -4,16 +4,16 @@ class CassandraTest < Test::Unit::TestCase
4
4
  include Cassandra::Constants
5
5
 
6
6
  def setup
7
- @twitter = Cassandra.new('Twitter', "127.0.0.1:9160", :retries => 2, :connect_timeout => 1, :exception_classes => [])
7
+ @twitter = Cassandra.new('Twitter', "127.0.0.1:9160", :retries => 2, :connect_timeout => 0.1, :exception_classes => [])
8
8
  @twitter.clear_keyspace!
9
9
 
10
- @blogs = Cassandra.new('Multiblog', "127.0.0.1:9160", :retries => 2, :connect_timeout => 1, :exception_classes => [])
10
+ @blogs = Cassandra.new('Multiblog', "127.0.0.1:9160", :retries => 2, :connect_timeout => 0.1, :exception_classes => [])
11
11
  @blogs.clear_keyspace!
12
12
 
13
- @blogs_long = Cassandra.new('MultiblogLong', "127.0.0.1:9160", :retries => 2, :connect_timeout => 1, :exception_classes => [])
13
+ @blogs_long = Cassandra.new('MultiblogLong', "127.0.0.1:9160", :retries => 2, :connect_timeout => 0.1, :exception_classes => [])
14
14
  @blogs_long.clear_keyspace!
15
15
 
16
- @type_conversions = Cassandra.new('TypeConversions', "127.0.0.1:9160", :retries => 2, :connect_timeout => 1, :exception_classes => [])
16
+ @type_conversions = Cassandra.new('TypeConversions', "127.0.0.1:9160", :retries => 2, :connect_timeout => 0.1, :exception_classes => [])
17
17
  @type_conversions.clear_keyspace!
18
18
 
19
19
  Cassandra::WRITE_DEFAULTS[:consistency] = Cassandra::Consistency::ONE
@@ -259,6 +259,25 @@ class CassandraTest < Test::Unit::TestCase
259
259
  assert_equal(4, @twitter.get_range_keys(:Statuses, :key_count => 4).size)
260
260
  end
261
261
 
262
+ def test_get_range_with_count
263
+ @twitter.insert(:Statuses, key + '1', {'test_column1' => '1', 'test_column2' => '2', 'test_column3' => '2', 'deleted_column' => '1'})
264
+ @twitter.insert(:Statuses, key + '2', {'test_column4' => '3', 'test_column5' => '4', 'test_column6' => '2', 'deleted_column' => '2'})
265
+
266
+ @twitter.get_range(:Statuses, :count => 3) do |key, columns|
267
+ assert_equal columns.count, 3
268
+ end
269
+
270
+ assert_equal 2, @twitter.get_range(:Statuses, :start_key => key + '1', :finish_key => key + '1', :count => 2)[key + '1'].count
271
+
272
+ @twitter.remove(:Statuses, key + '1', 'deleted_column')
273
+ @twitter.remove(:Statuses, key + '2', 'deleted_column')
274
+
275
+ @twitter.get_range(:Statuses, :count => 2) do |key, columns|
276
+ assert_equal columns.count, 2
277
+ end
278
+
279
+ end
280
+
262
281
  def test_get_range_block
263
282
  k = key
264
283
  5.times do |i|
@@ -275,6 +294,19 @@ class CassandraTest < Test::Unit::TestCase
275
294
  assert_equal [],values
276
295
 
277
296
  end
297
+
298
+ def test_get_range_reversed
299
+ data = 3.times.map { |i| ["body-#{i.to_s}", "v"] }
300
+ hash = Cassandra::OrderedHash[data]
301
+ reversed_hash = Cassandra::OrderedHash[data.reverse]
302
+
303
+ @twitter.insert(:Statuses, "all-keys", hash)
304
+
305
+ columns = @twitter.get_range(:Statuses, :reversed => true)["all-keys"]
306
+ columns.each do |column|
307
+ assert_equal reversed_hash.shift, column
308
+ end
309
+ end
278
310
 
279
311
  def test_each_key
280
312
  k = key
@@ -322,7 +354,7 @@ class CassandraTest < Test::Unit::TestCase
322
354
 
323
355
  keys_yielded = []
324
356
  @twitter.each(:Statuses, :columns => ['single_column_lookup'], :batch_size => 5) do |key, columns|
325
- assert_equal key_columns[key].reject {|k,v| k != 'single_column_lookup'}, columns
357
+ assert_equal key_columns[key].reject {|k2,v| k2 != 'single_column_lookup'}, columns
326
358
  keys_yielded << key
327
359
  end
328
360
 
@@ -456,8 +488,13 @@ class CassandraTest < Test::Unit::TestCase
456
488
  end
457
489
 
458
490
  def test_count_columns
459
- @twitter.insert(:Statuses, key, {'body' => 'v1', 'user' => 'v2'})
460
- assert_equal 2, @twitter.count_columns(:Statuses, key)
491
+ columns = (1..200).inject(Hash.new){|h,v| h['column' + v.to_s] = v.to_s; h;}
492
+
493
+ @twitter.insert(:Statuses, key, columns)
494
+ assert_equal 200, @twitter.count_columns(:Statuses, key, :count => 200)
495
+ assert_equal 100, @twitter.count_columns(:Statuses, key)
496
+ assert_equal 55, @twitter.count_columns(:Statuses, key, :count => 55)
497
+
461
498
  end
462
499
 
463
500
  def test_count_super_columns
@@ -510,7 +547,7 @@ class CassandraTest < Test::Unit::TestCase
510
547
  @twitter.remove(:Users, k + '1') # Full row
511
548
  assert_equal({'body' => 'v1', 'user' => 'v1'}, @twitter.get(:Users, k + '1')) # Not yet removed
512
549
 
513
- @twitter.remove(:Users, k +'0', 'delete_me') # A single column of the row
550
+ @twitter.remove(:Users, k + '0', 'delete_me') # A single column of the row
514
551
  assert_equal({'delete_me' => 'v0', 'keep_me' => 'v0'}, @twitter.get(:Users, k + '0')) # Not yet removed
515
552
 
516
553
  @twitter.remove(:Users, k + '4')
@@ -542,7 +579,7 @@ class CassandraTest < Test::Unit::TestCase
542
579
  assert_equal({'body' => 'v'}.keys.sort, @twitter.get(:Statuses, k + '3').timestamps.keys.sort) # Written
543
580
 
544
581
  # Final result: initial_subcolumns - initial_subcolumns.first + new_subcolumns
545
- resulting_subcolumns = initial_subcolumns.merge(new_subcolumns).reject{|k,v| k == subcolumn_to_delete }
582
+ resulting_subcolumns = initial_subcolumns.merge(new_subcolumns).reject{|k2,v| k2 == subcolumn_to_delete }
546
583
  assert_equal(resulting_subcolumns, @twitter.get(:StatusRelationships, key, 'user_timelines'))
547
584
  assert_equal({}, @twitter.get(:StatusRelationships, key, 'dummy_supercolumn')) # dummy supercolumn deleted
548
585
 
@@ -555,7 +592,7 @@ class CassandraTest < Test::Unit::TestCase
555
592
  end
556
593
 
557
594
  def test_nil_sub_column_value
558
- @twitter.insert(:Index, 'asdf', {"thing" => {'jkl' => ''} })
595
+ @twitter.insert(:Indexes, 'asdf', {"thing" => {'jkl' => ''} })
559
596
  end
560
597
 
561
598
  def test_disconnect!
@@ -721,6 +758,41 @@ class CassandraTest < Test::Unit::TestCase
721
758
  idx_clause = @twitter.create_idx_clause(idx_expr)
722
759
  assert_equal(5, @twitter.get_indexed_slices(:Statuses, idx_clause).length)
723
760
  end
761
+
762
+ def test_column_family_mutation
763
+ k = key
764
+
765
+ if @twitter.column_families.include?(k)
766
+ @twitter.drop_column_family(k)
767
+ end
768
+
769
+ # Verify add_column_family works as desired.
770
+ @twitter.add_column_family(
771
+ Cassandra::ColumnFamily.new(
772
+ :keyspace => 'Twitter',
773
+ :name => k
774
+ )
775
+ )
776
+ assert @twitter.column_families.include?(k)
777
+
778
+ if CASSANDRA_VERSION.to_f == 0.7
779
+ # Verify rename_column_family works properly
780
+ @twitter.rename_column_family(k, k + '_renamed')
781
+ assert @twitter.column_families.include?(k + '_renamed')
782
+
783
+ # Change it back and validate
784
+ @twitter.rename_column_family(k + '_renamed', k)
785
+ assert @twitter.column_families.include?(k)
786
+ end
787
+
788
+ temp_cf_def = @twitter.column_families[k]
789
+ temp_cf_def.comment = k
790
+ @twitter.update_column_family(temp_cf_def)
791
+ assert @twitter.column_families.include?(k)
792
+
793
+ @twitter.drop_column_family(k)
794
+ assert !@twitter.column_families.include?(k)
795
+ end
724
796
  end
725
797
 
726
798
  if CASSANDRA_VERSION.to_f >= 0.8
@@ -198,6 +198,11 @@ class OrderedHashTestInt < Test::Unit::TestCase
198
198
  assert_same original, @ordered_hash
199
199
  assert_equal @other_ordered_hash.keys, @ordered_hash.keys
200
200
  end
201
+
202
+ def test_reverse
203
+ assert_equal @keys.reverse, @ordered_hash.reverse.keys
204
+ assert_equal @values.reverse, @ordered_hash.reverse.values
205
+ end
201
206
  end
202
207
 
203
208
  class OrderedHashTest < Test::Unit::TestCase
@@ -1,4 +1,4 @@
1
- CASSANDRA_VERSION = ENV['CASSANDRA_VERSION'] || '0.7' unless defined?(CASSANDRA_VERSION)
1
+ CASSANDRA_VERSION = ENV['CASSANDRA_VERSION'] || '0.8' unless defined?(CASSANDRA_VERSION)
2
2
 
3
3
  require 'test/unit'
4
4
  require "#{File.expand_path(File.dirname(__FILE__))}/../lib/cassandra/#{CASSANDRA_VERSION}"
@@ -6,7 +6,6 @@
6
6
 
7
7
  require 'thrift'
8
8
  require 'cassandra_types'
9
- require 'cassandra_constants'
10
9
 
11
10
  module CassandraThrift
12
11
  module Cassandra
@@ -13,8 +13,10 @@ module CassandraThrift
13
13
  EACH_QUORUM = 4
14
14
  ALL = 5
15
15
  ANY = 6
16
- VALUE_MAP = {1 => "ONE", 2 => "QUORUM", 3 => "LOCAL_QUORUM", 4 => "EACH_QUORUM", 5 => "ALL", 6 => "ANY"}
17
- VALID_VALUES = Set.new([ONE, QUORUM, LOCAL_QUORUM, EACH_QUORUM, ALL, ANY]).freeze
16
+ TWO = 7
17
+ THREE = 8
18
+ VALUE_MAP = {1 => "ONE", 2 => "QUORUM", 3 => "LOCAL_QUORUM", 4 => "EACH_QUORUM", 5 => "ALL", 6 => "ANY", 7 => "TWO", 8 => "THREE"}
19
+ VALID_VALUES = Set.new([ONE, QUORUM, LOCAL_QUORUM, EACH_QUORUM, ALL, ANY, TWO, THREE]).freeze
18
20
  end
19
21
 
20
22
  module IndexOperator
@@ -745,6 +745,7 @@ module CassandraThrift
745
745
  STRATEGY_OPTIONS = 3
746
746
  REPLICATION_FACTOR = 4
747
747
  CF_DEFS = 5
748
+ DURABLE_WRITES = 6
748
749
 
749
750
  FIELDS = {
750
751
  NAME => {:type => ::Thrift::Types::STRING, :name => 'name'},
@@ -752,7 +753,8 @@ module CassandraThrift
752
753
  STRATEGY_OPTIONS => {:type => ::Thrift::Types::MAP, :name => 'strategy_options', :key => {:type => ::Thrift::Types::STRING}, :value => {:type => ::Thrift::Types::STRING}, :optional => true},
753
754
  # @deprecated
754
755
  REPLICATION_FACTOR => {:type => ::Thrift::Types::I32, :name => 'replication_factor', :optional => true},
755
- CF_DEFS => {:type => ::Thrift::Types::LIST, :name => 'cf_defs', :element => {:type => ::Thrift::Types::STRUCT, :class => CassandraThrift::CfDef}}
756
+ CF_DEFS => {:type => ::Thrift::Types::LIST, :name => 'cf_defs', :element => {:type => ::Thrift::Types::STRUCT, :class => CassandraThrift::CfDef}},
757
+ DURABLE_WRITES => {:type => ::Thrift::Types::BOOL, :name => 'durable_writes', :default => true, :optional => true}
756
758
  }
757
759
 
758
760
  def struct_fields; FIELDS; end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cassandra
3
3
  version: !ruby/object:Gem::Version
4
- hash: 59
4
+ hash: 47
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
- - 11
9
- - 4
10
- version: 0.11.4
8
+ - 12
9
+ - 0
10
+ version: 0.12.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Evan Weaver, Ryan King
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2011-07-22 00:00:00 Z
18
+ date: 2011-08-22 00:00:00 Z
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
21
21
  name: thrift_client
@@ -25,12 +25,12 @@ dependencies:
25
25
  requirements:
26
26
  - - ">="
27
27
  - !ruby/object:Gem::Version
28
- hash: 1
28
+ hash: 3
29
29
  segments:
30
30
  - 0
31
- - 6
32
- - 3
33
- version: 0.6.3
31
+ - 7
32
+ - 0
33
+ version: 0.7.0
34
34
  type: :runtime
35
35
  version_requirements: *id001
36
36
  - !ruby/object:Gem::Dependency