hallelujah-cassandra 0.12.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (92) hide show
  1. data/CHANGELOG +111 -0
  2. data/Gemfile +8 -0
  3. data/LICENSE +202 -0
  4. data/Manifest +91 -0
  5. data/README.md +352 -0
  6. data/Rakefile +171 -0
  7. data/bin/cassandra_helper +16 -0
  8. data/conf/0.6/cassandra.in.sh +47 -0
  9. data/conf/0.6/log4j.properties +38 -0
  10. data/conf/0.6/schema.json +57 -0
  11. data/conf/0.6/storage-conf.xml +352 -0
  12. data/conf/0.7/cassandra.in.sh +46 -0
  13. data/conf/0.7/cassandra.yaml +336 -0
  14. data/conf/0.7/log4j-server.properties +41 -0
  15. data/conf/0.7/schema.json +57 -0
  16. data/conf/0.7/schema.txt +45 -0
  17. data/conf/0.8/cassandra.in.sh +41 -0
  18. data/conf/0.8/cassandra.yaml +61 -0
  19. data/conf/0.8/log4j-server.properties +40 -0
  20. data/conf/0.8/schema.json +69 -0
  21. data/conf/0.8/schema.txt +51 -0
  22. data/conf/1.0/cassandra.in.sh +41 -0
  23. data/conf/1.0/cassandra.yaml +415 -0
  24. data/conf/1.0/log4j-server.properties +40 -0
  25. data/conf/1.0/schema.json +69 -0
  26. data/conf/1.0/schema.txt +51 -0
  27. data/conf/1.1/cassandra.in.sh +41 -0
  28. data/conf/1.1/cassandra.yaml +560 -0
  29. data/conf/1.1/log4j-server.properties +44 -0
  30. data/conf/1.1/schema.json +69 -0
  31. data/conf/1.1/schema.txt +51 -0
  32. data/hallelujah-cassandra.gemspec +43 -0
  33. data/lib/cassandra.rb +39 -0
  34. data/lib/cassandra/0.6.rb +7 -0
  35. data/lib/cassandra/0.6/cassandra.rb +113 -0
  36. data/lib/cassandra/0.6/columns.rb +78 -0
  37. data/lib/cassandra/0.6/protocol.rb +90 -0
  38. data/lib/cassandra/0.7.rb +7 -0
  39. data/lib/cassandra/0.7/cassandra.rb +2 -0
  40. data/lib/cassandra/0.7/columns.rb +4 -0
  41. data/lib/cassandra/0.7/protocol.rb +5 -0
  42. data/lib/cassandra/0.8.rb +7 -0
  43. data/lib/cassandra/0.8/cassandra.rb +25 -0
  44. data/lib/cassandra/0.8/columns.rb +28 -0
  45. data/lib/cassandra/0.8/protocol.rb +10 -0
  46. data/lib/cassandra/1.0.rb +7 -0
  47. data/lib/cassandra/1.0/cassandra.rb +25 -0
  48. data/lib/cassandra/1.0/columns.rb +28 -0
  49. data/lib/cassandra/1.0/protocol.rb +12 -0
  50. data/lib/cassandra/1.1.rb +6 -0
  51. data/lib/cassandra/1.1/cassandra.rb +25 -0
  52. data/lib/cassandra/1.1/columns.rb +28 -0
  53. data/lib/cassandra/1.1/protocol.rb +12 -0
  54. data/lib/cassandra/array.rb +8 -0
  55. data/lib/cassandra/cassandra.rb +1070 -0
  56. data/lib/cassandra/column_family.rb +3 -0
  57. data/lib/cassandra/columns.rb +147 -0
  58. data/lib/cassandra/comparable.rb +28 -0
  59. data/lib/cassandra/composite.rb +118 -0
  60. data/lib/cassandra/constants.rb +11 -0
  61. data/lib/cassandra/debug.rb +9 -0
  62. data/lib/cassandra/helpers.rb +41 -0
  63. data/lib/cassandra/keyspace.rb +3 -0
  64. data/lib/cassandra/long.rb +58 -0
  65. data/lib/cassandra/mock.rb +511 -0
  66. data/lib/cassandra/ordered_hash.rb +192 -0
  67. data/lib/cassandra/protocol.rb +120 -0
  68. data/lib/cassandra/time.rb +11 -0
  69. data/test/cassandra_client_test.rb +20 -0
  70. data/test/cassandra_mock_test.rb +122 -0
  71. data/test/cassandra_test.rb +922 -0
  72. data/test/comparable_types_test.rb +45 -0
  73. data/test/composite_type_test.rb +29 -0
  74. data/test/eventmachine_test.rb +42 -0
  75. data/test/ordered_hash_test.rb +386 -0
  76. data/test/test_helper.rb +15 -0
  77. data/vendor/0.6/gen-rb/cassandra.rb +1481 -0
  78. data/vendor/0.6/gen-rb/cassandra_constants.rb +12 -0
  79. data/vendor/0.6/gen-rb/cassandra_types.rb +482 -0
  80. data/vendor/0.7/gen-rb/cassandra.rb +1936 -0
  81. data/vendor/0.7/gen-rb/cassandra_constants.rb +12 -0
  82. data/vendor/0.7/gen-rb/cassandra_types.rb +681 -0
  83. data/vendor/0.8/gen-rb/cassandra.rb +2215 -0
  84. data/vendor/0.8/gen-rb/cassandra_constants.rb +12 -0
  85. data/vendor/0.8/gen-rb/cassandra_types.rb +824 -0
  86. data/vendor/1.0/gen-rb/cassandra.rb +2215 -0
  87. data/vendor/1.0/gen-rb/cassandra_constants.rb +12 -0
  88. data/vendor/1.0/gen-rb/cassandra_types.rb +857 -0
  89. data/vendor/1.1/gen-rb/cassandra.rb +2571 -0
  90. data/vendor/1.1/gen-rb/cassandra_constants.rb +12 -0
  91. data/vendor/1.1/gen-rb/cassandra_types.rb +928 -0
  92. metadata +260 -0
@@ -0,0 +1,3 @@
1
+ class Cassandra
2
+ class ColumnFamily < CassandraThrift::CfDef ; end
3
+ end
@@ -0,0 +1,147 @@
1
+
2
+ class Cassandra
3
+ # A bunch of crap, mostly related to introspecting on column types
4
+ module Columns #:nodoc:
5
+ private
6
+
7
+ def is_super(column_family)
8
+ @is_super[column_family] ||= column_family_property(column_family, 'column_type') == "Super"
9
+ end
10
+
11
+ def column_name_class(column_family)
12
+ @column_name_class[column_family] ||= column_name_class_for_key(column_family, "comparator_type")
13
+ end
14
+
15
+ def sub_column_name_class(column_family)
16
+ @sub_column_name_class[column_family] ||= column_name_class_for_key(column_family, "subcomparator_type")
17
+ end
18
+
19
+ def column_name_class_for_key(column_family, comparator_key)
20
+ property = column_family_property(column_family, comparator_key)
21
+ property =~ /[^(]*\.(.*?)$/
22
+ case $1
23
+ when "LongType" then Long
24
+ when "LexicalUUIDType", "TimeUUIDType" then SimpleUUID::UUID
25
+ when /^CompositeType\(/ then Composite
26
+ else
27
+ String # UTF8, Ascii, Bytes, anything else
28
+ end
29
+ end
30
+
31
+ def column_family_property(column_family, key)
32
+ cfdef = schema.cf_defs.find {|cfdef| cfdef.name == column_family }
33
+ unless cfdef
34
+ raise AccessError, "Invalid column family \"#{column_family}\""
35
+ end
36
+ cfdef.send(key)
37
+ end
38
+
39
+ def multi_key_slices_to_hash(column_family, array, return_empty_rows = false)
40
+ ret = OrderedHash.new
41
+ array.each do |value|
42
+ next if return_empty_rows == false && value.columns.length == 0
43
+ ret[value.key] = columns_to_hash(column_family, value.columns)
44
+ end
45
+ ret
46
+ end
47
+
48
+ def multi_column_to_hash!(hash)
49
+ hash.each do |key, column_or_supercolumn|
50
+ hash[key] = (column_or_supercolumn.column.value if column_or_supercolumn.column)
51
+ end
52
+ end
53
+
54
+ def multi_columns_to_hash!(column_family, hash)
55
+ hash.each do |key, columns|
56
+ hash[key] = columns_to_hash(column_family, columns)
57
+ end
58
+ end
59
+
60
+ def multi_sub_columns_to_hash!(column_family, hash)
61
+ hash.each do |key, sub_columns|
62
+ hash[key] = sub_columns_to_hash(column_family, sub_columns)
63
+ end
64
+ end
65
+
66
+ def columns_to_hash(column_family, columns)
67
+ columns_to_hash_for_classes(columns, column_name_class(column_family), sub_column_name_class(column_family))
68
+ end
69
+
70
+ def sub_columns_to_hash(column_family, columns)
71
+ columns_to_hash_for_classes(columns, sub_column_name_class(column_family))
72
+ end
73
+
74
+ def columns_to_hash_for_classes(columns, column_name_class, sub_column_name_class = nil)
75
+ hash = OrderedHash.new
76
+ Array(columns).each do |c|
77
+ c = c.super_column || c.column || c.counter_column || c.counter_super_column if c.is_a?(CassandraThrift::ColumnOrSuperColumn)
78
+ case c
79
+ when CassandraThrift::CounterSuperColumn
80
+ hash.[]=(column_name_class.new(c.name), columns_to_hash_for_classes(c.columns, sub_column_name_class)) # Pop the class stack, and recurse
81
+ when CassandraThrift::SuperColumn
82
+ hash.[]=(column_name_class.new(c.name), columns_to_hash_for_classes(c.columns, sub_column_name_class)) # Pop the class stack, and recurse
83
+ when CassandraThrift::Column
84
+ hash.[]=(column_name_class.new(c.name), c.value, c.timestamp)
85
+ when CassandraThrift::CounterColumn
86
+ hash.[]=(column_name_class.new(c.name), c.value, 0)
87
+ end
88
+ end
89
+ hash
90
+ end
91
+
92
+ def _standard_insert_mutation(column_family, column_name, value, timestamp, ttl = nil)
93
+ CassandraThrift::Mutation.new(
94
+ :column_or_supercolumn => CassandraThrift::ColumnOrSuperColumn.new(
95
+ :column => CassandraThrift::Column.new(
96
+ :name => column_name_class(column_family).new(column_name).to_s,
97
+ :value => value,
98
+ :timestamp => timestamp,
99
+ :ttl => ttl
100
+ )
101
+ )
102
+ )
103
+ end
104
+
105
+ def _super_insert_mutation(column_family, super_column_name, sub_columns, timestamp, ttl = nil)
106
+ CassandraThrift::Mutation.new(:column_or_supercolumn =>
107
+ CassandraThrift::ColumnOrSuperColumn.new(
108
+ :super_column => CassandraThrift::SuperColumn.new(
109
+ :name => column_name_class(column_family).new(super_column_name).to_s,
110
+ :columns => sub_columns.collect { |sub_column_name, sub_column_value|
111
+ CassandraThrift::Column.new(
112
+ :name => sub_column_name_class(column_family).new(sub_column_name).to_s,
113
+ :value => sub_column_value.to_s,
114
+ :timestamp => timestamp,
115
+ :ttl => ttl
116
+ )
117
+ }
118
+ )
119
+ )
120
+ )
121
+ end
122
+
123
+ # General info about a deletion object within a mutation
124
+ # timestamp - required. If this is the only param, it will cause deletion of the whole key at that TS
125
+ # supercolumn - opt. If passed, the deletes will only occur within that supercolumn (only subcolumns
126
+ # will be deleted). Otherwise the normal columns will be deleted.
127
+ # predicate - opt. Defines how to match the columns to delete. if supercolumn passed, the slice will
128
+ # be scoped to subcolumns of that supercolumn.
129
+
130
+ # Deletes a single column from the containing key/CF (and possibly supercolumn), at a given timestamp.
131
+ # Although mutations (as opposed to 'remove' calls) support deleting slices and lists of columns in one shot, this is not implemented here.
132
+ # 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
133
+ # methods to use delete mutation calls...although that might have performance implications. We'll leave that refactoring for later.
134
+ def _delete_mutation(cf, column, subcolumn, timestamp, options={})
135
+ deletion_hash = {:timestamp => timestamp}
136
+ if is_super(cf)
137
+ deletion_hash[:super_column] = column if column
138
+ deletion_hash[:predicate] = CassandraThrift::SlicePredicate.new(:column_names => [subcolumn]) if subcolumn
139
+ else
140
+ deletion_hash[:predicate] = CassandraThrift::SlicePredicate.new(:column_names => [column]) if column
141
+ end
142
+ CassandraThrift::Mutation.new(
143
+ :deletion => CassandraThrift::Deletion.new(deletion_hash)
144
+ )
145
+ end
146
+ end
147
+ end
@@ -0,0 +1,28 @@
1
+
2
+ class Cassandra
3
+ # Abstract base class for comparable numeric column name types
4
+ class Comparable
5
+ class TypeError < ::TypeError #:nodoc:
6
+ end
7
+
8
+ def <=>(other)
9
+ self.to_i <=> other.to_i
10
+ end
11
+
12
+ def hash
13
+ @bytes.hash
14
+ end
15
+
16
+ def eql?(other)
17
+ other.is_a?(Comparable) and @bytes == other.to_s
18
+ end
19
+
20
+ def ==(other)
21
+ other.respond_to?(:to_i) && self.to_i == other.to_i
22
+ end
23
+
24
+ def to_s
25
+ @bytes
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,118 @@
1
+
2
+ class Cassandra
3
+ class Composite
4
+ include ::Comparable
5
+ attr_reader :parts
6
+ attr_reader :column_slice
7
+
8
+ def initialize(*parts)
9
+ options = {}
10
+ if parts.last.is_a?(Hash)
11
+ options = parts.pop
12
+ end
13
+ @column_slice = options[:slice]
14
+ raise ArgumentError if @column_slice != nil && ![:before, :after].include?(@column_slice)
15
+
16
+ if parts.length == 1 && parts[0].instance_of?(self.class)
17
+ @column_slice = parts[0].column_slice
18
+ @parts = parts[0].parts
19
+ elsif parts.length == 1 && parts[0].instance_of?(String) && @column_slice.nil? && valid_packed_composite?(parts[0])
20
+ unpack(parts[0])
21
+ else
22
+ @parts = parts
23
+ end
24
+ end
25
+
26
+ def [](*args)
27
+ return @parts[*args]
28
+ end
29
+
30
+ def pack
31
+ packed = @parts.map do |part|
32
+ [part.length].pack('n') + part + "\x00"
33
+ end
34
+ if @column_slice
35
+ part = @parts[-1]
36
+ packed[-1] = [part.length].pack('n') + part + slice_end_of_component
37
+ end
38
+ return packed.join('')
39
+ end
40
+
41
+ def to_s
42
+ return pack
43
+ end
44
+
45
+ def <=>(other)
46
+ if !other.instance_of?(self.class)
47
+ return @parts.first <=> other
48
+ end
49
+ eoc = slice_end_of_component.unpack('c')[0]
50
+ other_eoc = other.slice_end_of_component.unpack('c')[0]
51
+ @parts.zip(other.parts).each do |a, b|
52
+ next if a == b
53
+ if a.nil? && b.nil?
54
+ return eoc <=> other_eoc
55
+ end
56
+
57
+ if a.nil?
58
+ return @column_slice == :after ? 1 : -1
59
+ end
60
+ if b.nil?
61
+ return other.column_slice == :after ? -1 : 1
62
+ end
63
+ return -1 if a < b
64
+ return 1 if a > b
65
+ end
66
+ return 0
67
+ end
68
+
69
+ def inspect
70
+ return "#<Composite:#{@column_slice} #{@parts.inspect}>"
71
+ end
72
+
73
+ def slice_end_of_component
74
+ return "\x01" if @column_slice == :after
75
+ return "\xFF" if @column_slice == :before
76
+ return "\x00"
77
+ end
78
+
79
+ private
80
+ def unpack(packed_string)
81
+ parts = []
82
+ end_of_component = nil
83
+ while packed_string.length > 0
84
+ length = packed_string.slice(0, 2).unpack('n')[0]
85
+ parts << packed_string.slice(2, length)
86
+ end_of_component = packed_string.slice(2 + length, 1)
87
+
88
+ packed_string = packed_string.slice(3 + length, packed_string.length)
89
+ end
90
+ @column_slice = :after if end_of_component == "\x01"
91
+ @column_slice = :before if end_of_component == "\xFF"
92
+ @parts = parts
93
+ end
94
+
95
+ def valid_packed_composite?(packed_string)
96
+ while packed_string.length > 0
97
+ length = packed_string.slice(0, 2).unpack('n')[0]
98
+ return false if length.nil? || length + 3 > packed_string.length
99
+
100
+ end_of_component = packed_string.slice(2 + length, 1)
101
+ if length + 3 != packed_string.length
102
+ return false if end_of_component != "\x00"
103
+ end
104
+
105
+ packed_string = packed_string.slice(3 + length, packed_string.length)
106
+ end
107
+ return true
108
+ end
109
+
110
+ def hash
111
+ return to_s.hash
112
+ end
113
+
114
+ def eql?(other)
115
+ return to_s == other.to_s
116
+ end
117
+ end
118
+ end
@@ -0,0 +1,11 @@
1
+
2
+ class Cassandra
3
+ # A helper module you can include in your own class. Makes it easier
4
+ # to work with Cassandra subclasses.
5
+ module Constants
6
+ include Cassandra::Consistency
7
+
8
+ Long = Cassandra::Long
9
+ OrderedHash = Cassandra::OrderedHash
10
+ end
11
+ end
@@ -0,0 +1,9 @@
1
+
2
+ require 'pp'
3
+
4
+ class CassandraThrift::Cassandra::Client
5
+ def send_message(*args)
6
+ pp args
7
+ super
8
+ end
9
+ end
@@ -0,0 +1,41 @@
1
+ class Cassandra
2
+ module Helpers
3
+ def extract_and_validate_params(column_family, keys, args, options)
4
+ options = options.dup
5
+ column_family = column_family.to_s
6
+ # Keys
7
+ [keys].flatten.each do |key|
8
+ raise ArgumentError, "Key #{key.inspect} must be a String for #{caller[2].inspect}." unless key.is_a?(String)
9
+ end
10
+
11
+ # Options
12
+ if args.last.is_a?(Hash)
13
+ extras = args.last.keys - options.keys
14
+ raise ArgumentError, "Invalid options #{extras.inspect[1..-2]} for #{caller[1]}" if extras.any?
15
+ options.merge!(args.pop)
16
+ end
17
+
18
+ # Ranges
19
+ column, sub_column = args[0], args[1]
20
+ raise ArgumentError, "Invalid arguments: subcolumns specified for a non-supercolumn family" if sub_column && !is_super(column_family)
21
+ klass, sub_klass = column_name_class(column_family), sub_column_name_class(column_family)
22
+ range_class = column ? sub_klass : klass
23
+
24
+ [:start, :finish].each do |opt|
25
+ options[opt] = options[opt] ? range_class.new(options[opt]).to_s : ''
26
+ end
27
+
28
+ [column_family, s_map(column, klass), s_map(sub_column, sub_klass), options]
29
+ end
30
+
31
+ # Convert stuff to strings.
32
+ def s_map(el, klass)
33
+ case el
34
+ when Array then el.map { |i| s_map(i, klass) }
35
+ when NilClass then nil
36
+ else
37
+ klass.new(el).to_s
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,3 @@
1
+ class Cassandra
2
+ class Keyspace < CassandraThrift::KsDef ; end
3
+ end
@@ -0,0 +1,58 @@
1
+
2
+ class Cassandra
3
+ # A temporally-ordered Long class for use in Cassandra column names
4
+ class Long < Comparable
5
+
6
+ # FIXME Should unify with or subclass Cassandra::UUID
7
+ def initialize(bytes = nil)
8
+ case bytes
9
+ when self.class # Long
10
+ @bytes = bytes.to_s
11
+ when String
12
+ case bytes.size
13
+ when 8 # Raw byte array
14
+ @bytes = bytes
15
+ when 18 # Human-readable UUID-like representation; inverse of #to_guid
16
+ elements = bytes.split("-")
17
+ raise TypeError, "Expected #{bytes.inspect} to cast to a #{self.class} (malformed UUID-like representation)" if elements.size != 3
18
+ @bytes = [elements.join].pack('H32')
19
+ else
20
+ raise TypeError, "Expected #{bytes.inspect} to cast to a #{self.class} (invalid bytecount)"
21
+ end
22
+ when Integer
23
+ raise TypeError, "Expected #{bytes.inspect} to cast to a #{self.class} (integer out of range)" if bytes < 0 or bytes > 2**64
24
+ @bytes = [bytes >> 32, bytes % 2**32].pack("NN")
25
+ when NilClass, Time
26
+ # Time.stamp is 52 bytes, so we have 12 bytes of entropy left over
27
+ int = ((bytes || Time).stamp << 12) + rand(2**12)
28
+ @bytes = [int >> 32, int % 2**32].pack("NN")
29
+ else
30
+ raise TypeError, "Expected #{bytes.inspect} to cast to a #{self.class} (unknown source class)"
31
+ end
32
+ end
33
+
34
+ def to_i
35
+ @to_i ||= begin
36
+ ints = @bytes.unpack("NN")
37
+ (ints[0] << 32) +
38
+ ints[1]
39
+ end
40
+ end
41
+
42
+ def to_guid
43
+ "%08x-%04x-%04x" % @bytes.unpack("Nnn")
44
+ end
45
+
46
+ def inspect
47
+ "<Cassandra::Long##{object_id} time: #{
48
+ Time.at((to_i >> 12) / 1_000_000).utc.inspect
49
+ }, usecs: #{
50
+ (to_i >> 12) % 1_000_000
51
+ }, jitter: #{
52
+ to_i % 2**12
53
+ }, guid: #{
54
+ to_guid
55
+ }>"
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,511 @@
1
+ class SimpleUUID::UUID
2
+ def >=(other)
3
+ (self <=> other) >= 0
4
+ end
5
+
6
+ def <=(other)
7
+ (self <=> other) <= 0
8
+ end
9
+ end
10
+
11
+ class Cassandra
12
+ class Mock
13
+ include ::Cassandra::Helpers
14
+ include ::Cassandra::Columns
15
+
16
+ attr_reader :keyspace
17
+
18
+ def initialize(keyspace, schema)
19
+ @is_super = {}
20
+ @keyspace = keyspace
21
+ @column_name_class = {}
22
+ @sub_column_name_class = {}
23
+ @indexes = {}
24
+ @schema = schema[keyspace]
25
+ clear_keyspace!
26
+ end
27
+
28
+ def disconnect!
29
+ end
30
+
31
+ def clear_keyspace!
32
+ @data = {}
33
+ end
34
+
35
+ def clear_column_family!(column_family)
36
+ @data[column_family.to_sym] = OrderedHash.new
37
+ end
38
+
39
+ def default_write_consistency=(value)
40
+ WRITE_DEFAULTS[:consistency] = value
41
+ end
42
+
43
+ def default_read_consistency=(value)
44
+ READ_DEFAULTS[:consistency] = value
45
+ end
46
+
47
+ def insert(column_family, key, hash_or_array, options = {})
48
+ if @batch
49
+ @batch << [:insert, column_family, key, hash_or_array, options]
50
+ else
51
+ raise ArgumentError if key.nil?
52
+ if !is_super(column_family)
53
+ insert_standard(column_family, key, hash_or_array)
54
+ else
55
+ insert_super(column_family, key, hash_or_array)
56
+ end
57
+ end
58
+ end
59
+
60
+ def insert_standard(column_family, key, hash_or_array)
61
+ old = cf(column_family)[key] || OrderedHash.new
62
+ cf(column_family)[key] = merge_and_sort(old, hash_or_array)
63
+ end
64
+
65
+ def insert_super(column_family, key, hash)
66
+ raise ArgumentError unless hash.is_a?(Hash)
67
+ cf(column_family)[key] ||= OrderedHash.new
68
+
69
+ hash.keys.each do |sub_key|
70
+ old = cf(column_family)[key][sub_key] || OrderedHash.new
71
+ cf(column_family)[key][sub_key] = merge_and_sort(old, hash[sub_key])
72
+ end
73
+ end
74
+
75
+ def batch
76
+ @batch = []
77
+ yield
78
+ b = @batch
79
+ @batch = nil
80
+ b.each do |mutation|
81
+ send(*mutation)
82
+ end
83
+ ensure
84
+ @batch = nil
85
+ end
86
+
87
+ def get(column_family, key, *columns_and_options)
88
+ column_family, column, sub_column, options =
89
+ extract_and_validate_params_for_real(column_family, [key], columns_and_options, READ_DEFAULTS)
90
+ if !is_super(column_family)
91
+ get_standard(column_family, key, column, options)
92
+ else
93
+ get_super(column_family, key, column, sub_column, options)
94
+ end
95
+ end
96
+
97
+ def get_standard(column_family, key, column, options)
98
+ columns = cf(column_family)[key] || OrderedHash.new
99
+ row = columns_to_hash(column_family, columns)
100
+
101
+ if column
102
+ row[column]
103
+ else
104
+ row = apply_range(row, column_family, options[:start], options[:finish])
105
+ row = apply_count(row, options[:count], options[:reversed])
106
+ end
107
+ end
108
+
109
+ def get_super(column_family, key, column, sub_column, options)
110
+ columns = cf(column_family)[key] || OrderedHash.new
111
+ row = columns_to_hash(column_family, columns)
112
+
113
+ if column
114
+ if sub_column
115
+ if row[column] &&
116
+ row[column][sub_column]
117
+ row[column][sub_column]
118
+ else
119
+ nil
120
+ end
121
+ else
122
+ row = row[column] || OrderedHash.new
123
+ row = apply_range(row, column_family, options[:start], options[:finish], false)
124
+ row = apply_count(row, options[:count], options[:reversed])
125
+ end
126
+ else
127
+ row
128
+ end
129
+ end
130
+
131
+ def exists?(column_family, key, *columns_and_options)
132
+ column_family, column, sub_column, options = extract_and_validate_params_for_real(column_family, [key], columns_and_options, READ_DEFAULTS)
133
+ results = get(column_family, key, column, sub_column)
134
+
135
+ ![{}, nil].include?(results)
136
+ end
137
+
138
+ def multi_get(column_family, keys, *columns_and_options)
139
+ column_family, column, sub_column, options = extract_and_validate_params_for_real(column_family, keys, columns_and_options, READ_DEFAULTS)
140
+ keys.inject(OrderedHash.new) do |hash, key|
141
+ hash[key] = get(column_family, key)
142
+ hash
143
+ end
144
+ end
145
+
146
+ def remove(column_family, key, *columns_and_options)
147
+ column_family, column, sub_column, options = extract_and_validate_params_for_real(column_family, key, columns_and_options, WRITE_DEFAULTS)
148
+ if @batch
149
+ @batch << [:remove, column_family, key, column, sub_column]
150
+ else
151
+ if column
152
+ if sub_column
153
+ cf(column_family)[key][column].delete(sub_column.to_s) if cf(column_family)[key][column]
154
+ else
155
+ cf(column_family)[key].delete(column.to_s) if cf(column_family)[key]
156
+ end
157
+ else
158
+ cf(column_family).delete(key)
159
+ end
160
+ end
161
+ end
162
+
163
+ def get_columns(column_family, key, *columns_and_options)
164
+ column_family, columns, sub_columns, options = extract_and_validate_params_for_real(column_family, key, columns_and_options, READ_DEFAULTS)
165
+ d = get(column_family, key)
166
+
167
+ if sub_columns
168
+ sub_columns.collect do |sub_column|
169
+ d[columns][sub_column]
170
+ end
171
+ else
172
+ columns.collect do |column|
173
+ d[column]
174
+ end
175
+ end
176
+ end
177
+
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
182
+ end
183
+
184
+ def multi_get_columns(column_family, keys, columns)
185
+ keys.inject(OrderedHash.new) do |hash, key|
186
+ hash[key] = get_columns(column_family, key, columns)
187
+ hash
188
+ end
189
+ end
190
+
191
+ def multi_count_columns(column_family, keys)
192
+ keys.inject(OrderedHash.new) do |hash, key|
193
+ hash[key] = count_columns(column_family, key)
194
+ hash
195
+ end
196
+ end
197
+
198
+ def get_range(column_family, options = {}, &blk)
199
+ column_family, _, _, options = extract_and_validate_params_for_real(column_family, "", [options],
200
+ READ_DEFAULTS.merge(:start_key => nil,
201
+ :finish_key => nil,
202
+ :key_count => 100,
203
+ :columns => nil,
204
+ :reversed => false
205
+ )
206
+ )
207
+ res = _get_range(column_family,
208
+ options[:start_key],
209
+ options[:finish_key],
210
+ options[:key_count],
211
+ options[:columns],
212
+ options[:start],
213
+ options[:finish],
214
+ options[:count],
215
+ options[:consistency],
216
+ options[:reversed], &blk)
217
+
218
+ if blk.nil?
219
+ res
220
+ else
221
+ nil
222
+ end
223
+ end
224
+
225
+ def get_range_keys(column_family, options = {})
226
+ get_range(column_family,options.merge!(:columns => [])).keys
227
+ end
228
+
229
+ def count_range(column_family, options = {})
230
+ Hash[get_range(column_family, options).select{|k,v| v.length > 0}].keys.compact.length
231
+ end
232
+
233
+ def each_key(column_family, options = {})
234
+ each(column_family, options.merge!(:columns => [])) do |key, value|
235
+ yield key
236
+ end
237
+ end
238
+
239
+ def each(column_family, options = {})
240
+ batch_size = options.delete(:batch_size) || 100
241
+ count = options.delete(:key_count)
242
+ yielded_count = 0
243
+
244
+ options[:start_key] ||= ''
245
+ last_key = nil
246
+
247
+ while options[:start_key] != last_key && (count.nil? || count > yielded_count)
248
+ options[:start_key] = last_key
249
+ res = get_range(column_family, options.merge!(:start_key => last_key, :key_count => batch_size))
250
+ res.each do |key, columns|
251
+ next if options[:start_key] == key
252
+ next if yielded_count == count
253
+ yield key, columns
254
+ yielded_count += 1
255
+ last_key = key
256
+ end
257
+ end
258
+ end
259
+
260
+ def create_index(ks_name, cf_name, c_name, v_class)
261
+ if @indexes[ks_name] &&
262
+ @indexes[ks_name][cf_name] &&
263
+ @indexes[ks_name][cf_name][c_name]
264
+ nil
265
+
266
+ else
267
+ @indexes[ks_name] ||= {}
268
+ @indexes[ks_name][cf_name] ||= {}
269
+ @indexes[ks_name][cf_name][c_name] = true
270
+ end
271
+ end
272
+
273
+ def drop_index(ks_name, cf_name, c_name)
274
+ if @indexes[ks_name] &&
275
+ @indexes[ks_name][cf_name] &&
276
+ @indexes[ks_name][cf_name][c_name]
277
+
278
+ @indexes[ks_name][cf_name].delete(c_name)
279
+ else
280
+ nil
281
+ end
282
+ end
283
+
284
+ def create_index_expression(c_name, value, op)
285
+ {:column_name => c_name, :value => value, :comparison => op}
286
+ end
287
+ alias :create_idx_expr :create_index_expression
288
+
289
+ def create_index_clause(idx_expressions, start = "", count = 100)
290
+ {:start => start, :index_expressions => idx_expressions, :count => count, :type => :index_clause}
291
+ end
292
+ alias :create_idx_clause :create_index_clause
293
+
294
+ def get_indexed_slices(column_family, idx_clause, *columns_and_options)
295
+ column_family, columns, _, options =
296
+ extract_and_validate_params_for_real(column_family, [], columns_and_options, READ_DEFAULTS.merge(:key_count => 100, :key_start => ""))
297
+
298
+ unless [Hash, OrderedHash].include?(idx_clause.class) && idx_clause[:type] == :index_clause
299
+ idx_clause = create_index_clause(idx_clause, options[:key_start], options[:key_count])
300
+ end
301
+
302
+ ret = {}
303
+ cf(column_family).each do |key, row|
304
+ next if idx_clause[:start] != '' && key < idx_clause[:start]
305
+ next if ret.length == idx_clause[:count]
306
+
307
+ matches = []
308
+ idx_clause[:index_expressions].each do |expr|
309
+ next if row[expr[:column_name]].nil?
310
+ next unless row[expr[:column_name]].send(expr[:comparison].to_sym, expr[:value])
311
+
312
+ matches << expr
313
+ end
314
+
315
+ ret[key] = row if matches.length == idx_clause[:index_expressions].length
316
+ end
317
+
318
+ ret
319
+ end
320
+
321
+ def add(column_family, key, value, *columns_and_options)
322
+ column_family, column, sub_column, options = extract_and_validate_params_for_real(column_family, key, columns_and_options, WRITE_DEFAULTS)
323
+
324
+ if is_super(column_family)
325
+ cf(column_family)[key] ||= OrderedHash.new
326
+ cf(column_family)[key][column] ||= OrderedHash.new
327
+ cf(column_family)[key][column][sub_column] ||= 0
328
+ cf(column_family)[key][column][sub_column] += value
329
+ else
330
+ cf(column_family)[key] ||= OrderedHash.new
331
+ cf(column_family)[key][column] ||= 0
332
+ cf(column_family)[key][column] += value
333
+ end
334
+
335
+ nil
336
+ end
337
+
338
+ def column_families
339
+ cf_defs = {}
340
+ schema.each do |key, value|
341
+ cf_def = Cassandra::ColumnFamily.new
342
+
343
+ value.each do |property, property_value|
344
+ cf_def.send(:"#{property}=", property_value)
345
+ end
346
+
347
+ cf_defs[key] = cf_def
348
+ end
349
+
350
+ cf_defs
351
+ end
352
+
353
+ def schema(load=true)
354
+ @schema
355
+ end
356
+
357
+ def column_family_property(column_family, key)
358
+ schema[column_family.to_s][key]
359
+ end
360
+
361
+ def add_column_family(cf)
362
+ @schema[cf.name.to_s] ||= OrderedHash.new
363
+
364
+ cf.instance_variables.each do |var|
365
+ @schema[cf.name.to_s][var.slice(1..-1)] = cf.instance_variable_get(var)
366
+ end
367
+ end
368
+
369
+ def update_column_family(cf)
370
+ return false unless @schema.include?(cf.name.to_s)
371
+
372
+ cf.instance_variables.each do |var|
373
+ @schema[cf.name.to_s][var.slice(1..-1)] = cf.instance_variable_get(var)
374
+ end
375
+ end
376
+
377
+ def drop_column_family(column_family_name)
378
+ @schema.delete(column_family_name)
379
+ end
380
+
381
+ private
382
+
383
+ def schema_for_keyspace(keyspace)
384
+ @schema
385
+ end
386
+
387
+ def _get_range(column_family, start_key, finish_key, key_count, columns, start, finish, count, consistency, reversed, &blk)
388
+ ret = OrderedHash.new
389
+ start = to_compare_with_type(start, column_family)
390
+ finish = to_compare_with_type(finish, column_family)
391
+ cf(column_family).keys.sort.each do |key|
392
+ break if ret.keys.size >= key_count
393
+ if (start_key.nil? || key >= start_key) && (finish_key.nil? || key <= finish_key)
394
+ if columns
395
+ #ret[key] = columns.inject(OrderedHash.new){|hash, column_name| hash[column_name] = cf(column_family)[key][column_name]; hash;}
396
+ selected_hash = OrderedHash.new
397
+ cf(column_family)[key].each do |k, v|
398
+ selected_hash.[]=(k, v, cf(column_family)[key].timestamps[k]) if columns.include?(k)
399
+ end
400
+ ret[key] = columns_to_hash(column_family, selected_hash)
401
+ ret[key] = apply_count(ret[key], count, reversed)
402
+ blk.call(key,ret[key]) unless blk.nil?
403
+ else
404
+ #ret[key] = apply_range(cf(column_family)[key], column_family, start, finish, !is_super(column_family))
405
+ start, finish = finish, start if reversed
406
+ ret[key] = apply_range(columns_to_hash(column_family, cf(column_family)[key]), column_family, start, finish)
407
+ ret[key] = apply_count(ret[key], count, reversed)
408
+ blk.call(key,ret[key]) unless blk.nil?
409
+ end
410
+ end
411
+ end
412
+ ret
413
+ end
414
+
415
+ def extract_and_validate_params_for_real(column_family, keys, args, options)
416
+ column_family, columns, sub_column, options = extract_and_validate_params(column_family, keys, args, options)
417
+ options[:start] = nil if options[:start] == ''
418
+ options[:finish] = nil if options[:finish] == ''
419
+ [column_family, to_compare_with_types(columns, column_family), to_compare_with_types(sub_column, column_family, false), options]
420
+ end
421
+
422
+ def to_compare_with_types(column_names, column_family, standard=true)
423
+ if column_names.is_a?(Array)
424
+ column_names.collect do |name|
425
+ to_compare_with_type(name, column_family, standard)
426
+ end
427
+ else
428
+ to_compare_with_type(column_names, column_family, standard)
429
+ end
430
+ end
431
+
432
+ def to_compare_with_type(column_name, column_family, standard=true)
433
+ return column_name if column_name.nil?
434
+ klass = if standard
435
+ column_name_class(column_family)
436
+ else
437
+ sub_column_name_class(column_family)
438
+ end
439
+
440
+ klass.new(column_name)
441
+ end
442
+
443
+ def cf(column_family)
444
+ @data[column_family.to_sym] ||= OrderedHash.new
445
+ end
446
+
447
+ def merge_and_sort(old_stuff, new_stuff)
448
+ if new_stuff.is_a?(Array)
449
+ new_stuff = new_stuff.inject({}){|h,k| h[k] = nil; h }
450
+ end
451
+
452
+ new_stuff = new_stuff.to_a.inject({}){|h,k| h[k[0].to_s] = k[1]; h }
453
+
454
+ new_stuff.each { |k,v| old_stuff.[]=(k, v, (Time.now.to_f * 1000000).to_i) }
455
+ hash = OrderedHash.new
456
+ old_stuff.sort{ |a,b| a[0] <=> b[0] }.each do |k, v|
457
+ hash.[]=(k, v, old_stuff.timestamps[k])
458
+ end
459
+ hash
460
+ end
461
+
462
+ def columns_to_hash(column_family, columns)
463
+ column_class, sub_column_class = column_name_class(column_family), sub_column_name_class(column_family)
464
+ output = OrderedHash.new
465
+
466
+ columns.each do |column_name, value|
467
+ timestamp = columns.timestamps[column_name]
468
+ column = column_class.new(column_name)
469
+
470
+ if [Hash, OrderedHash].include?(value.class)
471
+ output[column] ||= OrderedHash.new
472
+ value.each do |sub_column, sub_column_value|
473
+ timestamp = value.timestamps[sub_column]
474
+ output[column].[]=(sub_column_class.new(sub_column), sub_column_value, timestamp)
475
+ end
476
+ else
477
+ output.[]=(column_class.new(column_name), value, timestamp)
478
+ end
479
+ end
480
+
481
+ output
482
+ end
483
+
484
+ def apply_count(row, count, reversed=false)
485
+ if count
486
+ keys = row.keys.sort
487
+ keys = keys.reverse if reversed
488
+ keys = keys[0...count]
489
+ keys.inject(OrderedHash.new) do |memo, key|
490
+ memo.[]=(key, row[key], row.timestamps[key])
491
+ memo
492
+ end
493
+ else
494
+ row
495
+ end
496
+ end
497
+
498
+ def apply_range(row, column_family, strt, fin, standard=true)
499
+ start = to_compare_with_type(strt, column_family, standard)
500
+ finish = to_compare_with_type(fin, column_family, standard)
501
+ ret = OrderedHash.new
502
+ row.keys.each do |key|
503
+ if (start.nil? || key >= start) && (finish.nil? || key <= finish)
504
+ ret.[]=(key, row[key], row.timestamps[key])
505
+ end
506
+ end
507
+ ret
508
+ end
509
+
510
+ end
511
+ end