cassandra 0.13.0 → 0.14.0
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.
- data/CHANGELOG +15 -0
- data/Gemfile +8 -0
- data/Manifest +18 -0
- data/Rakefile +2 -1
- data/cassandra.gemspec +13 -9
- data/conf/0.6/schema.json +2 -2
- data/conf/0.6/storage-conf.xml +18 -18
- data/conf/0.8/schema.json +3 -0
- data/conf/0.8/schema.txt +7 -1
- data/conf/1.0/cassandra.in.sh +41 -0
- data/conf/1.0/cassandra.yaml +415 -0
- data/conf/1.0/log4j-server.properties +40 -0
- data/conf/1.0/schema.json +72 -0
- data/conf/1.0/schema.txt +57 -0
- data/ext/cassandra_native.c +34 -0
- data/ext/extconf.rb +9 -0
- data/lib/cassandra.rb +1 -0
- data/lib/cassandra/0.6/protocol.rb +10 -9
- data/lib/cassandra/cassandra.rb +32 -25
- data/lib/cassandra/columns.rb +38 -13
- data/lib/cassandra/composite.rb +37 -15
- data/lib/cassandra/dynamic_composite.rb +96 -0
- data/lib/cassandra/mock.rb +23 -14
- data/test/cassandra_mock_test.rb +6 -0
- data/test/cassandra_test.rb +390 -49
- data/test/composite_type_test.rb +42 -7
- metadata +47 -21
data/lib/cassandra/columns.rb
CHANGED
@@ -16,12 +16,37 @@ class Cassandra
|
|
16
16
|
@sub_column_name_class[column_family] ||= column_name_class_for_key(column_family, "subcomparator_type")
|
17
17
|
end
|
18
18
|
|
19
|
+
def column_name_maker(column_family)
|
20
|
+
@column_name_maker[column_family] ||=
|
21
|
+
begin
|
22
|
+
klass = column_name_class(column_family)
|
23
|
+
if klass == Composite
|
24
|
+
lambda {|name| klass.new_from_packed(name) }
|
25
|
+
else
|
26
|
+
lambda {|name| klass.new(name) }
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def sub_column_name_maker(column_family)
|
32
|
+
@sub_column_name_maker[column_family] ||=
|
33
|
+
begin
|
34
|
+
klass = sub_column_name_class(column_family)
|
35
|
+
if klass == Composite
|
36
|
+
lambda {|name| klass.new_from_packed(name) }
|
37
|
+
else
|
38
|
+
lambda {|name| klass.new(name) }
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
19
43
|
def column_name_class_for_key(column_family, comparator_key)
|
20
44
|
property = column_family_property(column_family, comparator_key)
|
21
45
|
property =~ /[^(]*\.(.*?)$/
|
22
46
|
case $1
|
23
47
|
when "LongType" then Long
|
24
48
|
when "LexicalUUIDType", "TimeUUIDType" then SimpleUUID::UUID
|
49
|
+
when /^DynamicCompositeType\(/ then DynamicComposite
|
25
50
|
when /^CompositeType\(/ then Composite
|
26
51
|
else
|
27
52
|
String # UTF8, Ascii, Bytes, anything else
|
@@ -64,26 +89,26 @@ class Cassandra
|
|
64
89
|
end
|
65
90
|
|
66
91
|
def columns_to_hash(column_family, columns)
|
67
|
-
columns_to_hash_for_classes(columns,
|
92
|
+
columns_to_hash_for_classes(columns, column_name_maker(column_family), sub_column_name_maker(column_family))
|
68
93
|
end
|
69
94
|
|
70
95
|
def sub_columns_to_hash(column_family, columns)
|
71
|
-
columns_to_hash_for_classes(columns,
|
96
|
+
columns_to_hash_for_classes(columns, sub_column_name_maker(column_family))
|
72
97
|
end
|
73
98
|
|
74
|
-
def columns_to_hash_for_classes(columns,
|
99
|
+
def columns_to_hash_for_classes(columns, column_name_maker, sub_column_name_maker = nil)
|
75
100
|
hash = OrderedHash.new
|
76
101
|
Array(columns).each do |c|
|
77
102
|
c = c.super_column || c.column || c.counter_column || c.counter_super_column if c.is_a?(CassandraThrift::ColumnOrSuperColumn)
|
78
103
|
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
104
|
when CassandraThrift::SuperColumn
|
82
|
-
hash.[]=(
|
105
|
+
hash.[]=(column_name_maker.call(c.name), columns_to_hash_for_classes(c.columns, sub_column_name_maker)) # Pop the class stack, and recurse
|
83
106
|
when CassandraThrift::Column
|
84
|
-
hash.[]=(
|
107
|
+
hash.[]=(column_name_maker.call(c.name), c.value, c.timestamp)
|
85
108
|
when CassandraThrift::CounterColumn
|
86
|
-
hash.[]=(
|
109
|
+
hash.[]=(column_name_maker.call(c.name), c.value, 0)
|
110
|
+
when CassandraThrift::CounterSuperColumn
|
111
|
+
hash.[]=(column_name_maker.call(c.name), columns_to_hash_for_classes(c.columns, sub_column_name_maker)) # Pop the class stack, and recurse
|
87
112
|
end
|
88
113
|
end
|
89
114
|
hash
|
@@ -103,7 +128,7 @@ class Cassandra
|
|
103
128
|
end
|
104
129
|
|
105
130
|
def _super_insert_mutation(column_family, super_column_name, sub_columns, timestamp, ttl = nil)
|
106
|
-
CassandraThrift::Mutation.new(:column_or_supercolumn =>
|
131
|
+
CassandraThrift::Mutation.new(:column_or_supercolumn =>
|
107
132
|
CassandraThrift::ColumnOrSuperColumn.new(
|
108
133
|
:super_column => CassandraThrift::SuperColumn.new(
|
109
134
|
:name => column_name_class(column_family).new(super_column_name).to_s,
|
@@ -122,12 +147,12 @@ class Cassandra
|
|
122
147
|
|
123
148
|
# General info about a deletion object within a mutation
|
124
149
|
# 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
|
150
|
+
# supercolumn - opt. If passed, the deletes will only occur within that supercolumn (only subcolumns
|
126
151
|
# 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
|
152
|
+
# predicate - opt. Defines how to match the columns to delete. if supercolumn passed, the slice will
|
128
153
|
# 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.
|
154
|
+
|
155
|
+
# Deletes a single column from the containing key/CF (and possibly supercolumn), at a given timestamp.
|
131
156
|
# Although mutations (as opposed to 'remove' calls) support deleting slices and lists of columns in one shot, this is not implemented here.
|
132
157
|
# 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
158
|
# methods to use delete mutation calls...although that might have performance implications. We'll leave that refactoring for later.
|
data/lib/cassandra/composite.rb
CHANGED
@@ -1,4 +1,3 @@
|
|
1
|
-
|
2
1
|
class Cassandra
|
3
2
|
class Composite
|
4
3
|
include ::Comparable
|
@@ -6,6 +5,8 @@ class Cassandra
|
|
6
5
|
attr_reader :column_slice
|
7
6
|
|
8
7
|
def initialize(*parts)
|
8
|
+
return if parts.empty?
|
9
|
+
|
9
10
|
options = {}
|
10
11
|
if parts.last.is_a?(Hash)
|
11
12
|
options = parts.pop
|
@@ -16,13 +17,19 @@ class Cassandra
|
|
16
17
|
if parts.length == 1 && parts[0].instance_of?(self.class)
|
17
18
|
@column_slice = parts[0].column_slice
|
18
19
|
@parts = parts[0].parts
|
19
|
-
elsif parts.length == 1 && parts[0].instance_of?(String) && @column_slice.nil? &&
|
20
|
-
|
20
|
+
elsif parts.length == 1 && parts[0].instance_of?(String) && @column_slice.nil? && try_packed_composite(parts[0])
|
21
|
+
@hash = parts[0].hash
|
21
22
|
else
|
22
23
|
@parts = parts
|
23
24
|
end
|
24
25
|
end
|
25
26
|
|
27
|
+
def self.new_from_packed(packed)
|
28
|
+
obj = new
|
29
|
+
obj.fast_unpack(packed)
|
30
|
+
return obj
|
31
|
+
end
|
32
|
+
|
26
33
|
def [](*args)
|
27
34
|
return @parts[*args]
|
28
35
|
end
|
@@ -67,7 +74,7 @@ class Cassandra
|
|
67
74
|
end
|
68
75
|
|
69
76
|
def inspect
|
70
|
-
return "
|
77
|
+
return "#<#{self.class}:#{@column_slice} #{@parts.inspect}>"
|
71
78
|
end
|
72
79
|
|
73
80
|
def slice_end_of_component
|
@@ -76,27 +83,31 @@ class Cassandra
|
|
76
83
|
return "\x00"
|
77
84
|
end
|
78
85
|
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
86
|
+
def fast_unpack(packed_string)
|
87
|
+
@hash = packed_string.hash
|
88
|
+
|
89
|
+
@parts = []
|
90
|
+
end_of_component = packed_string.slice(packed_string.length-1, 1)
|
83
91
|
while packed_string.length > 0
|
84
|
-
length = packed_string.
|
85
|
-
parts << packed_string.slice(2, length)
|
86
|
-
end_of_component = packed_string.slice(2 + length, 1)
|
92
|
+
length = packed_string.unpack('n')[0]
|
93
|
+
@parts << packed_string.slice(2, length)
|
87
94
|
|
88
|
-
packed_string
|
95
|
+
packed_string.slice!(0, length+3)
|
89
96
|
end
|
97
|
+
|
90
98
|
@column_slice = :after if end_of_component == "\x01"
|
91
99
|
@column_slice = :before if end_of_component == "\xFF"
|
92
|
-
@parts = parts
|
93
100
|
end
|
94
101
|
|
95
|
-
|
102
|
+
private
|
103
|
+
def try_packed_composite(packed_string)
|
104
|
+
parts = []
|
105
|
+
end_of_component = nil
|
96
106
|
while packed_string.length > 0
|
97
107
|
length = packed_string.slice(0, 2).unpack('n')[0]
|
98
108
|
return false if length.nil? || length + 3 > packed_string.length
|
99
109
|
|
110
|
+
parts << packed_string.slice(2, length)
|
100
111
|
end_of_component = packed_string.slice(2 + length, 1)
|
101
112
|
if length + 3 != packed_string.length
|
102
113
|
return false if end_of_component != "\x00"
|
@@ -104,11 +115,16 @@ class Cassandra
|
|
104
115
|
|
105
116
|
packed_string = packed_string.slice(3 + length, packed_string.length)
|
106
117
|
end
|
118
|
+
|
119
|
+
@column_slice = :after if end_of_component == "\x01"
|
120
|
+
@column_slice = :before if end_of_component == "\xFF"
|
121
|
+
@parts = parts
|
122
|
+
|
107
123
|
return true
|
108
124
|
end
|
109
125
|
|
110
126
|
def hash
|
111
|
-
return
|
127
|
+
return @hash ||= pack.hash
|
112
128
|
end
|
113
129
|
|
114
130
|
def eql?(other)
|
@@ -116,3 +132,9 @@ class Cassandra
|
|
116
132
|
end
|
117
133
|
end
|
118
134
|
end
|
135
|
+
|
136
|
+
begin
|
137
|
+
require "cassandra_native"
|
138
|
+
rescue LoadError
|
139
|
+
puts "Unable to load cassandra_native extension. Defaulting to pure Ruby libraries."
|
140
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
class Cassandra
|
2
|
+
class DynamicComposite < Composite
|
3
|
+
attr_accessor :types
|
4
|
+
|
5
|
+
def initialize(*parts)
|
6
|
+
return if parts.empty?
|
7
|
+
|
8
|
+
options = {}
|
9
|
+
if parts.last.is_a?(Hash)
|
10
|
+
options = parts.pop
|
11
|
+
end
|
12
|
+
@column_slice = options[:slice]
|
13
|
+
raise ArgumentError if @column_slice != nil && ![:before, :after].include?(@column_slice)
|
14
|
+
|
15
|
+
if parts.length == 1 && parts[0].instance_of?(self.class)
|
16
|
+
@column_slice = parts[0].column_slice
|
17
|
+
@parts = parts[0].parts
|
18
|
+
@types = parts[0].types
|
19
|
+
elsif parts.length == 1 && parts[0].instance_of?(String) && @column_slice.nil? && try_packed_composite(parts[0])
|
20
|
+
@hash = parts[0].hash
|
21
|
+
else
|
22
|
+
@types, @parts = parts.transpose
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def pack
|
27
|
+
packed_parts = @parts.map do |part|
|
28
|
+
[part.length].pack('n') + part + "\x00"
|
29
|
+
end
|
30
|
+
|
31
|
+
if @column_slice
|
32
|
+
part = @parts[-1]
|
33
|
+
packed_parts[-1] = [part.length].pack('n') + part + slice_end_of_component
|
34
|
+
end
|
35
|
+
|
36
|
+
packed_types = @types.map do |type|
|
37
|
+
if type.length == 1
|
38
|
+
[0x8000 | type.ord].pack('n')
|
39
|
+
else
|
40
|
+
[type.length].pack('n') + type
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
return packed_types.zip(packed_parts).flatten.join('')
|
45
|
+
end
|
46
|
+
|
47
|
+
def fast_unpack(packed_string)
|
48
|
+
result = try_packed_composite(packed_string)
|
49
|
+
raise ArgumentError.new("Invalid DynamicComposite column") if !result
|
50
|
+
@hash = packed_string.hash
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
def try_packed_composite(packed_string)
|
55
|
+
types = []
|
56
|
+
parts = []
|
57
|
+
end_of_component = nil
|
58
|
+
offset = 0
|
59
|
+
|
60
|
+
read_bytes = proc do |length|
|
61
|
+
return false if offset + length > packed_string.length
|
62
|
+
out = packed_string.slice(offset, length)
|
63
|
+
offset += length
|
64
|
+
out
|
65
|
+
end
|
66
|
+
|
67
|
+
while offset < packed_string.length
|
68
|
+
header = read_bytes.call(2).unpack('n')[0]
|
69
|
+
is_alias = header & 0x8000 != 0
|
70
|
+
if is_alias
|
71
|
+
alias_char = (header & 0xFF).chr
|
72
|
+
types << alias_char
|
73
|
+
else
|
74
|
+
length = header
|
75
|
+
return false if length.nil? || length + offset > packed_string.length
|
76
|
+
type = read_bytes.call(length)
|
77
|
+
types << type
|
78
|
+
end
|
79
|
+
length = read_bytes.call(2).unpack('n')[0]
|
80
|
+
return false if length.nil? || length + offset > packed_string.length
|
81
|
+
parts << read_bytes.call(length)
|
82
|
+
end_of_component = read_bytes.call(1)
|
83
|
+
if offset < packed_string.length
|
84
|
+
return false if end_of_component != "\x00"
|
85
|
+
end
|
86
|
+
end
|
87
|
+
@column_slice = :after if end_of_component == "\x01"
|
88
|
+
@column_slice = :before if end_of_component == "\xFF"
|
89
|
+
@types = types
|
90
|
+
@parts = parts
|
91
|
+
@hash = packed_string.hash
|
92
|
+
|
93
|
+
return true
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
data/lib/cassandra/mock.rb
CHANGED
@@ -20,6 +20,8 @@ class Cassandra
|
|
20
20
|
@keyspace = keyspace
|
21
21
|
@column_name_class = {}
|
22
22
|
@sub_column_name_class = {}
|
23
|
+
@column_name_maker = {}
|
24
|
+
@sub_column_name_maker = {}
|
23
25
|
@indexes = {}
|
24
26
|
@schema = schema[keyspace]
|
25
27
|
clear_keyspace!
|
@@ -293,10 +295,13 @@ class Cassandra
|
|
293
295
|
|
294
296
|
def get_indexed_slices(column_family, idx_clause, *columns_and_options)
|
295
297
|
column_family, columns, _, options =
|
296
|
-
extract_and_validate_params_for_real(column_family, [], columns_and_options,
|
298
|
+
extract_and_validate_params_for_real(column_family, [], columns_and_options,
|
299
|
+
READ_DEFAULTS.merge(:key_count => 100, :start_key => nil, :key_start => nil))
|
300
|
+
|
301
|
+
start_key = options[:start_key] || options[:key_start] || ""
|
297
302
|
|
298
303
|
unless [Hash, OrderedHash].include?(idx_clause.class) && idx_clause[:type] == :index_clause
|
299
|
-
idx_clause = create_index_clause(idx_clause,
|
304
|
+
idx_clause = create_index_clause(idx_clause, start_key, options[:key_count])
|
300
305
|
end
|
301
306
|
|
302
307
|
ret = {}
|
@@ -319,20 +324,24 @@ class Cassandra
|
|
319
324
|
end
|
320
325
|
|
321
326
|
def add(column_family, key, value, *columns_and_options)
|
322
|
-
|
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
|
327
|
+
if @batch
|
328
|
+
@batch << [:add, column_family, key, value, *columns_and_options]
|
329
329
|
else
|
330
|
-
|
331
|
-
cf(column_family)[key][column] ||= 0
|
332
|
-
cf(column_family)[key][column] += value
|
333
|
-
end
|
330
|
+
column_family, column, sub_column, options = extract_and_validate_params_for_real(column_family, key, columns_and_options, WRITE_DEFAULTS)
|
334
331
|
|
335
|
-
|
332
|
+
if is_super(column_family)
|
333
|
+
cf(column_family)[key] ||= OrderedHash.new
|
334
|
+
cf(column_family)[key][column] ||= OrderedHash.new
|
335
|
+
cf(column_family)[key][column][sub_column] ||= 0
|
336
|
+
cf(column_family)[key][column][sub_column] += value
|
337
|
+
else
|
338
|
+
cf(column_family)[key] ||= OrderedHash.new
|
339
|
+
cf(column_family)[key][column] ||= 0
|
340
|
+
cf(column_family)[key][column] += value
|
341
|
+
end
|
342
|
+
|
343
|
+
nil
|
344
|
+
end
|
336
345
|
end
|
337
346
|
|
338
347
|
def column_families
|
data/test/cassandra_mock_test.rb
CHANGED
@@ -28,6 +28,12 @@ class CassandraMockTest < CassandraTest
|
|
28
28
|
Cassandra::Composite.new([1].pack('N'), "elephant"),
|
29
29
|
Cassandra::Composite.new([10].pack('N'), "kangaroo"),
|
30
30
|
]
|
31
|
+
@dynamic_composites = [
|
32
|
+
Cassandra::DynamicComposite.new(['i', [5].pack('N')], ['UTF8Type', "zebra"]),
|
33
|
+
Cassandra::DynamicComposite.new(['i', [5].pack('N')], ['UTF8Type', "aardvark"]),
|
34
|
+
Cassandra::DynamicComposite.new(['IntegerType', [1].pack('N')], ['s', "elephant"]),
|
35
|
+
Cassandra::DynamicComposite.new(['IntegerType', [10].pack('N')], ['s', "kangaroo"]),
|
36
|
+
]
|
31
37
|
end
|
32
38
|
|
33
39
|
def test_setup
|
data/test/cassandra_test.rb
CHANGED
@@ -3,17 +3,21 @@ require File.expand_path(File.dirname(__FILE__) + '/test_helper')
|
|
3
3
|
class CassandraTest < Test::Unit::TestCase
|
4
4
|
include Cassandra::Constants
|
5
5
|
|
6
|
+
def assert_has_keys(keys, hash)
|
7
|
+
assert_equal Set.new(keys), Set.new(hash.keys)
|
8
|
+
end
|
9
|
+
|
6
10
|
def setup
|
7
|
-
@twitter = Cassandra.new('Twitter', "127.0.0.1:9160", :retries => 2, :connect_timeout => 0.1, :exception_classes => [])
|
11
|
+
@twitter = Cassandra.new('Twitter', "127.0.0.1:9160", :retries => 2, :connect_timeout => 0.1, :timeout => 5, :exception_classes => [])
|
8
12
|
@twitter.clear_keyspace!
|
9
13
|
|
10
|
-
@blogs = Cassandra.new('Multiblog', "127.0.0.1:9160", :retries => 2, :connect_timeout => 0.1, :exception_classes => [])
|
14
|
+
@blogs = Cassandra.new('Multiblog', "127.0.0.1:9160", :retries => 2, :connect_timeout => 0.1, :timeout => 5, :exception_classes => [])
|
11
15
|
@blogs.clear_keyspace!
|
12
16
|
|
13
|
-
@blogs_long = Cassandra.new('MultiblogLong', "127.0.0.1:9160", :retries => 2, :connect_timeout => 0.1, :exception_classes => [])
|
17
|
+
@blogs_long = Cassandra.new('MultiblogLong', "127.0.0.1:9160", :retries => 2, :connect_timeout => 0.1, :timeout => 5, :exception_classes => [])
|
14
18
|
@blogs_long.clear_keyspace!
|
15
19
|
|
16
|
-
@type_conversions = Cassandra.new('TypeConversions', "127.0.0.1:9160", :retries => 2, :connect_timeout => 0.1, :exception_classes => [])
|
20
|
+
@type_conversions = Cassandra.new('TypeConversions', "127.0.0.1:9160", :retries => 2, :connect_timeout => 0.1, :timeout => 5, :exception_classes => [])
|
17
21
|
@type_conversions.clear_keyspace!
|
18
22
|
|
19
23
|
Cassandra::WRITE_DEFAULTS[:consistency] = Cassandra::Consistency::ONE
|
@@ -27,6 +31,12 @@ class CassandraTest < Test::Unit::TestCase
|
|
27
31
|
Cassandra::Composite.new([1].pack('N'), "elephant"),
|
28
32
|
Cassandra::Composite.new([10].pack('N'), "kangaroo"),
|
29
33
|
]
|
34
|
+
@dynamic_composites = [
|
35
|
+
Cassandra::DynamicComposite.new(['i', [5].pack('N')], ['UTF8Type', "zebra"]),
|
36
|
+
Cassandra::DynamicComposite.new(['i', [5].pack('N')], ['UTF8Type', "aardvark"]),
|
37
|
+
Cassandra::DynamicComposite.new(['IntegerType', [1].pack('N')], ['s', "elephant"]),
|
38
|
+
Cassandra::DynamicComposite.new(['IntegerType', [10].pack('N')], ['s', "kangaroo"]),
|
39
|
+
]
|
30
40
|
end
|
31
41
|
|
32
42
|
def test_inspect
|
@@ -510,9 +520,8 @@ class CassandraTest < Test::Unit::TestCase
|
|
510
520
|
|
511
521
|
@twitter.insert(:Statuses, key, columns)
|
512
522
|
assert_equal 200, @twitter.count_columns(:Statuses, key, :count => 200)
|
513
|
-
assert_equal 100, @twitter.count_columns(:Statuses, key)
|
514
|
-
assert_equal 55, @twitter.count_columns(:Statuses, key, :count => 55)
|
515
|
-
|
523
|
+
assert_equal 100, @twitter.count_columns(:Statuses, key) if CASSANDRA_VERSION.to_f >= 0.8
|
524
|
+
assert_equal 55, @twitter.count_columns(:Statuses, key, :count => 55) if CASSANDRA_VERSION.to_f >= 0.8
|
516
525
|
end
|
517
526
|
|
518
527
|
def test_count_super_columns
|
@@ -562,7 +571,7 @@ class CassandraTest < Test::Unit::TestCase
|
|
562
571
|
assert_equal({'body' => 'v1', 'user' => 'v1'}, @twitter.get(:Users, k + '1')) # Written
|
563
572
|
assert_equal({}, @twitter.get(:Users, k + '2')) # Not yet written
|
564
573
|
assert_equal({}, @twitter.get(:Statuses, k + '3')) # Not yet written
|
565
|
-
assert_equal({}, @twitter.get(:UserCounters, 'bob')) if CASSANDRA_VERSION.to_f >= 0.8 #
|
574
|
+
assert_equal({}, @twitter.get(:UserCounters, 'bob')) if CASSANDRA_VERSION.to_f >= 0.8 # Written
|
566
575
|
|
567
576
|
@twitter.remove(:Users, k + '1') # Full row
|
568
577
|
assert_equal({'body' => 'v1', 'user' => 'v1'}, @twitter.get(:Users, k + '1')) # Not yet removed
|
@@ -715,69 +724,361 @@ class CassandraTest < Test::Unit::TestCase
|
|
715
724
|
|
716
725
|
if CASSANDRA_VERSION.to_f >= 0.7
|
717
726
|
def test_creating_and_dropping_new_index
|
718
|
-
@twitter.create_index('Twitter', 'Statuses', 'column_name', '
|
719
|
-
assert_nil @twitter.create_index('Twitter', 'Statuses', 'column_name', '
|
727
|
+
@twitter.create_index('Twitter', 'Statuses', 'column_name', 'BytesType')
|
728
|
+
assert_nil @twitter.create_index('Twitter', 'Statuses', 'column_name', 'BytesType')
|
720
729
|
|
721
730
|
@twitter.drop_index('Twitter', 'Statuses', 'column_name')
|
722
731
|
assert_nil @twitter.drop_index('Twitter', 'Statuses', 'column_name')
|
723
732
|
|
724
733
|
# Recreating and redropping the same index should not error either.
|
725
|
-
@twitter.create_index('Twitter', 'Statuses', 'column_name', '
|
734
|
+
@twitter.create_index('Twitter', 'Statuses', 'column_name', 'BytesType')
|
726
735
|
@twitter.drop_index('Twitter', 'Statuses', 'column_name')
|
727
736
|
end
|
728
737
|
|
729
738
|
def test_get_indexed_slices
|
730
|
-
@twitter.
|
739
|
+
@twitter.insert(:Statuses, 'row_a', {
|
740
|
+
'tags' => 'a',
|
741
|
+
'y' => 'foo'
|
742
|
+
})
|
743
|
+
@twitter.insert(:Statuses, 'row_b', {
|
744
|
+
'tags' => 'b',
|
745
|
+
'y' => 'foo'
|
746
|
+
})
|
747
|
+
[1,2].each do |i|
|
748
|
+
@twitter.insert(:Statuses, "row_c_#{i}", {
|
749
|
+
'tags' => 'c',
|
750
|
+
'y' => 'a'
|
751
|
+
})
|
752
|
+
end
|
753
|
+
[3,4].each do |i|
|
754
|
+
@twitter.insert(:Statuses, "row_c_#{i}", {
|
755
|
+
'tags' => 'c',
|
756
|
+
'y' => 'b'
|
757
|
+
})
|
758
|
+
end
|
759
|
+
@twitter.insert(:Statuses, 'row_d', {
|
760
|
+
'tags' => 'd',
|
761
|
+
'y' => 'foo'
|
762
|
+
})
|
763
|
+
@twitter.insert(:Statuses, 'row_e', {
|
764
|
+
'tags' => 'e',
|
765
|
+
'y' => 'bar'
|
766
|
+
})
|
767
|
+
|
768
|
+
# Test == operator (single clause)
|
769
|
+
rows = @twitter.get_indexed_slices(:Statuses, [
|
770
|
+
{:column_name => 'tags',
|
771
|
+
:value => 'c',
|
772
|
+
:comparison => "=="}
|
773
|
+
])
|
774
|
+
assert_has_keys %w[row_c_1 row_c_2 row_c_3 row_c_4], rows
|
775
|
+
|
776
|
+
# Test other operators
|
777
|
+
# Currently (as of Cassandra 1.1) you can't use anything but == as the
|
778
|
+
# primary query -- you must match on == first, and subsequent clauses are
|
779
|
+
# then applied as filters -- so that's what we are doing here.
|
780
|
+
|
781
|
+
# Test > operator
|
782
|
+
rows = @twitter.get_indexed_slices(:Statuses, [
|
783
|
+
{:column_name => 'tags',
|
784
|
+
:value => 'c',
|
785
|
+
:comparison => "=="},
|
786
|
+
{:column_name => 'y',
|
787
|
+
:value => 'a',
|
788
|
+
:comparison => ">"}
|
789
|
+
])
|
790
|
+
assert_has_keys %w[row_c_3 row_c_4], rows
|
791
|
+
|
792
|
+
# Test >= operator
|
793
|
+
rows = @twitter.get_indexed_slices(:Statuses, [
|
794
|
+
{:column_name => 'tags',
|
795
|
+
:value => 'c',
|
796
|
+
:comparison => "=="},
|
797
|
+
{:column_name => 'y',
|
798
|
+
:value => 'a',
|
799
|
+
:comparison => ">="}
|
800
|
+
])
|
801
|
+
assert_has_keys %w[row_c_1 row_c_2 row_c_3 row_c_4], rows
|
802
|
+
|
803
|
+
# Test < operator
|
804
|
+
rows = @twitter.get_indexed_slices(:Statuses, [
|
805
|
+
{:column_name => 'tags',
|
806
|
+
:value => 'c',
|
807
|
+
:comparison => "=="},
|
808
|
+
{:column_name => 'y',
|
809
|
+
:value => 'b',
|
810
|
+
:comparison => "<"}
|
811
|
+
])
|
812
|
+
assert_has_keys %w[row_c_1 row_c_2], rows
|
813
|
+
|
814
|
+
# Test <= operator
|
815
|
+
rows = @twitter.get_indexed_slices(:Statuses, [
|
816
|
+
{:column_name => 'tags',
|
817
|
+
:value => 'c',
|
818
|
+
:comparison => "=="},
|
819
|
+
{:column_name => 'y',
|
820
|
+
:value => 'b',
|
821
|
+
:comparison => "<="}
|
822
|
+
])
|
823
|
+
assert_has_keys %w[row_c_1 row_c_2 row_c_3 row_c_4], rows
|
824
|
+
|
825
|
+
# Test query on a non-indexed column
|
826
|
+
unless self.is_a?(CassandraMockTest)
|
827
|
+
assert_raises(CassandraThrift::InvalidRequestException) do
|
828
|
+
@twitter.get_indexed_slices(:Statuses, [
|
829
|
+
{:column_name => 'y',
|
830
|
+
:value => 'foo',
|
831
|
+
:comparison => "=="}
|
832
|
+
])
|
833
|
+
end
|
834
|
+
end
|
731
835
|
|
732
|
-
|
836
|
+
# Test start key
|
837
|
+
rows = @twitter.get_indexed_slices(:Statuses, [
|
838
|
+
{:column_name => 'tags',
|
839
|
+
:value => 'c',
|
840
|
+
:comparison => "=="},
|
841
|
+
], :start_key => 'row_c_2')
|
842
|
+
assert_equal 'row_c_2', rows.keys.first
|
843
|
+
# <- can't test any keys after that since it's going to be random
|
844
|
+
|
845
|
+
# Test key_start option
|
846
|
+
rows = @twitter.get_indexed_slices(:Statuses, [
|
847
|
+
{:column_name => 'tags',
|
848
|
+
:value => 'c',
|
849
|
+
:comparison => "=="},
|
850
|
+
], :key_start => 'row_c_2')
|
851
|
+
assert_equal 'row_c_2', rows.keys.first
|
852
|
+
# <- can't test any keys after that since it's going to be random
|
853
|
+
|
854
|
+
# Test key count
|
855
|
+
rows = @twitter.get_indexed_slices(:Statuses, [
|
856
|
+
{:column_name => 'tags',
|
857
|
+
:value => 'c',
|
858
|
+
:comparison => "=="}
|
859
|
+
], :key_count => 2)
|
860
|
+
assert_equal 2, rows.length
|
861
|
+
# <- can't test which keys are present since it's going to be random
|
862
|
+
end
|
733
863
|
|
734
|
-
|
735
|
-
|
864
|
+
def test_get_indexed_slices_with_IndexClause_objects
|
865
|
+
@twitter.insert(:Statuses, 'row_a', {
|
866
|
+
'tags' => 'a',
|
867
|
+
'y' => 'foo'
|
868
|
+
})
|
869
|
+
@twitter.insert(:Statuses, 'row_b', {
|
870
|
+
'tags' => 'b',
|
871
|
+
'y' => 'foo'
|
872
|
+
})
|
873
|
+
[1,2].each do |i|
|
874
|
+
@twitter.insert(:Statuses, "row_c_#{i}", {
|
875
|
+
'tags' => 'c',
|
876
|
+
'y' => 'a'
|
877
|
+
})
|
878
|
+
end
|
879
|
+
[3,4].each do |i|
|
880
|
+
@twitter.insert(:Statuses, "row_c_#{i}", {
|
881
|
+
'tags' => 'c',
|
882
|
+
'y' => 'b'
|
883
|
+
})
|
884
|
+
end
|
885
|
+
@twitter.insert(:Statuses, 'row_d', {
|
886
|
+
'tags' => 'd',
|
887
|
+
'y' => 'foo'
|
888
|
+
})
|
889
|
+
@twitter.insert(:Statuses, 'row_e', {
|
890
|
+
'tags' => 'e',
|
891
|
+
'y' => 'bar'
|
892
|
+
})
|
893
|
+
|
894
|
+
# Test == operator (single clause)
|
895
|
+
index_clause = @twitter.create_index_clause([
|
896
|
+
@twitter.create_index_expression('tags', 'c', '==')
|
897
|
+
])
|
898
|
+
rows = @twitter.get_indexed_slices(:Statuses, index_clause)
|
899
|
+
assert_has_keys %w[row_c_1 row_c_2 row_c_3 row_c_4], rows
|
900
|
+
|
901
|
+
# Test other operators
|
902
|
+
# Currently (as of Cassandra 1.1) you can't use anything but == as the
|
903
|
+
# primary query -- you must match on == first, and subsequent clauses are
|
904
|
+
# then applied as filters -- so that's what we are doing here.
|
905
|
+
|
906
|
+
# Test > operator
|
907
|
+
index_clause = @twitter.create_index_clause([
|
908
|
+
@twitter.create_index_expression('tags', 'c', '=='),
|
909
|
+
@twitter.create_index_expression('y', 'a', '>'),
|
910
|
+
])
|
911
|
+
rows = @twitter.get_indexed_slices(:Statuses, index_clause)
|
912
|
+
assert_has_keys %w[row_c_3 row_c_4], rows
|
913
|
+
|
914
|
+
# Test >= operator
|
915
|
+
index_clause = @twitter.create_index_clause([
|
916
|
+
@twitter.create_index_expression('tags', 'c', '=='),
|
917
|
+
@twitter.create_index_expression('y', 'a', '>=')
|
918
|
+
])
|
919
|
+
rows = @twitter.get_indexed_slices(:Statuses, index_clause)
|
920
|
+
assert_has_keys %w[row_c_1 row_c_2 row_c_3 row_c_4], rows
|
921
|
+
|
922
|
+
# Test < operator
|
923
|
+
index_clause = @twitter.create_index_clause([
|
924
|
+
@twitter.create_index_expression('tags', 'c', '=='),
|
925
|
+
@twitter.create_index_expression('y', 'b', '<')
|
926
|
+
])
|
927
|
+
rows = @twitter.get_indexed_slices(:Statuses, index_clause)
|
928
|
+
assert_has_keys %w[row_c_1 row_c_2], rows
|
929
|
+
|
930
|
+
# Test <= operator
|
931
|
+
index_clause = @twitter.create_index_clause([
|
932
|
+
@twitter.create_index_expression('tags', 'c', '=='),
|
933
|
+
@twitter.create_index_expression('y', 'b', '<=')
|
934
|
+
])
|
935
|
+
rows = @twitter.get_indexed_slices(:Statuses, index_clause)
|
936
|
+
assert_has_keys %w[row_c_1 row_c_2 row_c_3 row_c_4], rows
|
937
|
+
|
938
|
+
# Test query on a non-indexed column
|
939
|
+
unless self.is_a?(CassandraMockTest)
|
940
|
+
assert_raises(CassandraThrift::InvalidRequestException) do
|
941
|
+
index_clause = @twitter.create_index_clause([
|
942
|
+
@twitter.create_index_expression('y', 'foo', '==')
|
943
|
+
])
|
944
|
+
@twitter.get_indexed_slices(:Statuses, index_clause)
|
945
|
+
end
|
736
946
|
end
|
737
947
|
|
738
|
-
|
948
|
+
# Test start key
|
949
|
+
index_clause = @twitter.create_index_clause([
|
950
|
+
@twitter.create_index_expression('tags', 'c', '==')
|
951
|
+
], 'row_c_2')
|
952
|
+
rows = @twitter.get_indexed_slices(:Statuses, index_clause)
|
953
|
+
assert_equal 'row_c_2', rows.keys.first
|
954
|
+
# <- can't test any keys after that since it's going to be random
|
955
|
+
|
956
|
+
# Test key count
|
957
|
+
index_clause = @twitter.create_index_clause([
|
958
|
+
@twitter.create_index_expression('tags', 'c', '==')
|
959
|
+
], "", 2)
|
960
|
+
rows = @twitter.get_indexed_slices(:Statuses, index_clause)
|
961
|
+
assert_equal 2, rows.length
|
962
|
+
# <- can't test which keys are present since it's going to be random
|
963
|
+
end
|
739
964
|
|
740
|
-
|
965
|
+
def test_create_index_clause
|
966
|
+
return if self.is_a?(CassandraMockTest)
|
741
967
|
|
742
|
-
|
743
|
-
|
968
|
+
ie = CassandraThrift::IndexExpression.new(
|
969
|
+
:column_name => 'foo',
|
970
|
+
:value => 'x',
|
971
|
+
:op => '=='
|
972
|
+
)
|
744
973
|
|
745
|
-
|
746
|
-
|
747
|
-
|
748
|
-
|
749
|
-
|
750
|
-
|
974
|
+
ic = @twitter.create_index_clause([ie], 'aaa', 250)
|
975
|
+
assert_instance_of CassandraThrift::IndexClause, ic
|
976
|
+
assert_equal 'aaa', ic.start_key
|
977
|
+
assert_equal ie, ic.expressions[0]
|
978
|
+
assert_equal 250, ic.count
|
979
|
+
|
980
|
+
# test alias
|
981
|
+
ic = @twitter.create_idx_clause([ie], 'aaa', 250)
|
982
|
+
assert_instance_of CassandraThrift::IndexClause, ic
|
983
|
+
assert_equal 'aaa', ic.start_key
|
984
|
+
assert_equal ie, ic.expressions[0]
|
985
|
+
assert_equal 250, ic.count
|
751
986
|
end
|
752
987
|
|
753
|
-
def
|
754
|
-
|
988
|
+
def test_create_index_expression
|
989
|
+
return if self.is_a?(CassandraMockTest)
|
755
990
|
|
756
|
-
|
757
|
-
|
758
|
-
|
759
|
-
|
991
|
+
# EQ operator
|
992
|
+
[nil, "EQ", "eq", "=="].each do |op|
|
993
|
+
ie = @twitter.create_index_expression('foo', 'x', op)
|
994
|
+
assert_instance_of CassandraThrift::IndexExpression, ie
|
995
|
+
assert_equal 'foo', ie.column_name
|
996
|
+
assert_equal 'x', ie.value
|
997
|
+
assert_equal CassandraThrift::IndexOperator::EQ, ie.op
|
998
|
+
end
|
999
|
+
# alias
|
1000
|
+
[nil, "EQ", "eq", "=="].each do |op|
|
1001
|
+
ie = @twitter.create_idx_expr('foo', 'x', op)
|
1002
|
+
assert_instance_of CassandraThrift::IndexExpression, ie
|
1003
|
+
assert_equal 'foo', ie.column_name
|
1004
|
+
assert_equal 'x', ie.value
|
1005
|
+
assert_equal CassandraThrift::IndexOperator::EQ, ie.op
|
760
1006
|
end
|
761
1007
|
|
762
|
-
|
1008
|
+
# GTE operator
|
1009
|
+
["GTE", "gte", ">="].each do |op|
|
1010
|
+
ie = @twitter.create_index_expression('foo', 'x', op)
|
1011
|
+
assert_instance_of CassandraThrift::IndexExpression, ie
|
1012
|
+
assert_equal 'foo', ie.column_name
|
1013
|
+
assert_equal 'x', ie.value
|
1014
|
+
assert_equal CassandraThrift::IndexOperator::GTE, ie.op
|
1015
|
+
end
|
1016
|
+
# alias
|
1017
|
+
["GTE", "gte", ">="].each do |op|
|
1018
|
+
ie = @twitter.create_idx_expr('foo', 'x', op)
|
1019
|
+
assert_instance_of CassandraThrift::IndexExpression, ie
|
1020
|
+
assert_equal 'foo', ie.column_name
|
1021
|
+
assert_equal 'x', ie.value
|
1022
|
+
assert_equal CassandraThrift::IndexOperator::GTE, ie.op
|
1023
|
+
end
|
763
1024
|
|
764
|
-
|
1025
|
+
# GT operator
|
1026
|
+
["GT", "gt", ">"].each do |op|
|
1027
|
+
ie = @twitter.create_index_expression('foo', 'x', op)
|
1028
|
+
assert_instance_of CassandraThrift::IndexExpression, ie
|
1029
|
+
assert_equal 'foo', ie.column_name
|
1030
|
+
assert_equal 'x', ie.value
|
1031
|
+
assert_equal CassandraThrift::IndexOperator::GT, ie.op
|
1032
|
+
end
|
1033
|
+
# alias
|
1034
|
+
["GT", "gt", ">"].each do |op|
|
1035
|
+
ie = @twitter.create_idx_expr('foo', 'x', op)
|
1036
|
+
assert_instance_of CassandraThrift::IndexExpression, ie
|
1037
|
+
assert_equal 'foo', ie.column_name
|
1038
|
+
assert_equal 'x', ie.value
|
1039
|
+
assert_equal CassandraThrift::IndexOperator::GT, ie.op
|
1040
|
+
end
|
765
1041
|
|
766
|
-
#
|
767
|
-
|
768
|
-
|
1042
|
+
# LTE operator
|
1043
|
+
["LTE", "lte", "<="].each do |op|
|
1044
|
+
ie = @twitter.create_index_expression('foo', 'x', op)
|
1045
|
+
assert_instance_of CassandraThrift::IndexExpression, ie
|
1046
|
+
assert_equal 'foo', ie.column_name
|
1047
|
+
assert_equal 'x', ie.value
|
1048
|
+
assert_equal CassandraThrift::IndexOperator::LTE, ie.op
|
1049
|
+
end
|
1050
|
+
# alias
|
1051
|
+
["LTE", "lte", "<="].each do |op|
|
1052
|
+
ie = @twitter.create_idx_expr('foo', 'x', op)
|
1053
|
+
assert_instance_of CassandraThrift::IndexExpression, ie
|
1054
|
+
assert_equal 'foo', ie.column_name
|
1055
|
+
assert_equal 'x', ie.value
|
1056
|
+
assert_equal CassandraThrift::IndexOperator::LTE, ie.op
|
1057
|
+
end
|
769
1058
|
|
770
|
-
#
|
771
|
-
|
772
|
-
|
1059
|
+
# LT operator
|
1060
|
+
["LT", "lt", "<"].each do |op|
|
1061
|
+
ie = @twitter.create_index_expression('foo', 'x', op)
|
1062
|
+
assert_instance_of CassandraThrift::IndexExpression, ie
|
1063
|
+
assert_equal 'foo', ie.column_name
|
1064
|
+
assert_equal 'x', ie.value
|
1065
|
+
assert_equal CassandraThrift::IndexOperator::LT, ie.op
|
1066
|
+
end
|
1067
|
+
# alias
|
1068
|
+
["LT", "lt", "<"].each do |op|
|
1069
|
+
ie = @twitter.create_idx_expr('foo', 'x', op)
|
1070
|
+
assert_instance_of CassandraThrift::IndexExpression, ie
|
1071
|
+
assert_equal 'foo', ie.column_name
|
1072
|
+
assert_equal 'x', ie.value
|
1073
|
+
assert_equal CassandraThrift::IndexOperator::LT, ie.op
|
1074
|
+
end
|
773
1075
|
|
774
|
-
#
|
775
|
-
|
776
|
-
|
777
|
-
|
778
|
-
|
779
|
-
|
780
|
-
assert_equal(5, @twitter.get_indexed_slices(:Statuses, idx_clause).length)
|
1076
|
+
# unknown operator
|
1077
|
+
ie = @twitter.create_index_expression('foo', 'x', '~$')
|
1078
|
+
assert_equal nil, ie.op
|
1079
|
+
# alias
|
1080
|
+
ie = @twitter.create_idx_expr('foo', 'x', '~$')
|
1081
|
+
assert_equal nil, ie.op
|
781
1082
|
end
|
782
1083
|
|
783
1084
|
def test_column_family_mutation
|
@@ -843,7 +1144,7 @@ class CassandraTest < Test::Unit::TestCase
|
|
843
1144
|
assert_equal(1, @twitter.get(:UserCounterAggregates, 'bob', 'DAU', 'today'))
|
844
1145
|
assert_equal(2, @twitter.get(:UserCounterAggregates, 'bob', 'DAU', 'tomorrow'))
|
845
1146
|
end
|
846
|
-
|
1147
|
+
|
847
1148
|
def test_reading_rows_with_super_column_counter
|
848
1149
|
assert_nil @twitter.add(:UserCounterAggregates, 'bob', 1, 'DAU', 'today')
|
849
1150
|
assert_nil @twitter.add(:UserCounterAggregates, 'bob', 2, 'DAU', 'tomorrow')
|
@@ -853,8 +1154,8 @@ class CassandraTest < Test::Unit::TestCase
|
|
853
1154
|
assert_equal("DAU", result.first[0])
|
854
1155
|
assert_equal(1, result.first[1]["today"])
|
855
1156
|
assert_equal(2, result.first[1]["tomorrow"])
|
856
|
-
end
|
857
|
-
|
1157
|
+
end
|
1158
|
+
|
858
1159
|
def test_composite_column_type_conversion
|
859
1160
|
columns = {}
|
860
1161
|
@composites.each_with_index do |c, index|
|
@@ -893,6 +1194,46 @@ class CassandraTest < Test::Unit::TestCase
|
|
893
1194
|
|
894
1195
|
assert_equal('value-2', @type_conversions.get(:CompositeColumnConversion, key, columns_in_order.first))
|
895
1196
|
end
|
1197
|
+
|
1198
|
+
def test_dynamic_composite_column_type_conversion
|
1199
|
+
columns = {}
|
1200
|
+
@dynamic_composites.each_with_index do |c, index|
|
1201
|
+
columns[c] = "value-#{index}"
|
1202
|
+
end
|
1203
|
+
@type_conversions.insert(:DynamicComposite, key, columns)
|
1204
|
+
|
1205
|
+
columns_in_order = [
|
1206
|
+
Cassandra::DynamicComposite.new(['IntegerType', [1].pack('N')], ['s', "elephant"]),
|
1207
|
+
Cassandra::DynamicComposite.new(['i', [5].pack('N')], ['UTF8Type', "aardvark"]),
|
1208
|
+
Cassandra::DynamicComposite.new(['i', [5].pack('N')], ['UTF8Type', "zebra"]),
|
1209
|
+
Cassandra::DynamicComposite.new(['IntegerType', [10].pack('N')], ['s', "kangaroo"]),
|
1210
|
+
]
|
1211
|
+
assert_equal(columns_in_order, @type_conversions.get(:DynamicComposite, key).keys)
|
1212
|
+
|
1213
|
+
column_slice = @type_conversions.get(:DynamicComposite, key,
|
1214
|
+
:start => Cassandra::DynamicComposite.new(['i', [1].pack('N')]),
|
1215
|
+
:finish => Cassandra::DynamicComposite.new(['i', [10].pack('N')]),
|
1216
|
+
).keys
|
1217
|
+
assert_equal(columns_in_order[0..-2], column_slice)
|
1218
|
+
|
1219
|
+
column_slice = @type_conversions.get(:DynamicComposite, key,
|
1220
|
+
:start => Cassandra::DynamicComposite.new(['IntegerType', [5].pack('N')]),
|
1221
|
+
:finish => Cassandra::DynamicComposite.new(['IntegerType', [5].pack('N')], :slice => :after),
|
1222
|
+
).keys
|
1223
|
+
assert_equal(columns_in_order[1..2], column_slice)
|
1224
|
+
|
1225
|
+
column_slice = @type_conversions.get(:DynamicComposite, key,
|
1226
|
+
:start => Cassandra::DynamicComposite.new(['i', [5].pack('N')], :slice => :after).to_s,
|
1227
|
+
).keys
|
1228
|
+
assert_equal([columns_in_order[-1]], column_slice)
|
1229
|
+
|
1230
|
+
column_slice = @type_conversions.get(:DynamicComposite, key,
|
1231
|
+
:finish => Cassandra::DynamicComposite.new(['i', [10].pack('N')], :slice => :before).to_s,
|
1232
|
+
).keys
|
1233
|
+
assert_equal(columns_in_order[0..-2], column_slice)
|
1234
|
+
|
1235
|
+
assert_equal('value-2', @type_conversions.get(:DynamicComposite, key, columns_in_order.first))
|
1236
|
+
end
|
896
1237
|
end
|
897
1238
|
|
898
1239
|
def test_column_timestamps
|