cassandra_model 0.9.3.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.
- checksums.yaml +7 -0
- data/LICENSE.txt +13 -0
- data/README.md +170 -0
- data/lib/cassandra_model.rb +48 -0
- data/lib/cassandra_model/batch_reactor.rb +32 -0
- data/lib/cassandra_model/batch_reactor/future.rb +49 -0
- data/lib/cassandra_model/composite_record.rb +49 -0
- data/lib/cassandra_model/composite_record_static.rb +169 -0
- data/lib/cassandra_model/connection_cache.rb +24 -0
- data/lib/cassandra_model/counter_record.rb +58 -0
- data/lib/cassandra_model/data_inquirer.rb +105 -0
- data/lib/cassandra_model/data_modelling.rb +45 -0
- data/lib/cassandra_model/data_set.rb +84 -0
- data/lib/cassandra_model/displayable_attributes.rb +44 -0
- data/lib/cassandra_model/global_callbacks.rb +39 -0
- data/lib/cassandra_model/logging.rb +8 -0
- data/lib/cassandra_model/meta_columns.rb +162 -0
- data/lib/cassandra_model/meta_table.rb +66 -0
- data/lib/cassandra_model/query_builder.rb +122 -0
- data/lib/cassandra_model/query_helper.rb +44 -0
- data/lib/cassandra_model/query_result.rb +23 -0
- data/lib/cassandra_model/raw_connection.rb +163 -0
- data/lib/cassandra_model/record.rb +551 -0
- data/lib/cassandra_model/result_paginator.rb +37 -0
- data/lib/cassandra_model/rotating_table.rb +49 -0
- data/lib/cassandra_model/single_token_batch.rb +23 -0
- data/lib/cassandra_model/single_token_counter_batch.rb +5 -0
- data/lib/cassandra_model/single_token_logged_batch.rb +5 -0
- data/lib/cassandra_model/single_token_unlogged_batch.rb +5 -0
- data/lib/cassandra_model/table_definition.rb +72 -0
- data/lib/cassandra_model/table_descriptor.rb +49 -0
- data/lib/cassandra_model/table_redux.rb +58 -0
- data/lib/cassandra_model/type_guessing.rb +40 -0
- metadata +133 -0
@@ -0,0 +1,37 @@
|
|
1
|
+
module CassandraModel
|
2
|
+
class ResultPaginator
|
3
|
+
include Enumerable
|
4
|
+
|
5
|
+
def initialize(first_page, &callback)
|
6
|
+
@page = first_page
|
7
|
+
@callback = callback
|
8
|
+
end
|
9
|
+
|
10
|
+
def each(&block)
|
11
|
+
return to_enum(:each) unless block_given?
|
12
|
+
|
13
|
+
each_slice { |slice| slice.each(&block) }
|
14
|
+
end
|
15
|
+
|
16
|
+
def each_slice
|
17
|
+
return to_enum(:each_slice) unless block_given?
|
18
|
+
|
19
|
+
current_page = @page
|
20
|
+
loop do
|
21
|
+
page_results = current_page.get
|
22
|
+
modified_results = page_results.map { |result| @callback.call(result, page_results.execution_info) }
|
23
|
+
break if page_results.empty?
|
24
|
+
if page_results.last_page?
|
25
|
+
yield modified_results
|
26
|
+
break
|
27
|
+
else
|
28
|
+
current_page = page_results.next_page_async
|
29
|
+
yield modified_results
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
alias :get :to_a
|
35
|
+
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module CassandraModel
|
2
|
+
class RotatingTable
|
3
|
+
extend Forwardable
|
4
|
+
|
5
|
+
def_delegators :first_table, :primary_key, :partition_key, :clustering_columns, :columns
|
6
|
+
def_delegators :table, :connection, :name, :truncate!
|
7
|
+
|
8
|
+
def initialize(tables, schedule)
|
9
|
+
columns = tables.first.columns
|
10
|
+
raise 'RotatingTable, Table columns do not match' unless valid_tables?(columns, tables)
|
11
|
+
|
12
|
+
@tables = tables
|
13
|
+
@schedule = schedule
|
14
|
+
end
|
15
|
+
|
16
|
+
def allow_truncation!
|
17
|
+
tables.each(&:allow_truncation!)
|
18
|
+
end
|
19
|
+
|
20
|
+
def reset_local_schema!
|
21
|
+
@tables.reject { |table| table.is_a?(MetaTable) }.each(&:reset_local_schema!)
|
22
|
+
end
|
23
|
+
|
24
|
+
def ==(rhs)
|
25
|
+
@schedule == rhs.schedule &&
|
26
|
+
@tables == rhs.tables
|
27
|
+
end
|
28
|
+
|
29
|
+
protected
|
30
|
+
|
31
|
+
attr_reader :schedule, :tables
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def first_table
|
36
|
+
@tables.first
|
37
|
+
end
|
38
|
+
|
39
|
+
def table
|
40
|
+
index = (Time.now.to_f / @schedule).to_i % @tables.count
|
41
|
+
@tables[index]
|
42
|
+
end
|
43
|
+
|
44
|
+
def valid_tables?(columns, tables)
|
45
|
+
tables.map(&:columns).reduce(&:|) == columns
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module CassandraModel
|
2
|
+
module SingleTokenBatch
|
3
|
+
extend Forwardable
|
4
|
+
include Enumerable
|
5
|
+
|
6
|
+
attr_writer :result
|
7
|
+
|
8
|
+
def_delegators :result, :execution_info, :empty?, :each
|
9
|
+
|
10
|
+
def keyspace
|
11
|
+
nil
|
12
|
+
end
|
13
|
+
|
14
|
+
def partition_key
|
15
|
+
@statements.first.partition_key
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
attr_reader :result
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
module CassandraModel
|
2
|
+
class TableDefinition
|
3
|
+
attr_reader :name
|
4
|
+
|
5
|
+
def self.from_data_model(name, inquirer, data_set)
|
6
|
+
partition_key = inquirer_partition_key(inquirer)
|
7
|
+
partition_key.merge!(rk_shard: :int) if inquirer.is_sharding
|
8
|
+
clustering_columns = table_set_clustering_columns(data_set)
|
9
|
+
remaining_columns = table_set_remaining_columns(data_set)
|
10
|
+
new(name: name, partition_key: partition_key,
|
11
|
+
clustering_columns: clustering_columns,
|
12
|
+
remaining_columns: remaining_columns)
|
13
|
+
end
|
14
|
+
|
15
|
+
def initialize(options)
|
16
|
+
@partition_key = options[:partition_key].keys
|
17
|
+
@clustering_columns = options[:clustering_columns].keys
|
18
|
+
@name = options[:name]
|
19
|
+
@columns = options[:partition_key].merge(options[:clustering_columns].merge(options[:remaining_columns]))
|
20
|
+
end
|
21
|
+
|
22
|
+
def to_cql(options = {})
|
23
|
+
table_name = options[:no_id] ? name : name_in_cassandra
|
24
|
+
exists = if options[:check_exists]
|
25
|
+
'IF NOT EXISTS '
|
26
|
+
end
|
27
|
+
"CREATE TABLE #{exists}#{table_name} (#{columns}, PRIMARY KEY #{primary_key})"
|
28
|
+
end
|
29
|
+
|
30
|
+
def table_id
|
31
|
+
Digest::MD5.hexdigest(columns)
|
32
|
+
end
|
33
|
+
|
34
|
+
def name_in_cassandra
|
35
|
+
"#{name}_#{table_id}"
|
36
|
+
end
|
37
|
+
|
38
|
+
def ==(rhs)
|
39
|
+
to_cql == rhs.to_cql
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def self.table_set_remaining_columns(data_set)
|
45
|
+
data_set.columns.except(*data_set.clustering_columns)
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.table_set_clustering_columns(data_set)
|
49
|
+
data_set.clustering_columns.inject({}) do |memo, column|
|
50
|
+
memo.merge!(:"ck_#{column}" => data_set.columns[column])
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def self.inquirer_partition_key(inquirer)
|
55
|
+
inquirer.partition_key.inject({}) do |memo, (key, value)|
|
56
|
+
memo.merge!(:"rk_#{key}" => value)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def columns
|
61
|
+
@columns.map { |name, type| "#{name} #{type}" } * ', '
|
62
|
+
end
|
63
|
+
|
64
|
+
def primary_key
|
65
|
+
if @clustering_columns.present?
|
66
|
+
"((#{@partition_key * ', '}), #{@clustering_columns * ', '})"
|
67
|
+
else
|
68
|
+
"((#{@partition_key * ', '}))"
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module CassandraModel
|
2
|
+
class TableDescriptor < Record
|
3
|
+
|
4
|
+
class << self
|
5
|
+
def create_async(table_definition)
|
6
|
+
super(table_descriptor(table_definition), check_exists: true)
|
7
|
+
end
|
8
|
+
|
9
|
+
def create(table_definition)
|
10
|
+
create_async(table_definition).get
|
11
|
+
end
|
12
|
+
|
13
|
+
def create_descriptor_table
|
14
|
+
session.execute(table_desc.to_cql(no_id: true)) unless descriptor_table_exists?
|
15
|
+
end
|
16
|
+
|
17
|
+
def drop_descriptor_table
|
18
|
+
session.execute("DROP TABLE #{table_name}") if descriptor_table_exists?
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def descriptor_table_exists?
|
24
|
+
table.connection.keyspace.table(table.name)
|
25
|
+
end
|
26
|
+
|
27
|
+
def table_descriptor(table_definition)
|
28
|
+
{name: table_definition.name.to_s,
|
29
|
+
created_at: rounded_time,
|
30
|
+
id: table_definition.table_id}
|
31
|
+
end
|
32
|
+
|
33
|
+
def rounded_time
|
34
|
+
Time.at((Time.now.to_i / 1.day) * 1.day)
|
35
|
+
end
|
36
|
+
|
37
|
+
def table_desc
|
38
|
+
@table_desc ||= begin
|
39
|
+
options = {name: table_name,
|
40
|
+
partition_key: {name: :ascii},
|
41
|
+
clustering_columns: {id: :ascii},
|
42
|
+
remaining_columns: {created_at: :timestamp}}
|
43
|
+
TableDefinition.new(options)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module CassandraModel
|
2
|
+
class TableRedux
|
3
|
+
extend Forwardable
|
4
|
+
|
5
|
+
attr_reader :name
|
6
|
+
|
7
|
+
def initialize(connection_name = nil, table_name)
|
8
|
+
@name = table_name.to_s
|
9
|
+
@connection_name = connection_name
|
10
|
+
end
|
11
|
+
|
12
|
+
#noinspection RubyUnusedLocalVariable
|
13
|
+
def in_context(time)
|
14
|
+
yield self
|
15
|
+
end
|
16
|
+
|
17
|
+
def connection
|
18
|
+
ConnectionCache[@connection_name]
|
19
|
+
end
|
20
|
+
|
21
|
+
def allow_truncation!
|
22
|
+
@allow_truncation = true
|
23
|
+
end
|
24
|
+
|
25
|
+
def truncate!
|
26
|
+
raise "Truncation not enabled for table '#{name}'" unless @allow_truncation
|
27
|
+
connection.session.execute("TRUNCATE #{name}")
|
28
|
+
end
|
29
|
+
|
30
|
+
def reset_local_schema!
|
31
|
+
@partition_key = nil
|
32
|
+
@clustering_columns = nil
|
33
|
+
@columns = nil
|
34
|
+
end
|
35
|
+
|
36
|
+
def partition_key
|
37
|
+
@partition_key ||= table.send(:partition_key).map { |column| column.name.to_sym }
|
38
|
+
end
|
39
|
+
|
40
|
+
def clustering_columns
|
41
|
+
@clustering_columns ||= table.send(:clustering_columns).map { |column| column.name.to_sym }
|
42
|
+
end
|
43
|
+
|
44
|
+
def primary_key
|
45
|
+
partition_key + clustering_columns
|
46
|
+
end
|
47
|
+
|
48
|
+
def columns
|
49
|
+
@columns ||= table.columns.map { |column| column.name.to_sym }
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
def table
|
55
|
+
connection.keyspace.table(name)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module TypeGuessing
|
2
|
+
def guess_data_types!
|
3
|
+
@guess_data_types = true
|
4
|
+
end
|
5
|
+
|
6
|
+
private
|
7
|
+
|
8
|
+
class DataTypeGuess < Struct.new(:column, :counter_type)
|
9
|
+
def guessed_type
|
10
|
+
postfix_type || :text
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def postfix_type
|
16
|
+
if column =~ /_at$/
|
17
|
+
:timestamp
|
18
|
+
elsif column =~ /_at_id$/
|
19
|
+
:timeuuid
|
20
|
+
elsif column =~ /_id$/
|
21
|
+
:uuid
|
22
|
+
elsif column =~ /_(price|average|stddev)$/
|
23
|
+
:double
|
24
|
+
elsif column =~ /_(total|count)$/
|
25
|
+
counter_type
|
26
|
+
elsif column =~ /_(year|day|month|index)$/
|
27
|
+
:int
|
28
|
+
elsif column =~ /_data/
|
29
|
+
:blob
|
30
|
+
elsif column =~ /_map$/
|
31
|
+
'map<string, string>'
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def guessed_data_type(column, counter_type)
|
37
|
+
DataTypeGuess.new(column, counter_type).guessed_type
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
metadata
ADDED
@@ -0,0 +1,133 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: cassandra_model
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.9.3.2
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Thomas RM Rogers
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-12-02 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: cassandra-driver
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ~>
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.1'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ~>
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.1'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: activesupport
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ~>
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '4.0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ~>
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '4.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: batch_reactor
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ~>
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 0.0.1
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ~>
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 0.0.1
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: thomas_utils
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 0.1.13
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ~>
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 0.1.13
|
69
|
+
description: |-
|
70
|
+
Cassandra data modelling framework for Ruby that makes
|
71
|
+
data modelling for Cassandra tables easy, fast, and stable
|
72
|
+
email: thomasrogers03@gmail.com
|
73
|
+
executables: []
|
74
|
+
extensions: []
|
75
|
+
extra_rdoc_files: []
|
76
|
+
files:
|
77
|
+
- LICENSE.txt
|
78
|
+
- README.md
|
79
|
+
- lib/cassandra_model.rb
|
80
|
+
- lib/cassandra_model/batch_reactor.rb
|
81
|
+
- lib/cassandra_model/batch_reactor/future.rb
|
82
|
+
- lib/cassandra_model/composite_record.rb
|
83
|
+
- lib/cassandra_model/composite_record_static.rb
|
84
|
+
- lib/cassandra_model/connection_cache.rb
|
85
|
+
- lib/cassandra_model/counter_record.rb
|
86
|
+
- lib/cassandra_model/data_inquirer.rb
|
87
|
+
- lib/cassandra_model/data_modelling.rb
|
88
|
+
- lib/cassandra_model/data_set.rb
|
89
|
+
- lib/cassandra_model/displayable_attributes.rb
|
90
|
+
- lib/cassandra_model/global_callbacks.rb
|
91
|
+
- lib/cassandra_model/logging.rb
|
92
|
+
- lib/cassandra_model/meta_columns.rb
|
93
|
+
- lib/cassandra_model/meta_table.rb
|
94
|
+
- lib/cassandra_model/query_builder.rb
|
95
|
+
- lib/cassandra_model/query_helper.rb
|
96
|
+
- lib/cassandra_model/query_result.rb
|
97
|
+
- lib/cassandra_model/raw_connection.rb
|
98
|
+
- lib/cassandra_model/record.rb
|
99
|
+
- lib/cassandra_model/result_paginator.rb
|
100
|
+
- lib/cassandra_model/rotating_table.rb
|
101
|
+
- lib/cassandra_model/single_token_batch.rb
|
102
|
+
- lib/cassandra_model/single_token_counter_batch.rb
|
103
|
+
- lib/cassandra_model/single_token_logged_batch.rb
|
104
|
+
- lib/cassandra_model/single_token_unlogged_batch.rb
|
105
|
+
- lib/cassandra_model/table_definition.rb
|
106
|
+
- lib/cassandra_model/table_descriptor.rb
|
107
|
+
- lib/cassandra_model/table_redux.rb
|
108
|
+
- lib/cassandra_model/type_guessing.rb
|
109
|
+
homepage: https://www.github.com/thomasrogers03/cassandra_model
|
110
|
+
licenses:
|
111
|
+
- Apache License 2.0
|
112
|
+
metadata: {}
|
113
|
+
post_install_message:
|
114
|
+
rdoc_options: []
|
115
|
+
require_paths:
|
116
|
+
- lib
|
117
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
118
|
+
requirements:
|
119
|
+
- - '>='
|
120
|
+
- !ruby/object:Gem::Version
|
121
|
+
version: '0'
|
122
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
123
|
+
requirements:
|
124
|
+
- - '>='
|
125
|
+
- !ruby/object:Gem::Version
|
126
|
+
version: '0'
|
127
|
+
requirements: []
|
128
|
+
rubyforge_project:
|
129
|
+
rubygems_version: 2.4.8
|
130
|
+
signing_key:
|
131
|
+
specification_version: 4
|
132
|
+
summary: Cassandra data modelling framework for Ruby
|
133
|
+
test_files: []
|