jamesgolick-cassandra 0.8.2
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 +51 -0
- data/LICENSE +202 -0
- data/Manifest +31 -0
- data/README.rdoc +71 -0
- data/Rakefile +79 -0
- data/bin/cassandra_helper +16 -0
- data/cassandra.gemspec +45 -0
- data/conf/cassandra.in.sh +47 -0
- data/conf/log4j.properties +38 -0
- data/conf/storage-conf.xml +340 -0
- data/lib/cassandra.rb +22 -0
- data/lib/cassandra/array.rb +8 -0
- data/lib/cassandra/cassandra.rb +314 -0
- data/lib/cassandra/columns.rb +106 -0
- data/lib/cassandra/comparable.rb +28 -0
- data/lib/cassandra/constants.rb +11 -0
- data/lib/cassandra/debug.rb +7 -0
- data/lib/cassandra/helpers.rb +40 -0
- data/lib/cassandra/long.rb +58 -0
- data/lib/cassandra/mock.rb +322 -0
- data/lib/cassandra/ordered_hash.rb +141 -0
- data/lib/cassandra/protocol.rb +92 -0
- data/lib/cassandra/time.rb +11 -0
- data/test/cassandra_client_test.rb +20 -0
- data/test/cassandra_mock_test.rb +73 -0
- data/test/cassandra_test.rb +370 -0
- data/test/comparable_types_test.rb +45 -0
- data/test/eventmachine_test.rb +42 -0
- data/test/ordered_hash_test.rb +201 -0
- data/test/test_helper.rb +14 -0
- data/vendor/gen-rb/cassandra.rb +1477 -0
- data/vendor/gen-rb/cassandra_constants.rb +12 -0
- data/vendor/gen-rb/cassandra_types.rb +479 -0
- metadata +173 -0
@@ -0,0 +1,141 @@
|
|
1
|
+
# OrderedHash is namespaced to prevent conflicts with other implementations
|
2
|
+
class Cassandra
|
3
|
+
# Hash is ordered in Ruby 1.9!
|
4
|
+
if RUBY_VERSION >= '1.9'
|
5
|
+
OrderedHash = ::Hash
|
6
|
+
else
|
7
|
+
class OrderedHash < Hash #:nodoc:
|
8
|
+
def initialize(*args, &block)
|
9
|
+
super
|
10
|
+
@keys = []
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.[](*args)
|
14
|
+
ordered_hash = new
|
15
|
+
|
16
|
+
if (args.length == 1 && args.first.is_a?(Array))
|
17
|
+
args.first.each do |key_value_pair|
|
18
|
+
next unless (key_value_pair.is_a?(Array))
|
19
|
+
ordered_hash[key_value_pair[0]] = key_value_pair[1]
|
20
|
+
end
|
21
|
+
|
22
|
+
return ordered_hash
|
23
|
+
end
|
24
|
+
|
25
|
+
unless (args.size % 2 == 0)
|
26
|
+
raise ArgumentError.new("odd number of arguments for Hash")
|
27
|
+
end
|
28
|
+
|
29
|
+
args.each_with_index do |val, ind|
|
30
|
+
next if (ind % 2 != 0)
|
31
|
+
ordered_hash[val] = args[ind + 1]
|
32
|
+
end
|
33
|
+
|
34
|
+
ordered_hash
|
35
|
+
end
|
36
|
+
|
37
|
+
def initialize_copy(other)
|
38
|
+
super
|
39
|
+
# make a deep copy of keys
|
40
|
+
@keys = other.keys
|
41
|
+
end
|
42
|
+
|
43
|
+
def []=(key, value)
|
44
|
+
@keys << key if !has_key?(key)
|
45
|
+
super
|
46
|
+
end
|
47
|
+
|
48
|
+
def delete(key)
|
49
|
+
if has_key? key
|
50
|
+
index = @keys.index(key)
|
51
|
+
@keys.delete_at index
|
52
|
+
end
|
53
|
+
super
|
54
|
+
end
|
55
|
+
|
56
|
+
def delete_if
|
57
|
+
super
|
58
|
+
sync_keys!
|
59
|
+
self
|
60
|
+
end
|
61
|
+
|
62
|
+
def reject!
|
63
|
+
super
|
64
|
+
sync_keys!
|
65
|
+
self
|
66
|
+
end
|
67
|
+
|
68
|
+
def reject(&block)
|
69
|
+
dup.reject!(&block)
|
70
|
+
end
|
71
|
+
|
72
|
+
def keys
|
73
|
+
@keys.dup
|
74
|
+
end
|
75
|
+
|
76
|
+
def values
|
77
|
+
@keys.collect { |key| self[key] }
|
78
|
+
end
|
79
|
+
|
80
|
+
def to_hash
|
81
|
+
self
|
82
|
+
end
|
83
|
+
|
84
|
+
def to_a
|
85
|
+
@keys.map { |key| [ key, self[key] ] }
|
86
|
+
end
|
87
|
+
|
88
|
+
def each_key
|
89
|
+
@keys.each { |key| yield key }
|
90
|
+
end
|
91
|
+
|
92
|
+
def each_value
|
93
|
+
@keys.each { |key| yield self[key]}
|
94
|
+
end
|
95
|
+
|
96
|
+
def each
|
97
|
+
@keys.each {|key| yield [key, self[key]]}
|
98
|
+
end
|
99
|
+
|
100
|
+
alias_method :each_pair, :each
|
101
|
+
|
102
|
+
def clear
|
103
|
+
super
|
104
|
+
@keys.clear
|
105
|
+
self
|
106
|
+
end
|
107
|
+
|
108
|
+
def shift
|
109
|
+
k = @keys.first
|
110
|
+
v = delete(k)
|
111
|
+
[k, v]
|
112
|
+
end
|
113
|
+
|
114
|
+
def merge!(other_hash)
|
115
|
+
other_hash.each {|k,v| self[k] = v }
|
116
|
+
self
|
117
|
+
end
|
118
|
+
|
119
|
+
def merge(other_hash)
|
120
|
+
dup.merge!(other_hash)
|
121
|
+
end
|
122
|
+
|
123
|
+
# When replacing with another hash, the initial order of our keys must come from the other hash -ordered or not.
|
124
|
+
def replace(other)
|
125
|
+
super
|
126
|
+
@keys = other.keys
|
127
|
+
self
|
128
|
+
end
|
129
|
+
|
130
|
+
def inspect
|
131
|
+
"#<OrderedHash #{super}>"
|
132
|
+
end
|
133
|
+
|
134
|
+
private
|
135
|
+
|
136
|
+
def sync_keys!
|
137
|
+
@keys.delete_if {|k| !has_key?(k)}
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
|
2
|
+
class Cassandra
|
3
|
+
# Inner methods for actually doing the Thrift calls
|
4
|
+
module Protocol #:nodoc:
|
5
|
+
private
|
6
|
+
|
7
|
+
def _mutate(mutation_map, consistency_level)
|
8
|
+
client.batch_mutate(@keyspace, mutation_map, consistency_level)
|
9
|
+
end
|
10
|
+
|
11
|
+
def _remove(key, column_path, timestamp, consistency_level)
|
12
|
+
client.remove(@keyspace, key, column_path, timestamp, consistency_level)
|
13
|
+
end
|
14
|
+
|
15
|
+
def _count_columns(column_family, key, super_column, consistency)
|
16
|
+
client.get_count(@keyspace, key,
|
17
|
+
CassandraThrift::ColumnParent.new(:column_family => column_family, :super_column => super_column),
|
18
|
+
consistency
|
19
|
+
)
|
20
|
+
end
|
21
|
+
|
22
|
+
def _get_columns(column_family, key, columns, sub_columns, consistency)
|
23
|
+
result = if is_super(column_family)
|
24
|
+
if sub_columns
|
25
|
+
columns_to_hash(column_family, client.get_slice(@keyspace, key,
|
26
|
+
CassandraThrift::ColumnParent.new(:column_family => column_family, :super_column => columns),
|
27
|
+
CassandraThrift::SlicePredicate.new(:column_names => sub_columns),
|
28
|
+
consistency))
|
29
|
+
else
|
30
|
+
columns_to_hash(column_family, client.get_slice(@keyspace, key,
|
31
|
+
CassandraThrift::ColumnParent.new(:column_family => column_family),
|
32
|
+
CassandraThrift::SlicePredicate.new(:column_names => columns),
|
33
|
+
consistency))
|
34
|
+
end
|
35
|
+
else
|
36
|
+
columns_to_hash(column_family, client.get_slice(@keyspace, key,
|
37
|
+
CassandraThrift::ColumnParent.new(:column_family => column_family),
|
38
|
+
CassandraThrift::SlicePredicate.new(:column_names => columns),
|
39
|
+
consistency))
|
40
|
+
end
|
41
|
+
|
42
|
+
klass = column_name_class(column_family)
|
43
|
+
(sub_columns || columns).map { |name| result[klass.new(name)] }
|
44
|
+
end
|
45
|
+
|
46
|
+
def _multiget(column_family, keys, column, sub_column, count, start, finish, reversed, consistency)
|
47
|
+
# Single values; count and range parameters have no effect
|
48
|
+
if is_super(column_family) and sub_column
|
49
|
+
column_path = CassandraThrift::ColumnPath.new(:column_family => column_family, :super_column => column, :column => sub_column)
|
50
|
+
multi_column_to_hash!(client.multiget(@keyspace, keys, column_path, consistency))
|
51
|
+
elsif !is_super(column_family) and column
|
52
|
+
column_path = CassandraThrift::ColumnPath.new(:column_family => column_family, :column => column)
|
53
|
+
multi_column_to_hash!(client.multiget(@keyspace, keys, column_path, consistency))
|
54
|
+
|
55
|
+
# Slices
|
56
|
+
else
|
57
|
+
predicate = CassandraThrift::SlicePredicate.new(:slice_range =>
|
58
|
+
CassandraThrift::SliceRange.new(
|
59
|
+
:reversed => reversed,
|
60
|
+
:count => count,
|
61
|
+
:start => start,
|
62
|
+
:finish => finish))
|
63
|
+
|
64
|
+
if is_super(column_family) and column
|
65
|
+
column_parent = CassandraThrift::ColumnParent.new(:column_family => column_family, :super_column => column)
|
66
|
+
multi_sub_columns_to_hash!(column_family, client.multiget_slice(@keyspace, keys, column_parent, predicate, consistency))
|
67
|
+
else
|
68
|
+
column_parent = CassandraThrift::ColumnParent.new(:column_family => column_family)
|
69
|
+
multi_columns_to_hash!(column_family, client.multiget_slice(@keyspace, keys, column_parent, predicate, consistency))
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def _get_range(column_family, start, finish, count, consistency)
|
75
|
+
column_parent = CassandraThrift::ColumnParent.new(:column_family => column_family)
|
76
|
+
predicate = CassandraThrift::SlicePredicate.new(:slice_range => CassandraThrift::SliceRange.new(:start => '', :finish => ''))
|
77
|
+
range = CassandraThrift::KeyRange.new(:start_key => start, :end_key => finish, :count => count)
|
78
|
+
client.get_range_slices(@keyspace, column_parent, predicate, range, 1)
|
79
|
+
end
|
80
|
+
|
81
|
+
def _get_range_keys(column_family, start, finish, count, consistency)
|
82
|
+
_get_range(column_family, start, finish, count, consistency).collect{|i| i.key }
|
83
|
+
end
|
84
|
+
|
85
|
+
def each_key(column_family)
|
86
|
+
column_parent = CassandraThrift::ColumnParent.new(:column_family => column_family.to_s)
|
87
|
+
predicate = CassandraThrift::SlicePredicate.new(:column_names => [])
|
88
|
+
range = CassandraThrift::KeyRange.new(:start_key => '', :end_key => '')
|
89
|
+
client.get_range_slices(@keyspace, column_parent, predicate, range, 1).each{|i| yield i.key }
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/test_helper')
|
2
|
+
|
3
|
+
class CassandraClientTest < Test::Unit::TestCase
|
4
|
+
include Cassandra::Constants
|
5
|
+
|
6
|
+
def setup
|
7
|
+
@twitter = Cassandra.new('Twitter', "127.0.0.1:9160", :retries => 2, :exception_classes => [])
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_client_method_is_called
|
11
|
+
assert_nil @twitter.instance_variable_get(:@client)
|
12
|
+
@twitter.insert(:Statuses, key, {'1' => 'v', '2' => 'v', '3' => 'v'})
|
13
|
+
assert_not_nil @twitter.instance_variable_get(:@client)
|
14
|
+
end
|
15
|
+
|
16
|
+
def key
|
17
|
+
caller.first[/`(.*?)'/, 1]
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/test_helper')
|
2
|
+
require 'cassandra_test'
|
3
|
+
require 'cassandra/mock'
|
4
|
+
|
5
|
+
class CassandraMockTest < CassandraTest
|
6
|
+
include Cassandra::Constants
|
7
|
+
|
8
|
+
def setup
|
9
|
+
storage_xml_path = File.expand_path(File.join(File.dirname(File.dirname(__FILE__)), 'conf', 'storage-conf.xml'))
|
10
|
+
@twitter = Cassandra::Mock.new('Twitter', storage_xml_path)
|
11
|
+
@twitter.clear_keyspace!
|
12
|
+
|
13
|
+
@blogs = Cassandra::Mock.new('Multiblog', storage_xml_path)
|
14
|
+
@blogs.clear_keyspace!
|
15
|
+
|
16
|
+
@blogs_long = Cassandra::Mock.new('MultiblogLong', storage_xml_path)
|
17
|
+
@blogs_long.clear_keyspace!
|
18
|
+
|
19
|
+
@uuids = (0..6).map {|i| SimpleUUID::UUID.new(Time.at(2**(24+i))) }
|
20
|
+
@longs = (0..6).map {|i| Long.new(Time.at(2**(24+i))) }
|
21
|
+
end
|
22
|
+
|
23
|
+
def test_setup
|
24
|
+
assert @twitter
|
25
|
+
assert @blogs
|
26
|
+
assert @blogs_long
|
27
|
+
end
|
28
|
+
|
29
|
+
def test_schema_for_keyspace
|
30
|
+
data = {
|
31
|
+
"StatusRelationships"=>{
|
32
|
+
"CompareSubcolumnsWith"=>"org.apache.cassandra.db.marshal.TimeUUIDType",
|
33
|
+
"CompareWith"=>"org.apache.cassandra.db.marshal.UTF8Type",
|
34
|
+
"Type"=>"Super"},
|
35
|
+
"StatusAudits"=>{
|
36
|
+
"CompareWith"=>"org.apache.cassandra.db.marshal.UTF8Type",
|
37
|
+
"Type"=>"Standard"},
|
38
|
+
"Statuses"=>{
|
39
|
+
"CompareWith"=>"org.apache.cassandra.db.marshal.UTF8Type",
|
40
|
+
"Type"=>"Standard"},
|
41
|
+
"UserRelationships"=>{
|
42
|
+
"CompareSubcolumnsWith"=>"org.apache.cassandra.db.marshal.TimeUUIDType",
|
43
|
+
"CompareWith"=>"org.apache.cassandra.db.marshal.UTF8Type",
|
44
|
+
"Type"=>"Super"},
|
45
|
+
"UserAudits"=>{
|
46
|
+
"CompareWith"=>"org.apache.cassandra.db.marshal.UTF8Type",
|
47
|
+
"Type"=>"Standard"},
|
48
|
+
"Users"=>{"CompareWith"=>"org.apache.cassandra.db.marshal.UTF8Type", "Type"=>"Standard"},
|
49
|
+
"TimelinishThings"=>
|
50
|
+
{"CompareWith"=>"org.apache.cassandra.db.marshal.BytesType", "Type"=>"Standard"}
|
51
|
+
}
|
52
|
+
stuff = @twitter.send(:schema_for_keyspace, 'Twitter')
|
53
|
+
data.keys.each do |k|
|
54
|
+
assert_equal data[k], stuff[k], k
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def test_sorting_row_keys
|
59
|
+
@twitter.insert(:Statuses, 'b', {:text => 'foo'})
|
60
|
+
@twitter.insert(:Statuses, 'a', {:text => 'foo'})
|
61
|
+
assert_equal ['a'], @twitter.get_range(:Statuses, :count => 1)
|
62
|
+
end
|
63
|
+
|
64
|
+
def test_inserting_array_for_indices
|
65
|
+
@twitter.insert(:TimelinishThings, 'a', ['1','2'])
|
66
|
+
row = @twitter.get(:TimelinishThings, 'a')
|
67
|
+
assert_equal({'1' => nil, '2' => nil}, row)
|
68
|
+
|
69
|
+
assert_raises(ArgumentError) {
|
70
|
+
@twitter.insert(:UserRelationships, 'a', ['u1','u2'])
|
71
|
+
}
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,370 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/test_helper')
|
2
|
+
|
3
|
+
class CassandraTest < Test::Unit::TestCase
|
4
|
+
include Cassandra::Constants
|
5
|
+
|
6
|
+
def setup
|
7
|
+
@twitter = Cassandra.new('Twitter', "127.0.0.1:9160", :retries => 2, :exception_classes => [])
|
8
|
+
@twitter.clear_keyspace!
|
9
|
+
|
10
|
+
@blogs = Cassandra.new('Multiblog')
|
11
|
+
@blogs.clear_keyspace!
|
12
|
+
|
13
|
+
@blogs_long = Cassandra.new('MultiblogLong')
|
14
|
+
@blogs_long.clear_keyspace!
|
15
|
+
|
16
|
+
@uuids = (0..6).map {|i| SimpleUUID::UUID.new(Time.at(2**(24+i))) }
|
17
|
+
@longs = (0..6).map {|i| Long.new(Time.at(2**(24+i))) }
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_inspect
|
21
|
+
assert_nothing_raised do
|
22
|
+
@blogs.inspect
|
23
|
+
@twitter.inspect
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def test_get_key
|
28
|
+
@twitter.insert(:Users, key, {'body' => 'v', 'user' => 'v'})
|
29
|
+
assert_equal({'body' => 'v', 'user' => 'v'}, @twitter.get(:Users, key))
|
30
|
+
assert_equal({}, @twitter.get(:Users, 'bogus'))
|
31
|
+
end
|
32
|
+
|
33
|
+
def test_get_key_preserving_order
|
34
|
+
# In-order hash is preserved
|
35
|
+
hash = OrderedHash['a', '', 'b', '', 'c', '', 'd', '',]
|
36
|
+
@twitter.insert(:Users, key, hash)
|
37
|
+
assert_equal(hash.keys, @twitter.get(:Users, key).keys)
|
38
|
+
|
39
|
+
@twitter.remove(:Users, key)
|
40
|
+
|
41
|
+
# Out-of-order hash is returned sorted
|
42
|
+
hash = OrderedHash['b', '', 'c', '', 'd', '', 'a', '']
|
43
|
+
@twitter.insert(:Users, key, hash)
|
44
|
+
assert_equal(hash.keys.sort, @twitter.get(:Users, key).keys)
|
45
|
+
assert_not_equal(hash.keys, @twitter.get(:Users, key).keys)
|
46
|
+
end
|
47
|
+
|
48
|
+
def test_get_first_time_uuid_column
|
49
|
+
@blogs.insert(:Blogs, key,
|
50
|
+
{@uuids[0] => 'I like this cat', @uuids[1] => 'Buttons is cuter', @uuids[2] => 'I disagree'})
|
51
|
+
|
52
|
+
assert_equal({@uuids[0] => 'I like this cat'}, @blogs.get(:Blogs, key, :count => 1))
|
53
|
+
assert_equal({@uuids[2] => 'I disagree'}, @blogs.get(:Blogs, key, :count => 1, :reversed => true))
|
54
|
+
assert_equal({}, @blogs.get(:Blogs, 'bogus'))
|
55
|
+
end
|
56
|
+
|
57
|
+
def test_get_multiple_time_uuid_columns
|
58
|
+
@blogs.insert(:Blogs, key,
|
59
|
+
{@uuids[0] => 'I like this cat', @uuids[1] => 'Buttons is cuter', @uuids[2] => 'I disagree'})
|
60
|
+
|
61
|
+
assert_equal(['I like this cat', 'Buttons is cuter'], @blogs.get_columns(:Blogs, key, @uuids[0..1]))
|
62
|
+
end
|
63
|
+
|
64
|
+
def test_get_first_long_column
|
65
|
+
@blogs_long.insert(:Blogs, key,
|
66
|
+
{@longs[0] => 'I like this cat', @longs[1] => 'Buttons is cuter', @longs[2] => 'I disagree'})
|
67
|
+
|
68
|
+
assert_equal({@longs[0] => 'I like this cat'}, @blogs_long.get(:Blogs, key, :count => 1))
|
69
|
+
assert_equal({@longs[2] => 'I disagree'}, @blogs_long.get(:Blogs, key, :count => 1, :reversed => true))
|
70
|
+
assert_equal({}, @blogs_long.get(:Blogs, 'bogus'))
|
71
|
+
end
|
72
|
+
|
73
|
+
def test_long_remove_bug
|
74
|
+
@blogs_long.insert(:Blogs, key, {@longs[0] => 'I like this cat'})
|
75
|
+
@blogs_long.remove(:Blogs, key)
|
76
|
+
assert_equal({}, @blogs_long.get(:Blogs, key, :count => 1))
|
77
|
+
|
78
|
+
@blogs_long.insert(:Blogs, key, {@longs[0] => 'I really like this cat'})
|
79
|
+
assert_equal({@longs[0] => 'I really like this cat'}, @blogs_long.get(:Blogs, key, :count => 1))
|
80
|
+
end
|
81
|
+
|
82
|
+
def test_get_with_count
|
83
|
+
@twitter.insert(:Statuses, key, {'1' => 'v', '2' => 'v', '3' => 'v'})
|
84
|
+
assert_equal 1, @twitter.get(:Statuses, key, :count => 1).size
|
85
|
+
assert_equal 2, @twitter.get(:Statuses, key, :count => 2).size
|
86
|
+
end
|
87
|
+
|
88
|
+
def test_get_value
|
89
|
+
@twitter.insert(:Statuses, key, {'body' => 'v'})
|
90
|
+
assert_equal 'v', @twitter.get(:Statuses, key, 'body')
|
91
|
+
assert_nil @twitter.get(:Statuses, 'bogus', 'body')
|
92
|
+
|
93
|
+
assert @twitter.exists?(:Statuses, key, 'body')
|
94
|
+
assert !@twitter.exists?(:Statuses, 'bogus', 'body')
|
95
|
+
end
|
96
|
+
|
97
|
+
def test_exists_with_only_key
|
98
|
+
@twitter.insert(:Statuses, key, {'body' => 'v'})
|
99
|
+
assert @twitter.exists?(:Statuses, key)
|
100
|
+
end
|
101
|
+
|
102
|
+
def test_get_super_key
|
103
|
+
columns = {'user_timelines' => {@uuids[4] => '4', @uuids[5] => '5'}}
|
104
|
+
@twitter.insert(:StatusRelationships, key, columns)
|
105
|
+
assert_equal(columns, @twitter.get(:StatusRelationships, key))
|
106
|
+
assert_equal({}, @twitter.get(:StatusRelationships, 'bogus'))
|
107
|
+
end
|
108
|
+
|
109
|
+
def test_get_several_super_keys
|
110
|
+
columns = {
|
111
|
+
'user_timelines' => {@uuids[1] => 'v1'},
|
112
|
+
'mentions_timelines' => {@uuids[2] => 'v2'}}
|
113
|
+
@twitter.insert(:StatusRelationships, key, columns)
|
114
|
+
|
115
|
+
assert_equal(columns, @twitter.get(:StatusRelationships, key))
|
116
|
+
assert_equal({}, @twitter.get(:StatusRelationships, 'bogus'))
|
117
|
+
end
|
118
|
+
|
119
|
+
def test_get_super_sub_keys_with_count
|
120
|
+
@twitter.insert(:StatusRelationships, key,
|
121
|
+
{'user_timelines' => {@uuids[1] => 'v1', @uuids[2] => 'v2', @uuids[3] => 'v3'}})
|
122
|
+
assert_equal({@uuids[1] => 'v1'},
|
123
|
+
@twitter.get(:StatusRelationships, key, "user_timelines", :count => 1))
|
124
|
+
assert_equal({@uuids[3] => 'v3'},
|
125
|
+
@twitter.get(:StatusRelationships, key, "user_timelines", :count => 1, :reversed => true))
|
126
|
+
end
|
127
|
+
|
128
|
+
def test_get_super_sub_keys_with_ranges
|
129
|
+
@twitter.insert(:StatusRelationships, key,
|
130
|
+
{'user_timelines' => {
|
131
|
+
@uuids[1] => 'v1',
|
132
|
+
@uuids[2] => 'v2',
|
133
|
+
@uuids[3] => 'v3',
|
134
|
+
@uuids[4] => 'v4',
|
135
|
+
@uuids[5] => 'v5'}})
|
136
|
+
|
137
|
+
keys = @twitter.get(:StatusRelationships, key, "user_timelines").keys
|
138
|
+
assert_equal keys.sort, keys
|
139
|
+
assert_equal({@uuids[1] => 'v1'}, @twitter.get(:StatusRelationships, key, "user_timelines", :finish => @uuids[2], :count => 1))
|
140
|
+
assert_equal({@uuids[2] => 'v2'}, @twitter.get(:StatusRelationships, key, "user_timelines", :start => @uuids[2], :count => 1))
|
141
|
+
assert_equal 4, @twitter.get(:StatusRelationships, key, "user_timelines", :start => @uuids[2], :finish => @uuids[5]).size
|
142
|
+
end
|
143
|
+
|
144
|
+
def test_get_super_sub_key
|
145
|
+
columns = {@uuids[1] => 'v1', @uuids[2] => 'v2'}
|
146
|
+
@twitter.insert(:StatusRelationships, key, {'user_timelines' => columns})
|
147
|
+
assert_equal(columns, @twitter.get(:StatusRelationships, key, 'user_timelines'))
|
148
|
+
assert_equal({}, @twitter.get(:StatusRelationships, 'bogus', 'user_timelines'))
|
149
|
+
# FIXME Not sure if this is valid
|
150
|
+
# assert_nil @twitter.exists?(:StatusRelationships, 'bogus', 'user_timelines')
|
151
|
+
end
|
152
|
+
|
153
|
+
def test_get_super_value
|
154
|
+
columns = {@uuids[1] => 'v1'}
|
155
|
+
@twitter.insert(:StatusRelationships, key, {'user_timelines' => columns})
|
156
|
+
assert_equal('v1', @twitter.get(:StatusRelationships, key, 'user_timelines', columns.keys.first))
|
157
|
+
assert_nil @twitter.get(:StatusRelationships, 'bogus', 'user_timelines', columns.keys.first)
|
158
|
+
end
|
159
|
+
|
160
|
+
|
161
|
+
#TODO: add a OPP keyspace for this
|
162
|
+
# def test_get_range
|
163
|
+
# @twitter.insert(:Statuses, '2', {'body' => '1'})
|
164
|
+
# @twitter.insert(:Statuses, '3', {'body' => '1'})
|
165
|
+
# @twitter.insert(:Statuses, '4', {'body' => '1'})
|
166
|
+
# @twitter.insert(:Statuses, '5', {'body' => '1'})
|
167
|
+
# @twitter.insert(:Statuses, '6', {'body' => '1'})
|
168
|
+
# assert_equal(['3', '4', '5'], @twitter.get_range(:Statuses, :start => '3', :finish => '5'))
|
169
|
+
# end
|
170
|
+
|
171
|
+
def test_get_range_count
|
172
|
+
@twitter.insert(:Statuses, '2', {'body' => '1'})
|
173
|
+
@twitter.insert(:Statuses, '3', {'body' => '1'})
|
174
|
+
@twitter.insert(:Statuses, '4', {'body' => '1'})
|
175
|
+
@twitter.insert(:Statuses, '5', {'body' => '1'})
|
176
|
+
@twitter.insert(:Statuses, '6', {'body' => '1'})
|
177
|
+
assert_equal(3, @twitter.get_range(:Statuses, :count => 3).size)
|
178
|
+
end
|
179
|
+
|
180
|
+
def test_multi_get
|
181
|
+
@twitter.insert(:Users, key + '1', {'body' => 'v1', 'user' => 'v1'})
|
182
|
+
@twitter.insert(:Users, key + '2', {'body' => 'v2', 'user' => 'v2'})
|
183
|
+
|
184
|
+
expected = OrderedHash[key + '1', {'body' => 'v1', 'user' => 'v1'}, key + '2', {'body' => 'v2', 'user' => 'v2'}, 'bogus', {}]
|
185
|
+
result = @twitter.multi_get(:Users, [key + '1', key + '2', 'bogus'])
|
186
|
+
assert_equal expected, result
|
187
|
+
assert_equal expected.keys, result.keys
|
188
|
+
|
189
|
+
expected = OrderedHash[key + '2', {'body' => 'v2', 'user' => 'v2'}, 'bogus', {}, key + '1', {'body' => 'v1', 'user' => 'v1'}]
|
190
|
+
result = @twitter.multi_get(:Users, [key + '2', 'bogus', key + '1'])
|
191
|
+
assert_equal expected, result
|
192
|
+
assert_equal expected.keys, result.keys
|
193
|
+
end
|
194
|
+
|
195
|
+
def test_remove_key
|
196
|
+
@twitter.insert(:Statuses, key, {'body' => 'v'})
|
197
|
+
assert_equal({'body' => 'v'}, @twitter.get(:Statuses, key))
|
198
|
+
|
199
|
+
@twitter.remove(:Statuses, key)
|
200
|
+
assert_equal({}, @twitter.get(:Statuses, key))
|
201
|
+
end
|
202
|
+
|
203
|
+
def test_remove_value
|
204
|
+
@twitter.insert(:Statuses, key, {'body' => 'v'})
|
205
|
+
@twitter.remove(:Statuses, key, 'body')
|
206
|
+
assert_nil @twitter.get(:Statuses, key, 'body')
|
207
|
+
end
|
208
|
+
|
209
|
+
def test_remove_super_key
|
210
|
+
@twitter.insert(:StatusRelationships, key, {'user_timelines' => {@uuids[1] => 'v1'}})
|
211
|
+
@twitter.remove(:StatusRelationships, key)
|
212
|
+
assert_equal({}, @twitter.get(:StatusRelationships, key))
|
213
|
+
end
|
214
|
+
|
215
|
+
def test_remove_super_sub_key
|
216
|
+
@twitter.insert(:StatusRelationships, key, {'user_timelines' => {@uuids[1] => 'v1'}})
|
217
|
+
@twitter.remove(:StatusRelationships, key, 'user_timelines')
|
218
|
+
assert_equal({}, @twitter.get(:StatusRelationships, key, 'user_timelines'))
|
219
|
+
end
|
220
|
+
|
221
|
+
def test_remove_super_value
|
222
|
+
columns = {@uuids[1] => 'v1'}
|
223
|
+
@twitter.insert(:StatusRelationships, key, {'user_timelines' => columns})
|
224
|
+
@twitter.remove(:StatusRelationships, key, 'user_timelines', columns.keys.first)
|
225
|
+
assert_nil @twitter.get(:StatusRelationships, key, 'user_timelines', columns.keys.first)
|
226
|
+
end
|
227
|
+
|
228
|
+
def test_clear_column_family
|
229
|
+
@twitter.insert(:Statuses, key + "1", {'body' => '1'})
|
230
|
+
@twitter.insert(:Statuses, key + "2", {'body' => '2'})
|
231
|
+
@twitter.insert(:Statuses, key + "3", {'body' => '3'})
|
232
|
+
@twitter.clear_column_family!(:Statuses)
|
233
|
+
assert_equal 0, @twitter.count_range(:Statuses)
|
234
|
+
end
|
235
|
+
|
236
|
+
def test_insert_key
|
237
|
+
@twitter.insert(:Statuses, key, {'body' => 'v', 'user' => 'v'})
|
238
|
+
assert_equal({'body' => 'v', 'user' => 'v'}, @twitter.get(:Statuses, key))
|
239
|
+
end
|
240
|
+
|
241
|
+
def test_insert_super_key
|
242
|
+
columns = {@uuids[1] => 'v1', @uuids[2] => 'v2'}
|
243
|
+
@twitter.insert(:StatusRelationships, key, {'user_timelines' => columns})
|
244
|
+
assert_equal(columns, @twitter.get(:StatusRelationships, key, 'user_timelines'))
|
245
|
+
end
|
246
|
+
|
247
|
+
def test_get_columns
|
248
|
+
@twitter.insert(:Statuses, key, {'body' => 'v1', 'user' => 'v2'})
|
249
|
+
assert_equal(['v1' , 'v2'], @twitter.get_columns(:Statuses, key, ['body', 'user']))
|
250
|
+
end
|
251
|
+
|
252
|
+
def test_get_column_values_super
|
253
|
+
user_columns, mentions_columns = {@uuids[1] => 'v1'}, {@uuids[2] => 'v2'}
|
254
|
+
@twitter.insert(:StatusRelationships, key,
|
255
|
+
{'user_timelines' => user_columns, 'mentions_timelines' => mentions_columns})
|
256
|
+
assert_equal [user_columns, mentions_columns],
|
257
|
+
@twitter.get_columns(:StatusRelationships, key, ['user_timelines', 'mentions_timelines'])
|
258
|
+
end
|
259
|
+
|
260
|
+
def test_get_sub_column_values_super
|
261
|
+
user_columns = {@uuids[1] => 'v1', @uuids[2] => 'v2'}
|
262
|
+
@twitter.insert(:StatusRelationships, key, {'user_timelines' => user_columns})
|
263
|
+
assert_equal ['v1', 'v2'],
|
264
|
+
@twitter.get_columns(:StatusRelationships, key, 'user_timelines', @uuids[1..2])
|
265
|
+
end
|
266
|
+
|
267
|
+
def test_multi_get_columns
|
268
|
+
@twitter.insert(:Users, key + '1', {'body' => 'v1', 'user' => 'v1'})
|
269
|
+
@twitter.insert(:Users, key + '2', {'body' => 'v2', 'user' => 'v2'})
|
270
|
+
assert_equal(
|
271
|
+
OrderedHash[key + '1', ['v1', 'v1'], key + '2', ['v2', 'v2'], 'bogus', [nil, nil]],
|
272
|
+
@twitter.multi_get_columns(:Users, [key + '1', key + '2', 'bogus'], ['body', 'user']))
|
273
|
+
assert_equal(
|
274
|
+
OrderedHash[key + '2', ['v2', 'v2'], 'bogus', [nil, nil], key + '1', ['v1', 'v1']],
|
275
|
+
@twitter.multi_get_columns(:Users, [key + '2', 'bogus', key + '1'], ['body', 'user']))
|
276
|
+
end
|
277
|
+
|
278
|
+
def test_count_keys
|
279
|
+
@twitter.insert(:Statuses, key + "1", {'body' => '1'})
|
280
|
+
@twitter.insert(:Statuses, key + "2", {'body' => '2'})
|
281
|
+
@twitter.insert(:Statuses, key + "3", {'body' => '3'})
|
282
|
+
assert_equal 3, @twitter.count_range(:Statuses)
|
283
|
+
end
|
284
|
+
|
285
|
+
def test_count_columns
|
286
|
+
@twitter.insert(:Statuses, key, {'body' => 'v1', 'user' => 'v2'})
|
287
|
+
assert_equal 2, @twitter.count_columns(:Statuses, key)
|
288
|
+
end
|
289
|
+
|
290
|
+
def test_count_super_columns
|
291
|
+
@twitter.insert(:StatusRelationships, key, {
|
292
|
+
'user_timelines' => {@uuids[1] => 'v1'},
|
293
|
+
'mentions_timelines' => {@uuids[2] => 'v2'}})
|
294
|
+
assert_equal 2, @twitter.count_columns(:StatusRelationships, key)
|
295
|
+
end
|
296
|
+
|
297
|
+
def test_count_super_sub_columns
|
298
|
+
@twitter.insert(:StatusRelationships, key, {'user_timelines' => {@uuids[1] => 'v1', @uuids[2] => 'v2'}})
|
299
|
+
assert_equal 2, @twitter.count_columns(:StatusRelationships, key, 'user_timelines')
|
300
|
+
end
|
301
|
+
|
302
|
+
def test_multi_count_columns
|
303
|
+
@twitter.insert(:Users, key + '1', {'body' => 'v1', 'user' => 'v1'})
|
304
|
+
@twitter.insert(:Users, key + '2', {'body' => 'v2', 'user' => 'v2'})
|
305
|
+
assert_equal(
|
306
|
+
OrderedHash[key + '1', 2, key + '2', 2, 'bogus', 0],
|
307
|
+
@twitter.multi_count_columns(:Users, [key + '1', key + '2', 'bogus']))
|
308
|
+
assert_equal(
|
309
|
+
OrderedHash[key + '2', 2, 'bogus', 0, key + '1', 2],
|
310
|
+
@twitter.multi_count_columns(:Users, [key + '2', 'bogus', key + '1']))
|
311
|
+
end
|
312
|
+
|
313
|
+
def test_batch_mutate
|
314
|
+
k = key
|
315
|
+
|
316
|
+
@twitter.insert(:Users, k + '1', {'body' => 'v1', 'user' => 'v1'})
|
317
|
+
|
318
|
+
@twitter.batch do
|
319
|
+
@twitter.insert(:Users, k + '2', {'body' => 'v2', 'user' => 'v2'})
|
320
|
+
@twitter.insert(:Users, k + '3', {'body' => 'bogus', 'user' => 'v3'})
|
321
|
+
@twitter.insert(:Users, k + '3', {'body' => 'v3', 'location' => 'v3'})
|
322
|
+
@twitter.insert(:Statuses, k + '3', {'body' => 'v'})
|
323
|
+
|
324
|
+
assert_equal({'body' => 'v1', 'user' => 'v1'}, @twitter.get(:Users, k + '1')) # Written
|
325
|
+
assert_equal({}, @twitter.get(:Users, k + '2')) # Not yet written
|
326
|
+
assert_equal({}, @twitter.get(:Statuses, k + '3')) # Not yet written
|
327
|
+
|
328
|
+
@twitter.remove(:Users, k + '1')
|
329
|
+
assert_equal({'body' => 'v1', 'user' => 'v1'}, @twitter.get(:Users, k + '1')) # Not yet removed
|
330
|
+
|
331
|
+
@twitter.remove(:Users, k + '4')
|
332
|
+
@twitter.insert(:Users, k + '4', {'body' => 'v4', 'user' => 'v4'})
|
333
|
+
assert_equal({}, @twitter.get(:Users, k + '4')) # Not yet written
|
334
|
+
end
|
335
|
+
|
336
|
+
assert_equal({'body' => 'v2', 'user' => 'v2'}, @twitter.get(:Users, k + '2')) # Written
|
337
|
+
assert_equal({'body' => 'v3', 'user' => 'v3', 'location' => 'v3'}, @twitter.get(:Users, k + '3')) # Written and compacted
|
338
|
+
assert_equal({'body' => 'v4', 'user' => 'v4'}, @twitter.get(:Users, k + '4')) # Written
|
339
|
+
assert_equal({'body' => 'v'}, @twitter.get(:Statuses, k + '3')) # Written
|
340
|
+
assert_equal({}, @twitter.get(:Users, k + '1')) # Removed
|
341
|
+
end
|
342
|
+
|
343
|
+
def test_complain_about_nil_key
|
344
|
+
assert_raises(ArgumentError) do
|
345
|
+
@twitter.insert(:Statuses, nil, {'text' => 'crap'})
|
346
|
+
end
|
347
|
+
end
|
348
|
+
|
349
|
+
def test_raise_access_error_on_nonexistent_keyspace
|
350
|
+
nonexistent = Cassandra.new('Nonexistent')
|
351
|
+
assert_raises(Cassandra::AccessError) do
|
352
|
+
nonexistent.get "foo", "bar"
|
353
|
+
end
|
354
|
+
end
|
355
|
+
|
356
|
+
def test_nil_sub_column_value
|
357
|
+
@twitter.insert(:Index, 'asdf', {"thing" => {'jkl' => nil} })
|
358
|
+
end
|
359
|
+
|
360
|
+
def test_disconnect!
|
361
|
+
@twitter.disconnect!
|
362
|
+
assert_nil @twitter.instance_variable_get(:@client)
|
363
|
+
end
|
364
|
+
|
365
|
+
private
|
366
|
+
|
367
|
+
def key
|
368
|
+
caller.first[/`(.*?)'/, 1]
|
369
|
+
end
|
370
|
+
end
|