cassandra_client 0.1 → 0.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.tar.gz.sig +0 -0
- data/CHANGELOG +2 -0
- data/Manifest +3 -2
- data/README +9 -12
- data/Rakefile +1 -0
- data/cassandra_client.gemspec +4 -4
- data/conf/storage-conf.xml +12 -15
- data/lib/cassandra_client.rb +3 -2
- data/lib/cassandra_client/cassandra_client.rb +170 -0
- data/lib/cassandra_client/helper.rb +57 -0
- data/lib/cassandra_client/ordered_hash.rb +23 -22
- data/lib/cassandra_client/safe_client.rb +18 -0
- data/test/cassandra_client_test.rb +85 -81
- metadata +10 -6
- metadata.gz.sig +0 -0
- data/lib/cassandra_client/client.rb +0 -65
- data/lib/cassandra_client/table.rb +0 -202
data.tar.gz.sig
CHANGED
Binary file
|
data/CHANGELOG
CHANGED
data/Manifest
CHANGED
@@ -2,10 +2,11 @@ CHANGELOG
|
|
2
2
|
conf/cassandra.in.sh
|
3
3
|
conf/log4j.properties
|
4
4
|
conf/storage-conf.xml
|
5
|
-
lib/cassandra_client/
|
5
|
+
lib/cassandra_client/cassandra_client.rb
|
6
|
+
lib/cassandra_client/helper.rb
|
6
7
|
lib/cassandra_client/ordered_hash.rb
|
8
|
+
lib/cassandra_client/safe_client.rb
|
7
9
|
lib/cassandra_client/serialization.rb
|
8
|
-
lib/cassandra_client/table.rb
|
9
10
|
lib/cassandra_client.rb
|
10
11
|
LICENSE
|
11
12
|
Manifest
|
data/README
CHANGED
@@ -19,7 +19,9 @@ The public certificate for this gem is here[http://rubyforge.org/frs/download.ph
|
|
19
19
|
|
20
20
|
This is an alpha release and does not yet support the full Thrift API.
|
21
21
|
|
22
|
-
Cassandra is a rapidly moving target; this library is currently tested
|
22
|
+
Cassandra is a rapidly moving target; this library is currently tested against {Cassandra trunk revision 789419}[http://blog.evanweaver.com/files/cassandra/cassandra-r789419.tar.bz2].
|
23
|
+
|
24
|
+
The Github source repository is {here}[http://github.com/fauna/cassandra_client/]; patches and contributions are very welcome.
|
23
25
|
|
24
26
|
== Installation
|
25
27
|
|
@@ -35,32 +37,27 @@ Require the library:
|
|
35
37
|
|
36
38
|
require 'cassandra_client'
|
37
39
|
|
38
|
-
Connect to a server:
|
40
|
+
Connect to a server and keyspace:
|
39
41
|
|
40
|
-
client = CassandraClient.new("127.0.0.1")
|
42
|
+
client = CassandraClient.new('Twitter', "127.0.0.1")
|
41
43
|
|
42
|
-
Get a keyspace:
|
43
|
-
|
44
|
-
users = client.table('Users')
|
45
|
-
|
46
44
|
Insert into a column family. You can insert a CassandraClient::OrderedHash, or a regular Hash, if order doesn't matter:
|
47
45
|
|
48
|
-
|
46
|
+
client.insert(:Users, "5", {'screen_name' => "buttonscat"})
|
49
47
|
|
50
48
|
Insert into a super column family:
|
51
49
|
|
52
|
-
|
50
|
+
client.insert(:UserRelationships, "5", {"user_timeline" => {"1" => ""}})
|
53
51
|
|
54
52
|
Query a super column:
|
55
53
|
|
56
|
-
timeline =
|
54
|
+
timeline = client.get(:UserRelationships, "5", "user_timeline")
|
57
55
|
|
58
56
|
The returned result will always be a CassandraClient::OrderedHash.
|
59
57
|
|
60
|
-
See CassandraClient::Table for more methods.
|
58
|
+
See CassandraClient and CassandraClient::Table for more methods.
|
61
59
|
|
62
60
|
== Reporting problems
|
63
61
|
|
64
62
|
The Github issue tracker is {here}[http://github.com/fauna/cassandra_client/issues]. If you have problems with Cassandra itself, please use the {cassandra-user mailing list}[http://mail-archives.apache.org/mod_mbox/incubator-cassandra-user/].
|
65
63
|
|
66
|
-
Patches and contributions are very welcome. Please note that contributors are required to assign copyright for their additions to Twitter, Inc.
|
data/Rakefile
CHANGED
@@ -7,6 +7,7 @@ Echoe.new("cassandra_client") do |p|
|
|
7
7
|
p.rubygems_version = ">= 0.8"
|
8
8
|
p.dependencies = ['json']
|
9
9
|
p.ignore_pattern = /^(data|vendor\/cassandra|cassandra-r789419|vendor\/thrift)/
|
10
|
+
p.rdoc_pattern = /^(lib|bin|tasks|ext)|_types.rb|_constants.rb|^README|^CHANGELOG|^TODO|^LICENSE|^COPYING$/
|
10
11
|
p.url = "http://blog.evanweaver.com/files/doc/fauna/cassandra_client/"
|
11
12
|
p.docs_host = "blog.evanweaver.com:~/www/bax/public/files/doc/"
|
12
13
|
end
|
data/cassandra_client.gemspec
CHANGED
@@ -2,16 +2,16 @@
|
|
2
2
|
|
3
3
|
Gem::Specification.new do |s|
|
4
4
|
s.name = %q{cassandra_client}
|
5
|
-
s.version = "0.
|
5
|
+
s.version = "0.2"
|
6
6
|
|
7
7
|
s.required_rubygems_version = Gem::Requirement.new(">= 0.8") if s.respond_to? :required_rubygems_version=
|
8
8
|
s.authors = ["Evan Weaver"]
|
9
9
|
s.cert_chain = ["/Users/eweaver/p/configuration/gem_certificates/evan_weaver-original-public_cert.pem"]
|
10
|
-
s.date = %q{2009-07-
|
10
|
+
s.date = %q{2009-07-06}
|
11
11
|
s.description = %q{A Ruby client for CassandraDB.}
|
12
12
|
s.email = %q{}
|
13
|
-
s.extra_rdoc_files = ["CHANGELOG", "lib/cassandra_client/
|
14
|
-
s.files = ["CHANGELOG", "conf/cassandra.in.sh", "conf/log4j.properties", "conf/storage-conf.xml", "lib/cassandra_client/
|
13
|
+
s.extra_rdoc_files = ["CHANGELOG", "lib/cassandra_client/cassandra_client.rb", "lib/cassandra_client/helper.rb", "lib/cassandra_client/ordered_hash.rb", "lib/cassandra_client/safe_client.rb", "lib/cassandra_client/serialization.rb", "lib/cassandra_client.rb", "LICENSE", "README", "vendor/gen-rb/cassandra_constants.rb", "vendor/gen-rb/cassandra_types.rb"]
|
14
|
+
s.files = ["CHANGELOG", "conf/cassandra.in.sh", "conf/log4j.properties", "conf/storage-conf.xml", "lib/cassandra_client/cassandra_client.rb", "lib/cassandra_client/helper.rb", "lib/cassandra_client/ordered_hash.rb", "lib/cassandra_client/safe_client.rb", "lib/cassandra_client/serialization.rb", "lib/cassandra_client.rb", "LICENSE", "Manifest", "quickstart.sh", "Rakefile", "README", "test/cassandra_client_test.rb", "vendor/gen-rb/cassandra.rb", "vendor/gen-rb/cassandra_constants.rb", "vendor/gen-rb/cassandra_types.rb", "cassandra_client.gemspec"]
|
15
15
|
s.homepage = %q{http://blog.evanweaver.com/files/doc/fauna/cassandra_client/}
|
16
16
|
s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Cassandra_client", "--main", "README"]
|
17
17
|
s.require_paths = ["lib"]
|
data/conf/storage-conf.xml
CHANGED
@@ -20,7 +20,7 @@
|
|
20
20
|
<!--======================================================================-->
|
21
21
|
<!-- Basic Configuration -->
|
22
22
|
<!--======================================================================-->
|
23
|
-
<ClusterName>
|
23
|
+
<ClusterName>Test</ClusterName>
|
24
24
|
|
25
25
|
<!-- Tables and ColumnFamilies
|
26
26
|
Think of a table as a namespace, not a relational table.
|
@@ -29,22 +29,19 @@
|
|
29
29
|
There is an implicit table named 'system' for Cassandra internals.
|
30
30
|
-->
|
31
31
|
<Tables>
|
32
|
-
<Table Name="
|
33
|
-
<ColumnFamily ColumnSort="Name" Name="
|
34
|
-
<ColumnFamily ColumnSort="Name" Name="
|
35
|
-
<ColumnFamily ColumnType="Super" ColumnSort="Name" Name="
|
36
|
-
<ColumnFamily ColumnSort="Time" Name="
|
32
|
+
<Table Name="Twitter">
|
33
|
+
<ColumnFamily ColumnSort="Name" Name="Users" />
|
34
|
+
<ColumnFamily ColumnSort="Name" Name="UserAudits" />
|
35
|
+
<ColumnFamily ColumnType="Super" ColumnSort="Name" Name="UserRelationships" />
|
36
|
+
<ColumnFamily ColumnSort="Time" Name="Usernames" />
|
37
|
+
<ColumnFamily ColumnSort="Time" Name="Statuses" />
|
38
|
+
<ColumnFamily ColumnSort="Name" Name="StatusAudits" />
|
39
|
+
<ColumnFamily ColumnType="Super" ColumnSort="Name" Name="StatusRelationships" />
|
37
40
|
</Table>
|
38
41
|
|
39
|
-
<Table Name="
|
40
|
-
|
41
|
-
|
42
|
-
<ColumnFamily ColumnType="Super" ColumnSort="Name" Name="relationships" />
|
43
|
-
</Table>
|
44
|
-
|
45
|
-
<Table Name="Blogs">
|
46
|
-
<ColumnFamily ColumnSort="Time" Name="posts"/>
|
47
|
-
<ColumnFamily ColumnSort="Time" Name="comments"/>
|
42
|
+
<Table Name="Multiblog">
|
43
|
+
<ColumnFamily ColumnSort="Time" Name="Blogs"/>
|
44
|
+
<ColumnFamily ColumnSort="Time" Name="Comments"/>
|
48
45
|
</Table>
|
49
46
|
</Tables>
|
50
47
|
|
data/lib/cassandra_client.rb
CHANGED
@@ -5,10 +5,11 @@ require 'thrift'
|
|
5
5
|
|
6
6
|
HERE = File.expand_path(File.dirname(__FILE__))
|
7
7
|
|
8
|
-
require "#{HERE}/cassandra_client/
|
9
|
-
require "#{HERE}/cassandra_client/
|
8
|
+
require "#{HERE}/cassandra_client/helper"
|
9
|
+
require "#{HERE}/cassandra_client/safe_client"
|
10
10
|
require "#{HERE}/cassandra_client/serialization"
|
11
11
|
require "#{HERE}/cassandra_client/ordered_hash"
|
12
|
+
require "#{HERE}/cassandra_client/cassandra_client"
|
12
13
|
|
13
14
|
$LOAD_PATH << "#{HERE}/../vendor/gen-rb"
|
14
15
|
require 'cassandra'
|
@@ -0,0 +1,170 @@
|
|
1
|
+
class CassandraClient
|
2
|
+
include Helper
|
3
|
+
class AccessError < StandardError; end
|
4
|
+
|
5
|
+
MAX_INT = 2**31 - 1
|
6
|
+
|
7
|
+
attr_reader :keyspace, :host, :port, :quorum, :serialization, :transport, :client, :schema
|
8
|
+
|
9
|
+
# Instantiate a new CassandraClient and open the connection.
|
10
|
+
def initialize(keyspace, host = '127.0.0.1', port = 9160, quorum = 1, serialization = CassandraClient::Serialization::JSON)
|
11
|
+
@keyspace = keyspace
|
12
|
+
@host = host
|
13
|
+
@port = port
|
14
|
+
@quorum = quorum
|
15
|
+
@serialization = serialization
|
16
|
+
|
17
|
+
extend(@serialization)
|
18
|
+
|
19
|
+
@transport = Thrift::BufferedTransport.new(Thrift::Socket.new(@host, @port))
|
20
|
+
@transport.open
|
21
|
+
@client = Cassandra::SafeClient.new(
|
22
|
+
Cassandra::Client.new(Thrift::BinaryProtocol.new(@transport)),
|
23
|
+
@transport)
|
24
|
+
|
25
|
+
keyspaces = @client.getStringListProperty("tables")
|
26
|
+
unless keyspaces.include?(@keyspace)
|
27
|
+
raise AccessError, "Keyspace #{@keyspace.inspect} not found. Available: #{keyspaces.inspect}"
|
28
|
+
end
|
29
|
+
|
30
|
+
@schema = @client.describeTable(@keyspace)
|
31
|
+
end
|
32
|
+
|
33
|
+
def inspect
|
34
|
+
"#<CassandraClient:#{object_id}, @keyspace=#{keyspace.inspect}, @schema={#{
|
35
|
+
schema.map {|name, hash| ":#{name} => #{hash['type'].inspect}"}.join(', ')
|
36
|
+
}}, @host=#{host.inspect}, @port=#{port}, @quorum=#{quorum}, @serialization=#{serialization.name}>"
|
37
|
+
end
|
38
|
+
|
39
|
+
## Write
|
40
|
+
|
41
|
+
# Insert a row for a key. Pass a flat hash for a regular column family, and
|
42
|
+
# a nested hash for a super column family.
|
43
|
+
def insert(column_family, key, hash, timestamp = now)
|
44
|
+
column_family = column_family.to_s
|
45
|
+
insert = is_super(column_family) ? :insert_super : :insert_standard
|
46
|
+
send(insert, column_family, key, hash, timestamp)
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def insert_standard(column_family, key, hash, timestamp = now)
|
52
|
+
mutation = Batch_mutation_t.new(
|
53
|
+
:table => @keyspace,
|
54
|
+
:key => key,
|
55
|
+
:cfmap => {column_family => hash_to_columns(hash, timestamp)})
|
56
|
+
@client.batch_insert(mutation, @quorum)
|
57
|
+
end
|
58
|
+
|
59
|
+
def insert_super(column_family, key, hash, timestamp = now)
|
60
|
+
mutation = Batch_mutation_super_t.new(
|
61
|
+
:table => @keyspace,
|
62
|
+
:key => key,
|
63
|
+
:cfmap => {column_family => hash_to_super_columns(hash, timestamp)})
|
64
|
+
@client.batch_insert_superColumn(mutation, @quorum)
|
65
|
+
end
|
66
|
+
|
67
|
+
public
|
68
|
+
|
69
|
+
## Delete
|
70
|
+
|
71
|
+
# Remove the element at the column_family:key:super_column:column
|
72
|
+
# path you request.
|
73
|
+
def remove(column_family, key, super_column = nil, column = nil, timestamp = now)
|
74
|
+
column_family = column_family.to_s
|
75
|
+
column_family += ":#{super_column}" if super_column
|
76
|
+
column_family += ":#{column}" if column
|
77
|
+
@client.remove(@keyspace, key, column_family, timestamp, @quorum)
|
78
|
+
end
|
79
|
+
|
80
|
+
# Remove all rows in the column family you request.
|
81
|
+
def clear_column_family!(column_family)
|
82
|
+
get_key_range(column_family).each do |key|
|
83
|
+
remove(column_family, key)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
# Remove all rows in the keyspace
|
88
|
+
def clear_keyspace!
|
89
|
+
@schema.keys.each do |column_family|
|
90
|
+
clear_column_family!(column_family)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
## Read
|
95
|
+
|
96
|
+
# Count the elements at the column_family:key:super_column path you
|
97
|
+
# request.
|
98
|
+
def count_columns(column_family, key, super_column = nil)
|
99
|
+
column_family = column_family.to_s
|
100
|
+
column_family += ":#{super_column}" if super_column
|
101
|
+
@client.get_column_count(@keyspace, key, column_family)
|
102
|
+
end
|
103
|
+
|
104
|
+
# Return a list of single values for the elements at the
|
105
|
+
# column_family:key:super_column:column path you request.
|
106
|
+
def get_columns(column_family, key, super_columns, columns = nil)
|
107
|
+
column_family = column_family.to_s
|
108
|
+
get_slice_by_names = (is_super(column_family) && !columns) ? :get_slice_super_by_names : :get_slice_by_names
|
109
|
+
if super_columns and columns
|
110
|
+
column_family += ":#{super_columns}"
|
111
|
+
columns = Array(columns)
|
112
|
+
else
|
113
|
+
columns = Array(super_columns)
|
114
|
+
end
|
115
|
+
|
116
|
+
hash = columns_to_hash(@client.send(get_slice_by_names, @keyspace, key, column_family, columns))
|
117
|
+
columns.map { |column| hash[column] }
|
118
|
+
end
|
119
|
+
|
120
|
+
# Return a hash (actually, a CassandraClient::OrderedHash) or a single value
|
121
|
+
# representing the element at the column_family:key:super_column:column
|
122
|
+
# path you request.
|
123
|
+
def get(column_family, key, super_column = nil, column = nil, offset = -1, limit = 100)
|
124
|
+
column_family = column_family.to_s
|
125
|
+
column_family += ":#{super_column}" if super_column
|
126
|
+
column_family += ":#{column}" if column
|
127
|
+
|
128
|
+
# You have got to be kidding
|
129
|
+
if is_super(column_family)
|
130
|
+
if column
|
131
|
+
load(@client.get_column(@keyspace, key, column_family).value)
|
132
|
+
elsif super_column
|
133
|
+
columns_to_hash(@client.get_superColumn(@keyspace, key, column_family).columns)
|
134
|
+
else
|
135
|
+
columns_to_hash(@client.get_slice_super(@keyspace, key, "#{column_family}:", offset, limit))
|
136
|
+
end
|
137
|
+
else
|
138
|
+
if super_column
|
139
|
+
load(@client.get_column(@keyspace, key, column_family).value)
|
140
|
+
elsif is_sorted_by_time(column_family)
|
141
|
+
result = columns_to_hash(@client.get_columns_since(@keyspace, key, column_family, 0))
|
142
|
+
|
143
|
+
# FIXME Hack until get_slice on a time-sorted column family works again
|
144
|
+
result = OrderedHash[*flatten_once(result.to_a[offset, limit])] if offset > -1
|
145
|
+
result
|
146
|
+
else
|
147
|
+
columns_to_hash(@client.get_slice(@keyspace, key, "#{column_family}:", offset, limit))
|
148
|
+
end
|
149
|
+
end
|
150
|
+
rescue NotFoundException
|
151
|
+
is_super(column_family) && !column ? {} : nil
|
152
|
+
end
|
153
|
+
|
154
|
+
# FIXME
|
155
|
+
# def get_recent(column_family, key, super_column = nil, column = nil, timestamp = 0)
|
156
|
+
# end
|
157
|
+
|
158
|
+
# Return a list of keys in the column_family you request. Requires the
|
159
|
+
# table to be partitioned with OrderPreservingHash.
|
160
|
+
def get_key_range(column_family, key_range = ''..'', limit = 100)
|
161
|
+
column_families = Array(column_family).map {|c| c.to_s}
|
162
|
+
@client.get_key_range(@keyspace, column_families, key_range.begin, key_range.end, limit)
|
163
|
+
end
|
164
|
+
|
165
|
+
# Count all rows in the column_family you request. Requires the table
|
166
|
+
# to be partitioned with OrderPreservingHash.
|
167
|
+
def count(column_family, key_range = ''..'', limit = MAX_INT)
|
168
|
+
get_key_range(column_family, key_range, limit).size
|
169
|
+
end
|
170
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
class CassandraClient
|
2
|
+
module Helper
|
3
|
+
|
4
|
+
private
|
5
|
+
|
6
|
+
def is_super(column_family)
|
7
|
+
column_family_property(column_family, 'type') == 'Super'
|
8
|
+
end
|
9
|
+
|
10
|
+
def is_sorted_by_time(column_family)
|
11
|
+
column_family_property(column_family, 'sort') == 'Time'
|
12
|
+
end
|
13
|
+
|
14
|
+
def column_family_property(column_family_or_path, key)
|
15
|
+
column_family = column_family_or_path.to_s.split(':').first
|
16
|
+
@schema[column_family][key]
|
17
|
+
rescue NoMethodError
|
18
|
+
raise AccessError, "Invalid column family \":#{column_family}\""
|
19
|
+
end
|
20
|
+
|
21
|
+
def columns_to_hash(columns)
|
22
|
+
hash = ::CassandraClient::OrderedHash.new
|
23
|
+
Array(columns).each do |c|
|
24
|
+
if c.is_a?(SuperColumn_t)
|
25
|
+
hash[c.name] = columns_to_hash(c.columns)
|
26
|
+
else
|
27
|
+
hash[c.columnName] = load(c.value)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
hash
|
31
|
+
end
|
32
|
+
|
33
|
+
def hash_to_columns(hash, timestamp)
|
34
|
+
hash.map do |column, value|
|
35
|
+
Column_t.new(:columnName => column, :value => dump(value), :timestamp => timestamp)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def hash_to_super_columns(hash, timestamp)
|
40
|
+
hash.map do |super_column, columns|
|
41
|
+
SuperColumn_t.new(:name => super_column, :columns => hash_to_columns(columns, timestamp))
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def time_in_microseconds
|
46
|
+
time = Time.now
|
47
|
+
time.to_i * 1_000_000 + time.usec
|
48
|
+
end
|
49
|
+
alias :now :time_in_microseconds
|
50
|
+
|
51
|
+
def flatten_once(array)
|
52
|
+
result = []
|
53
|
+
array.each { |el| result.concat(el) }
|
54
|
+
result
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -1,30 +1,31 @@
|
|
1
|
-
# Copyright (c) 2004-2009 David Heinemeier Hansson
|
2
|
-
#
|
3
|
-
# Permission is hereby granted, free of charge, to any person obtaining
|
4
|
-
# a copy of this software and associated documentation files (the
|
5
|
-
# "Software"), to deal in the Software without restriction, including
|
6
|
-
# without limitation the rights to use, copy, modify, merge, publish,
|
7
|
-
# distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
-
# permit persons to whom the Software is furnished to do so, subject to
|
9
|
-
# the following conditions:
|
10
|
-
#
|
11
|
-
# The above copyright notice and this permission notice shall be
|
12
|
-
# included in all copies or substantial portions of the Software.
|
13
|
-
#
|
14
|
-
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
-
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
-
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
-
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
-
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
-
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
-
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
21
1
|
|
22
2
|
class CassandraClient
|
23
3
|
# Hash is ordered in Ruby 1.9!
|
24
4
|
if RUBY_VERSION >= '1.9'
|
25
5
|
OrderedHash = ::Hash
|
26
|
-
else
|
27
|
-
|
6
|
+
else
|
7
|
+
# Copyright (c) 2004-2009 David Heinemeier Hansson
|
8
|
+
#
|
9
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
10
|
+
# a copy of this software and associated documentation files (the
|
11
|
+
# "Software"), to deal in the Software without restriction, including
|
12
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
13
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
14
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
15
|
+
# the following conditions:
|
16
|
+
#
|
17
|
+
# The above copyright notice and this permission notice shall be
|
18
|
+
# included in all copies or substantial portions of the Software.
|
19
|
+
#
|
20
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
21
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
22
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
23
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
24
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
25
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
26
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
27
|
+
|
28
|
+
class OrderedHash < Hash
|
28
29
|
require 'enumerator'
|
29
30
|
|
30
31
|
def self.[](*array)
|
@@ -0,0 +1,18 @@
|
|
1
|
+
|
2
|
+
module Cassandra
|
3
|
+
class SafeClient
|
4
|
+
def initialize(client, transport)
|
5
|
+
@client = client
|
6
|
+
@transport = transport
|
7
|
+
end
|
8
|
+
|
9
|
+
def method_missing(*args)
|
10
|
+
@client.send(*args)
|
11
|
+
rescue IOError
|
12
|
+
@transport.open
|
13
|
+
raise if defined?(once)
|
14
|
+
once = true
|
15
|
+
retry
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -6,194 +6,198 @@ begin; require 'ruby-debug'; rescue LoadError; end
|
|
6
6
|
|
7
7
|
class CassandraClientTest < Test::Unit::TestCase
|
8
8
|
def setup
|
9
|
-
@
|
10
|
-
@
|
11
|
-
@
|
12
|
-
@
|
9
|
+
@twitter = CassandraClient.new('Twitter', '127.0.0.1')
|
10
|
+
@twitter.clear_keyspace!
|
11
|
+
@blogs = CassandraClient.new('Multiblog', '127.0.0.1')
|
12
|
+
@blogs.clear_keyspace!
|
13
13
|
end
|
14
14
|
|
15
15
|
def test_inspect
|
16
16
|
assert_nothing_raised do
|
17
|
-
@
|
18
|
-
@
|
17
|
+
@blogs.inspect
|
18
|
+
@twitter.inspect
|
19
19
|
end
|
20
20
|
end
|
21
21
|
|
22
22
|
def test_connection_reopens
|
23
23
|
assert_raises(NoMethodError) do
|
24
|
-
@
|
24
|
+
@twitter.insert(:Statuses, 1, {'body' => 'v'})
|
25
25
|
end
|
26
26
|
assert_nothing_raised do
|
27
|
-
@
|
27
|
+
@twitter.insert(:Statuses, key, {'body' => 'v'})
|
28
28
|
end
|
29
29
|
end
|
30
30
|
|
31
31
|
def test_get_key_name_sorted
|
32
|
-
@
|
33
|
-
assert_equal({'body' => 'v', 'user' => 'v'}, @
|
34
|
-
assert_equal({}, @
|
32
|
+
@twitter.insert(:Users, key, {'body' => 'v', 'user' => 'v'})
|
33
|
+
assert_equal({'body' => 'v', 'user' => 'v'}, @twitter.get(:Users, key))
|
34
|
+
assert_equal({}, @twitter.get(:Users, 'bogus'))
|
35
35
|
end
|
36
36
|
|
37
37
|
def test_get_key_name_sorted_preserving_order
|
38
38
|
# In-order hash is preserved
|
39
39
|
hash = CassandraClient::OrderedHash['a', '', 'b', '', 'c', '', 'd', '',]
|
40
|
-
@
|
41
|
-
assert_equal(hash.keys, @
|
40
|
+
@twitter.insert(:Users, key, hash)
|
41
|
+
assert_equal(hash.keys, @twitter.get(:Users, key).keys)
|
42
42
|
|
43
|
-
@
|
43
|
+
@twitter.remove(:Users, key)
|
44
44
|
|
45
45
|
# Out-of-order hash is returned sorted
|
46
46
|
hash = CassandraClient::OrderedHash['b', '', 'c', '', 'd', '', 'a', '']
|
47
|
-
@
|
48
|
-
assert_equal(hash.keys.sort, @
|
49
|
-
assert_not_equal(hash.keys, @
|
47
|
+
@twitter.insert(:Users, key, hash)
|
48
|
+
assert_equal(hash.keys.sort, @twitter.get(:Users, key).keys)
|
49
|
+
assert_not_equal(hash.keys, @twitter.get(:Users, key).keys)
|
50
50
|
end
|
51
51
|
|
52
52
|
def test_get_key_time_sorted
|
53
|
-
@
|
54
|
-
assert_equal({'body' => 'v', 'user' => 'v'}, @
|
55
|
-
assert_equal({}, @
|
53
|
+
@twitter.insert(:Statuses, key, {'body' => 'v', 'user' => 'v'})
|
54
|
+
assert_equal({'body' => 'v', 'user' => 'v'}, @twitter.get(:Statuses, key))
|
55
|
+
assert_equal({}, @twitter.get(:Statuses, 'bogus'))
|
56
56
|
end
|
57
57
|
|
58
58
|
def test_get_key_time_sorted_with_limit
|
59
|
-
@
|
60
|
-
@
|
61
|
-
assert_equal({'second' => 'v'}, @
|
59
|
+
@twitter.insert(:Statuses, key, {'first' => 'v'})
|
60
|
+
@twitter.insert(:Statuses, key, {'second' => 'v'})
|
61
|
+
assert_equal({'second' => 'v'}, @twitter.get(:Statuses, key, nil, nil, 0, 1))
|
62
62
|
end
|
63
63
|
|
64
64
|
def test_get_value
|
65
|
-
@
|
66
|
-
assert_equal 'v', @
|
67
|
-
assert_nil @
|
65
|
+
@twitter.insert(:Statuses, key, {'body' => 'v'})
|
66
|
+
assert_equal 'v', @twitter.get(:Statuses, key, 'body')
|
67
|
+
assert_nil @twitter.get(:Statuses, 'bogus', 'body')
|
68
68
|
end
|
69
69
|
|
70
70
|
def test_get_super_key
|
71
|
-
@
|
72
|
-
assert_equal({'user_timelines' => {'4' => 'v', '5' => 'v'}}, @
|
73
|
-
assert_equal({}, @
|
71
|
+
@twitter.insert(:StatusRelationships, key, {'user_timelines' => {'4' => 'v', '5' => 'v'}})
|
72
|
+
assert_equal({'user_timelines' => {'4' => 'v', '5' => 'v'}}, @twitter.get(:StatusRelationships, key))
|
73
|
+
assert_equal({}, @twitter.get(:StatusRelationships, 'bogus'))
|
74
74
|
end
|
75
75
|
|
76
76
|
def test_get_super_key_multi
|
77
|
-
@
|
77
|
+
@twitter.insert(:StatusRelationships, key, {
|
78
78
|
'user_timelines' => {'1' => 'v1'},
|
79
79
|
'mentions_timelines' => {'2' => 'v2'}})
|
80
80
|
assert_equal({
|
81
81
|
'user_timelines' => {'1' => 'v1'},
|
82
|
-
'mentions_timelines' => {'2' => 'v2'}}, @
|
83
|
-
assert_equal({}, @
|
82
|
+
'mentions_timelines' => {'2' => 'v2'}}, @twitter.get(:StatusRelationships, key))
|
83
|
+
assert_equal({}, @twitter.get(:StatusRelationships, 'bogus'))
|
84
84
|
end
|
85
85
|
|
86
86
|
def test_get_super_sub_key
|
87
|
-
@
|
88
|
-
assert_equal({'4' => 'v', '5' => 'v'}, @
|
89
|
-
assert_equal({}, @
|
87
|
+
@twitter.insert(:StatusRelationships, key, {'user_timelines' => {'4' => 'v', '5' => 'v'}})
|
88
|
+
assert_equal({'4' => 'v', '5' => 'v'}, @twitter.get(:StatusRelationships, key, 'user_timelines'))
|
89
|
+
assert_equal({}, @twitter.get(:StatusRelationships, 'bogus', 'user_timelines'))
|
90
90
|
end
|
91
91
|
|
92
92
|
def test_get_super_value
|
93
|
-
@
|
94
|
-
assert_equal('v', @
|
95
|
-
assert_nil @
|
93
|
+
@twitter.insert(:StatusRelationships, key, {'user_timelines' => {'1' => 'v'}})
|
94
|
+
assert_equal('v', @twitter.get(:StatusRelationships, key, 'user_timelines', '1'))
|
95
|
+
assert_nil @twitter.get(:StatusRelationships, 'bogus', 'user_timelines', '1')
|
96
96
|
end
|
97
97
|
|
98
98
|
def test_get_key_range
|
99
|
-
@
|
100
|
-
@
|
101
|
-
@
|
102
|
-
|
99
|
+
@twitter.insert(:Statuses, '2', {'body' => '1'})
|
100
|
+
@twitter.insert(:Statuses, '3', {'body' => '1'})
|
101
|
+
@twitter.insert(:Statuses, '4', {'body' => '1'})
|
102
|
+
@twitter.insert(:Statuses, '5', {'body' => '1'})
|
103
|
+
@twitter.insert(:Statuses, '6', {'body' => '1'})
|
104
|
+
assert_equal(['3', '4', '5'], @twitter.get_key_range(:Statuses, '3'..'5'))
|
103
105
|
end
|
104
106
|
|
105
107
|
# Not supported
|
106
108
|
# def test_get_key_range_super
|
107
|
-
# @
|
108
|
-
# @
|
109
|
-
# @
|
110
|
-
#
|
109
|
+
# @twitter.insert(:StatusRelationships, '2', {'user_timelines' => {'1' => 'v'}})
|
110
|
+
# @twitter.insert(:StatusRelationships, '3', {'user_timelines' => {'1' => 'v'}})
|
111
|
+
# @twitter.insert(:StatusRelationships, '4', {'user_timelines' => {'1' => 'v'}})
|
112
|
+
# @twitter.insert(:StatusRelationships, '5', {'user_timelines' => {'1' => 'v'}})
|
113
|
+
# @twitter.insert(:StatusRelationships, '6', {'user_timelines' => {'1' => 'v'}})
|
114
|
+
# assert_equal(['3', '4', '5'], @twitter.get_key_range(:StatusRelationships, '3'..'5', 'user_timelines'))
|
111
115
|
# end
|
112
116
|
|
113
117
|
def test_remove_key
|
114
|
-
@
|
115
|
-
@
|
116
|
-
assert_equal({}, @
|
118
|
+
@twitter.insert(:Statuses, key, {'body' => 'v'})
|
119
|
+
@twitter.remove(:Statuses, key)
|
120
|
+
assert_equal({}, @twitter.get(:Statuses, key))
|
117
121
|
end
|
118
122
|
|
119
123
|
def test_remove_value
|
120
|
-
@
|
121
|
-
@
|
122
|
-
assert_nil @
|
124
|
+
@twitter.insert(:Statuses, key, {'body' => 'v'})
|
125
|
+
@twitter.remove(:Statuses, key, 'body')
|
126
|
+
assert_nil @twitter.get(:Statuses, key, 'body')
|
123
127
|
end
|
124
128
|
|
125
129
|
def test_remove_super_key
|
126
|
-
@
|
127
|
-
@
|
128
|
-
assert_equal({}, @
|
130
|
+
@twitter.insert(:StatusRelationships, key, {'user_timelines' => {'1' => 'v'}})
|
131
|
+
@twitter.remove(:StatusRelationships, key)
|
132
|
+
assert_equal({}, @twitter.get(:StatusRelationships, key))
|
129
133
|
end
|
130
134
|
|
131
135
|
def test_remove_super_sub_key
|
132
|
-
@
|
133
|
-
@
|
134
|
-
assert_equal({}, @
|
136
|
+
@twitter.insert(:StatusRelationships, key, {'user_timelines' => {'1' => 'v'}})
|
137
|
+
@twitter.remove(:StatusRelationships, key, 'user_timelines')
|
138
|
+
assert_equal({}, @twitter.get(:StatusRelationships, key, 'user_timelines'))
|
135
139
|
end
|
136
140
|
|
137
141
|
def test_remove_super_value
|
138
|
-
@
|
139
|
-
@
|
140
|
-
assert_nil @
|
142
|
+
@twitter.insert(:StatusRelationships, key, {'user_timelines' => {'1' => 'v'}})
|
143
|
+
@twitter.remove(:StatusRelationships, key, 'user_timelines', '1')
|
144
|
+
assert_nil @twitter.get(:StatusRelationships, key, 'user_timelines', '1')
|
141
145
|
end
|
142
146
|
|
143
147
|
def test_insert_key
|
144
|
-
@
|
145
|
-
assert_equal({'body' => 'v', 'user' => 'v'}, @
|
148
|
+
@twitter.insert(:Statuses, key, {'body' => 'v', 'user' => 'v'})
|
149
|
+
assert_equal({'body' => 'v', 'user' => 'v'}, @twitter.get(:Statuses, key))
|
146
150
|
end
|
147
151
|
|
148
152
|
def test_insert_super_key
|
149
|
-
@
|
150
|
-
assert_equal({'1' => 'v' , key => 'v'}, @
|
153
|
+
@twitter.insert(:StatusRelationships, key, {'user_timelines' => {'1' => 'v', key => 'v'}})
|
154
|
+
assert_equal({'1' => 'v' , key => 'v'}, @twitter.get(:StatusRelationships, key, 'user_timelines'))
|
151
155
|
end
|
152
156
|
|
153
157
|
def test_get_column_values
|
154
|
-
@
|
155
|
-
assert_equal(['v1' , 'v2'], @
|
158
|
+
@twitter.insert(:Statuses, key, {'body' => 'v1', 'user' => 'v2'})
|
159
|
+
assert_equal(['v1' , 'v2'], @twitter.get_columns(:Statuses, key,['body', 'user']))
|
156
160
|
end
|
157
161
|
|
158
162
|
def test_get_column_values_super
|
159
|
-
@
|
163
|
+
@twitter.insert(:StatusRelationships, key, {
|
160
164
|
'user_timelines' => {'1' => 'v1'},
|
161
165
|
'mentions_timelines' => {'2' => 'v2'}})
|
162
166
|
assert_equal [{'1' => 'v1'}, {'2' => 'v2'}],
|
163
|
-
@
|
167
|
+
@twitter.get_columns(:StatusRelationships, key, ['user_timelines', 'mentions_timelines'])
|
164
168
|
end
|
165
169
|
|
166
170
|
# Not supported
|
167
171
|
# def test_get_columns_super_sub
|
168
|
-
# @
|
172
|
+
# @twitter.insert(:StatusRelationships, key, {
|
169
173
|
# 'user_timelines' => {'1' => 'v1'},
|
170
174
|
# 'mentions_timelines' => {'2' => 'v2'}})
|
171
175
|
# assert_equal ['v1', 'v2'],
|
172
|
-
# @
|
176
|
+
# @twitter.get_columns(:StatusRelationships, key, 'user_timelines', ['1', key])
|
173
177
|
# end
|
174
178
|
|
175
179
|
def test_count_keys
|
176
|
-
@
|
177
|
-
@
|
178
|
-
@
|
179
|
-
assert_equal 3, @
|
180
|
+
@twitter.insert(:Statuses, key + "1", {'body' => '1'})
|
181
|
+
@twitter.insert(:Statuses, key + "2", {'body' => '2'})
|
182
|
+
@twitter.insert(:Statuses, key + "3", {'body' => '3'})
|
183
|
+
assert_equal 3, @twitter.count(:Statuses)
|
180
184
|
end
|
181
185
|
|
182
186
|
def test_count_columns
|
183
|
-
@
|
184
|
-
assert_equal 2, @
|
187
|
+
@twitter.insert(:Statuses, key, {'body' => 'v1', 'user' => 'v2'})
|
188
|
+
assert_equal 2, @twitter.count_columns(:Statuses, key)
|
185
189
|
end
|
186
190
|
|
187
191
|
def test_count_super_columns
|
188
|
-
@
|
192
|
+
@twitter.insert(:StatusRelationships, key, {
|
189
193
|
'user_timelines' => {'1' => 'v1'},
|
190
194
|
'mentions_timelines' => {'2' => 'v2'}})
|
191
|
-
assert_equal 2, @
|
195
|
+
assert_equal 2, @twitter.count_columns(:StatusRelationships, key)
|
192
196
|
end
|
193
197
|
|
194
198
|
def test_count_super_sub_columns
|
195
|
-
@
|
196
|
-
assert_equal 2, @
|
199
|
+
@twitter.insert(:StatusRelationships, key, {'user_timelines' => {'1' => 'v1', key => 'v2'}})
|
200
|
+
assert_equal 2, @twitter.count_columns(:StatusRelationships, key, 'user_timelines')
|
197
201
|
end
|
198
202
|
|
199
203
|
private
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cassandra_client
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: "0.
|
4
|
+
version: "0.2"
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Evan Weaver
|
@@ -30,7 +30,7 @@ cert_chain:
|
|
30
30
|
yZ0=
|
31
31
|
-----END CERTIFICATE-----
|
32
32
|
|
33
|
-
date: 2009-07-
|
33
|
+
date: 2009-07-06 00:00:00 -07:00
|
34
34
|
default_executable:
|
35
35
|
dependencies:
|
36
36
|
- !ruby/object:Gem::Dependency
|
@@ -51,22 +51,26 @@ extensions: []
|
|
51
51
|
|
52
52
|
extra_rdoc_files:
|
53
53
|
- CHANGELOG
|
54
|
-
- lib/cassandra_client/
|
54
|
+
- lib/cassandra_client/cassandra_client.rb
|
55
|
+
- lib/cassandra_client/helper.rb
|
55
56
|
- lib/cassandra_client/ordered_hash.rb
|
57
|
+
- lib/cassandra_client/safe_client.rb
|
56
58
|
- lib/cassandra_client/serialization.rb
|
57
|
-
- lib/cassandra_client/table.rb
|
58
59
|
- lib/cassandra_client.rb
|
59
60
|
- LICENSE
|
60
61
|
- README
|
62
|
+
- vendor/gen-rb/cassandra_constants.rb
|
63
|
+
- vendor/gen-rb/cassandra_types.rb
|
61
64
|
files:
|
62
65
|
- CHANGELOG
|
63
66
|
- conf/cassandra.in.sh
|
64
67
|
- conf/log4j.properties
|
65
68
|
- conf/storage-conf.xml
|
66
|
-
- lib/cassandra_client/
|
69
|
+
- lib/cassandra_client/cassandra_client.rb
|
70
|
+
- lib/cassandra_client/helper.rb
|
67
71
|
- lib/cassandra_client/ordered_hash.rb
|
72
|
+
- lib/cassandra_client/safe_client.rb
|
68
73
|
- lib/cassandra_client/serialization.rb
|
69
|
-
- lib/cassandra_client/table.rb
|
70
74
|
- lib/cassandra_client.rb
|
71
75
|
- LICENSE
|
72
76
|
- Manifest
|
metadata.gz.sig
CHANGED
Binary file
|
@@ -1,65 +0,0 @@
|
|
1
|
-
class CassandraClient
|
2
|
-
attr_reader :client, :transport, :tables, :host, :port, :block_for, :serialization
|
3
|
-
|
4
|
-
class AccessError < StandardError; end
|
5
|
-
|
6
|
-
# Instantiate a new CassandraClient and open the connection.
|
7
|
-
def initialize(host = '127.0.0.1', port = 9160, block_for = 1, serialization = CassandraClient::Serialization::JSON)
|
8
|
-
@host = host
|
9
|
-
@port = port
|
10
|
-
@serialization = serialization
|
11
|
-
@block_for = block_for
|
12
|
-
|
13
|
-
@transport = Thrift::BufferedTransport.new(Thrift::Socket.new(@host, @port))
|
14
|
-
@transport.open
|
15
|
-
|
16
|
-
@client = SafeClient.new(
|
17
|
-
Cassandra::Client.new(Thrift::BinaryProtocol.new(@transport)),
|
18
|
-
@transport)
|
19
|
-
|
20
|
-
@tables = @client.getStringListProperty("tables").map do |table_name|
|
21
|
-
::CassandraClient::Table.new(table_name, self)
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
def inspect(full = true)
|
26
|
-
string = "#<CassandraClient:#{object_id}, @host=#{host.inspect}, @port=#{@port.inspect}"
|
27
|
-
string += ", @block_for=#{block_for.inspect}, @tables=[#{tables.map {|t| t.inspect(false) }.join(', ')}]" if full
|
28
|
-
string + ">"
|
29
|
-
end
|
30
|
-
|
31
|
-
# Return the CassandraClient::Table instance for the table_name you
|
32
|
-
# request. You can get an array of all available tables with the #tables
|
33
|
-
# method.
|
34
|
-
def table(table_name)
|
35
|
-
table = @tables.detect {|table| table.name == table_name }
|
36
|
-
raise AccessError, "No such table #{table_name.inspect}" unless table
|
37
|
-
table
|
38
|
-
end
|
39
|
-
|
40
|
-
# Remove all rows in all column families in all tables.
|
41
|
-
def remove_all
|
42
|
-
tables.each do |table|
|
43
|
-
table.schema.keys.each do |column_family|
|
44
|
-
table.remove_all(column_family)
|
45
|
-
end
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
class SafeClient
|
50
|
-
def initialize(client, transport)
|
51
|
-
@client = client
|
52
|
-
@transport = transport
|
53
|
-
end
|
54
|
-
|
55
|
-
def method_missing(*args)
|
56
|
-
@client.send(*args)
|
57
|
-
rescue IOError
|
58
|
-
@transport.open
|
59
|
-
raise if defined?(once)
|
60
|
-
once = true
|
61
|
-
retry
|
62
|
-
end
|
63
|
-
end
|
64
|
-
|
65
|
-
end
|
@@ -1,202 +0,0 @@
|
|
1
|
-
class CassandraClient
|
2
|
-
class Table
|
3
|
-
attr_reader :name, :schema, :parent
|
4
|
-
|
5
|
-
MAX_INT = 2**31 - 1
|
6
|
-
|
7
|
-
def initialize(name, parent)
|
8
|
-
@parent = parent
|
9
|
-
@client = parent.client
|
10
|
-
@block_for = parent.block_for
|
11
|
-
|
12
|
-
@name = name
|
13
|
-
@schema = @client.describeTable(@name)
|
14
|
-
extend(parent.serialization)
|
15
|
-
end
|
16
|
-
|
17
|
-
def inspect(full = true)
|
18
|
-
string = "#<CassandraClient::Table:#{object_id}, @name=#{name.inspect}"
|
19
|
-
string += ", @schema={#{schema.map {|name, hash| ":#{name} => #{hash['type'].inspect}"}.join(', ')}}, @parent=#{parent.inspect(false)}" if full
|
20
|
-
string + ">"
|
21
|
-
end
|
22
|
-
|
23
|
-
## Write
|
24
|
-
|
25
|
-
# Insert a row for a key. Pass a flat hash for a regular column family, and
|
26
|
-
# a nested hash for a super column family.
|
27
|
-
def insert(key, column_family, hash, timestamp = now)
|
28
|
-
column_family = column_family.to_s
|
29
|
-
insert = is_super(column_family) ? :insert_super : :insert_standard
|
30
|
-
send(insert, key, column_family, hash, timestamp)
|
31
|
-
end
|
32
|
-
|
33
|
-
private
|
34
|
-
|
35
|
-
def insert_standard(key, column_family, hash, timestamp = now)
|
36
|
-
mutation = Batch_mutation_t.new(
|
37
|
-
:table => @name,
|
38
|
-
:key => key,
|
39
|
-
:cfmap => {column_family => hash_to_columns(hash, timestamp)})
|
40
|
-
@client.batch_insert(mutation, @block_for)
|
41
|
-
end
|
42
|
-
|
43
|
-
def insert_super(key, column_family, hash, timestamp = now)
|
44
|
-
mutation = Batch_mutation_super_t.new(
|
45
|
-
:table => @name,
|
46
|
-
:key => key,
|
47
|
-
:cfmap => {column_family => hash_to_super_columns(hash, timestamp)})
|
48
|
-
@client.batch_insert_superColumn(mutation, @block_for)
|
49
|
-
end
|
50
|
-
|
51
|
-
public
|
52
|
-
|
53
|
-
## Delete
|
54
|
-
|
55
|
-
# Remove the element at the column_family:key:super_column:column
|
56
|
-
# path you request.
|
57
|
-
def remove(key, column_family, super_column = nil, column = nil, timestamp = now)
|
58
|
-
column_family = column_family.to_s
|
59
|
-
column_family += ":#{super_column}" if super_column
|
60
|
-
column_family += ":#{column}" if column
|
61
|
-
@client.remove(@name, key, column_family, timestamp, @block_for )
|
62
|
-
end
|
63
|
-
|
64
|
-
# Remove all rows in the column family you request.
|
65
|
-
def remove_all(column_family)
|
66
|
-
get_key_range(column_family).each do |key|
|
67
|
-
remove(key, column_family)
|
68
|
-
end
|
69
|
-
end
|
70
|
-
|
71
|
-
## Read
|
72
|
-
|
73
|
-
# Count the elements at the column_family:key:super_column path you
|
74
|
-
# request.
|
75
|
-
def count_columns(key, column_family, super_column = nil)
|
76
|
-
column_family = column_family.to_s
|
77
|
-
column_family += ":#{super_column}" if super_column
|
78
|
-
@client.get_column_count(@name, key, column_family)
|
79
|
-
end
|
80
|
-
|
81
|
-
# Return a list of single values for the elements at the
|
82
|
-
# column_family:key:super_column:column path you request.
|
83
|
-
def get_columns(key, column_family, super_columns, columns = nil)
|
84
|
-
column_family = column_family.to_s
|
85
|
-
get_slice_by_names = (is_super(column_family) && !columns) ? :get_slice_super_by_names : :get_slice_by_names
|
86
|
-
if super_columns and columns
|
87
|
-
column_family += ":#{super_columns}"
|
88
|
-
columns = Array(columns)
|
89
|
-
else
|
90
|
-
columns = Array(super_columns)
|
91
|
-
end
|
92
|
-
|
93
|
-
hash = columns_to_hash(@client.send(get_slice_by_names, @name, key, column_family, columns))
|
94
|
-
columns.map { |column| hash[column] }
|
95
|
-
end
|
96
|
-
|
97
|
-
# Return a hash (actually, a CassandraClient::OrderedHash) or a single value
|
98
|
-
# representing the element at the column_family:key:super_column:column
|
99
|
-
# path you request.
|
100
|
-
def get(key, column_family, super_column = nil, column = nil, offset = -1, limit = 100)
|
101
|
-
column_family = column_family.to_s
|
102
|
-
column_family += ":#{super_column}" if super_column
|
103
|
-
column_family += ":#{column}" if column
|
104
|
-
|
105
|
-
# You have got to be kidding
|
106
|
-
if is_super(column_family)
|
107
|
-
if column
|
108
|
-
load(@client.get_column(@name, key, column_family).value)
|
109
|
-
elsif super_column
|
110
|
-
columns_to_hash(@client.get_superColumn(@name, key, column_family).columns)
|
111
|
-
else
|
112
|
-
columns_to_hash(@client.get_slice_super(@name, key, "#{column_family}:", offset, limit))
|
113
|
-
end
|
114
|
-
else
|
115
|
-
if super_column
|
116
|
-
load(@client.get_column(@name, key, column_family).value)
|
117
|
-
elsif is_sorted_by_time(column_family)
|
118
|
-
result = columns_to_hash(@client.get_columns_since(@name, key, column_family, 0))
|
119
|
-
|
120
|
-
# FIXME Hack until get_slice on a time-sorted column family works again
|
121
|
-
result = OrderedHash[*flatten_once(result.to_a[offset, limit])] if offset > -1
|
122
|
-
result
|
123
|
-
else
|
124
|
-
columns_to_hash(@client.get_slice(@name, key, "#{column_family}:", offset, limit))
|
125
|
-
end
|
126
|
-
end
|
127
|
-
rescue NotFoundException
|
128
|
-
is_super(column_family) && !column ? {} : nil
|
129
|
-
end
|
130
|
-
|
131
|
-
# FIXME
|
132
|
-
# def get_recent(key, column_family, super_column = nil, column = nil, timestamp = 0)
|
133
|
-
# end
|
134
|
-
|
135
|
-
# Return a list of keys in the column_family you request. Requires the
|
136
|
-
# table to be partitioned with OrderPreservingHash.
|
137
|
-
def get_key_range(key_range, column_family = nil, limit = 100)
|
138
|
-
column_family, key_range = key_range, ''..'' unless column_family
|
139
|
-
column_families = Array(column_family).map {|c| c.to_s}
|
140
|
-
@client.get_key_range(@name, column_families, key_range.begin, key_range.end, limit)
|
141
|
-
end
|
142
|
-
|
143
|
-
# Count all rows in the column_family you request. Requires the table
|
144
|
-
# to be partitioned with OrderPreservingHash.
|
145
|
-
def count(key_range, column_family = nil, limit = MAX_INT)
|
146
|
-
get_key_range(key_range, column_family, limit).size
|
147
|
-
end
|
148
|
-
|
149
|
-
private
|
150
|
-
|
151
|
-
def is_super(column_family)
|
152
|
-
column_family_property(column_family, 'type') == 'Super'
|
153
|
-
end
|
154
|
-
|
155
|
-
def is_sorted_by_time(column_family)
|
156
|
-
column_family_property(column_family, 'sort') == 'Time'
|
157
|
-
end
|
158
|
-
|
159
|
-
def column_family_property(column_family_or_path, key)
|
160
|
-
column_family = column_family_or_path.to_s.split(':').first
|
161
|
-
@schema[column_family][key]
|
162
|
-
rescue NoMethodError
|
163
|
-
raise AccessError, "Invalid column family \":#{column_family}\""
|
164
|
-
end
|
165
|
-
|
166
|
-
def columns_to_hash(columns)
|
167
|
-
hash = ::CassandraClient::OrderedHash.new
|
168
|
-
Array(columns).each do |c|
|
169
|
-
if c.is_a?(SuperColumn_t)
|
170
|
-
hash[c.name] = columns_to_hash(c.columns)
|
171
|
-
else
|
172
|
-
hash[c.columnName] = load(c.value)
|
173
|
-
end
|
174
|
-
end
|
175
|
-
hash
|
176
|
-
end
|
177
|
-
|
178
|
-
def hash_to_columns(hash, timestamp)
|
179
|
-
hash.map do |column, value|
|
180
|
-
Column_t.new(:columnName => column, :value => dump(value), :timestamp => timestamp)
|
181
|
-
end
|
182
|
-
end
|
183
|
-
|
184
|
-
def hash_to_super_columns(hash, timestamp)
|
185
|
-
hash.map do |super_column, columns|
|
186
|
-
SuperColumn_t.new(:name => super_column, :columns => hash_to_columns(columns, timestamp))
|
187
|
-
end
|
188
|
-
end
|
189
|
-
|
190
|
-
def time_in_microseconds
|
191
|
-
time = Time.now
|
192
|
-
time.to_i * 1_000_000 + time.usec
|
193
|
-
end
|
194
|
-
alias :now :time_in_microseconds
|
195
|
-
|
196
|
-
def flatten_once(array)
|
197
|
-
result = []
|
198
|
-
array.each { |el| result.concat(el) }
|
199
|
-
result
|
200
|
-
end
|
201
|
-
end
|
202
|
-
end
|