massive_record 0.1.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/.autotest +15 -0
- data/.gitignore +6 -0
- data/.rspec +2 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +38 -0
- data/Manifest +24 -0
- data/README.md +225 -0
- data/Rakefile +16 -0
- data/TODO.md +8 -0
- data/autotest/discover.rb +1 -0
- data/lib/massive_record.rb +18 -0
- data/lib/massive_record/exceptions.rb +11 -0
- data/lib/massive_record/orm/attribute_methods.rb +61 -0
- data/lib/massive_record/orm/attribute_methods/dirty.rb +80 -0
- data/lib/massive_record/orm/attribute_methods/read.rb +23 -0
- data/lib/massive_record/orm/attribute_methods/write.rb +24 -0
- data/lib/massive_record/orm/base.rb +176 -0
- data/lib/massive_record/orm/callbacks.rb +52 -0
- data/lib/massive_record/orm/column.rb +18 -0
- data/lib/massive_record/orm/config.rb +47 -0
- data/lib/massive_record/orm/errors.rb +47 -0
- data/lib/massive_record/orm/finders.rb +125 -0
- data/lib/massive_record/orm/id_factory.rb +133 -0
- data/lib/massive_record/orm/persistence.rb +199 -0
- data/lib/massive_record/orm/schema.rb +4 -0
- data/lib/massive_record/orm/schema/column_families.rb +48 -0
- data/lib/massive_record/orm/schema/column_family.rb +102 -0
- data/lib/massive_record/orm/schema/column_interface.rb +91 -0
- data/lib/massive_record/orm/schema/common_interface.rb +48 -0
- data/lib/massive_record/orm/schema/field.rb +128 -0
- data/lib/massive_record/orm/schema/fields.rb +37 -0
- data/lib/massive_record/orm/schema/table_interface.rb +96 -0
- data/lib/massive_record/orm/table.rb +9 -0
- data/lib/massive_record/orm/validations.rb +52 -0
- data/lib/massive_record/spec/support/simple_database_cleaner.rb +52 -0
- data/lib/massive_record/thrift/hbase.rb +2307 -0
- data/lib/massive_record/thrift/hbase_constants.rb +14 -0
- data/lib/massive_record/thrift/hbase_types.rb +225 -0
- data/lib/massive_record/version.rb +3 -0
- data/lib/massive_record/wrapper/base.rb +28 -0
- data/lib/massive_record/wrapper/cell.rb +45 -0
- data/lib/massive_record/wrapper/column_families_collection.rb +19 -0
- data/lib/massive_record/wrapper/column_family.rb +22 -0
- data/lib/massive_record/wrapper/connection.rb +71 -0
- data/lib/massive_record/wrapper/row.rb +170 -0
- data/lib/massive_record/wrapper/scanner.rb +50 -0
- data/lib/massive_record/wrapper/table.rb +148 -0
- data/lib/massive_record/wrapper/tables_collection.rb +13 -0
- data/massive_record.gemspec +28 -0
- data/spec/config.yml.example +4 -0
- data/spec/orm/cases/attribute_methods_spec.rb +47 -0
- data/spec/orm/cases/auto_generate_id_spec.rb +54 -0
- data/spec/orm/cases/base_spec.rb +176 -0
- data/spec/orm/cases/callbacks_spec.rb +309 -0
- data/spec/orm/cases/column_spec.rb +49 -0
- data/spec/orm/cases/config_spec.rb +103 -0
- data/spec/orm/cases/dirty_spec.rb +129 -0
- data/spec/orm/cases/encoding_spec.rb +49 -0
- data/spec/orm/cases/finders_spec.rb +208 -0
- data/spec/orm/cases/hbase/connection_spec.rb +13 -0
- data/spec/orm/cases/i18n_spec.rb +32 -0
- data/spec/orm/cases/id_factory_spec.rb +75 -0
- data/spec/orm/cases/persistence_spec.rb +479 -0
- data/spec/orm/cases/table_spec.rb +81 -0
- data/spec/orm/cases/validation_spec.rb +92 -0
- data/spec/orm/models/address.rb +7 -0
- data/spec/orm/models/person.rb +15 -0
- data/spec/orm/models/test_class.rb +5 -0
- data/spec/orm/schema/column_families_spec.rb +186 -0
- data/spec/orm/schema/column_family_spec.rb +131 -0
- data/spec/orm/schema/column_interface_spec.rb +115 -0
- data/spec/orm/schema/field_spec.rb +196 -0
- data/spec/orm/schema/fields_spec.rb +126 -0
- data/spec/orm/schema/table_interface_spec.rb +171 -0
- data/spec/spec_helper.rb +15 -0
- data/spec/support/connection_helpers.rb +76 -0
- data/spec/support/mock_massive_record_connection.rb +80 -0
- data/spec/thrift/cases/encoding_spec.rb +48 -0
- data/spec/wrapper/cases/connection_spec.rb +53 -0
- data/spec/wrapper/cases/table_spec.rb +231 -0
- metadata +228 -0
@@ -0,0 +1,225 @@
|
|
1
|
+
#
|
2
|
+
# Autogenerated by Thrift
|
3
|
+
#
|
4
|
+
# DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
|
5
|
+
#
|
6
|
+
|
7
|
+
module Apache
|
8
|
+
module Hadoop
|
9
|
+
module Hbase
|
10
|
+
module Thrift
|
11
|
+
# TCell - Used to transport a cell value (byte[]) and the timestamp it was
|
12
|
+
# stored with together as a result for get and getRow methods. This promotes
|
13
|
+
# the timestamp of a cell to a first-class value, making it easy to take
|
14
|
+
# note of temporal data. Cell is used all the way from HStore up to HTable.
|
15
|
+
class TCell
|
16
|
+
include ::Thrift::Struct, ::Thrift::Struct_Union
|
17
|
+
VALUE = 1
|
18
|
+
TIMESTAMP = 2
|
19
|
+
|
20
|
+
FIELDS = {
|
21
|
+
VALUE => {:type => ::Thrift::Types::STRING, :name => 'value', :binary => true},
|
22
|
+
TIMESTAMP => {:type => ::Thrift::Types::I64, :name => 'timestamp'}
|
23
|
+
}
|
24
|
+
|
25
|
+
def struct_fields; FIELDS; end
|
26
|
+
|
27
|
+
def validate
|
28
|
+
end
|
29
|
+
|
30
|
+
::Thrift::Struct.generate_accessors self
|
31
|
+
end
|
32
|
+
|
33
|
+
# An HColumnDescriptor contains information about a column family
|
34
|
+
# such as the number of versions, compression settings, etc. It is
|
35
|
+
# used as input when creating a table or adding a column.
|
36
|
+
class ColumnDescriptor
|
37
|
+
include ::Thrift::Struct, ::Thrift::Struct_Union
|
38
|
+
NAME = 1
|
39
|
+
MAXVERSIONS = 2
|
40
|
+
COMPRESSION = 3
|
41
|
+
INMEMORY = 4
|
42
|
+
BLOOMFILTERTYPE = 5
|
43
|
+
BLOOMFILTERVECTORSIZE = 6
|
44
|
+
BLOOMFILTERNBHASHES = 7
|
45
|
+
BLOCKCACHEENABLED = 8
|
46
|
+
TIMETOLIVE = 9
|
47
|
+
|
48
|
+
FIELDS = {
|
49
|
+
NAME => {:type => ::Thrift::Types::STRING, :name => 'name', :binary => true},
|
50
|
+
MAXVERSIONS => {:type => ::Thrift::Types::I32, :name => 'maxVersions', :default => 3},
|
51
|
+
COMPRESSION => {:type => ::Thrift::Types::STRING, :name => 'compression', :default => %q"NONE"},
|
52
|
+
INMEMORY => {:type => ::Thrift::Types::BOOL, :name => 'inMemory', :default => false},
|
53
|
+
BLOOMFILTERTYPE => {:type => ::Thrift::Types::STRING, :name => 'bloomFilterType', :default => %q"NONE"},
|
54
|
+
BLOOMFILTERVECTORSIZE => {:type => ::Thrift::Types::I32, :name => 'bloomFilterVectorSize', :default => 0},
|
55
|
+
BLOOMFILTERNBHASHES => {:type => ::Thrift::Types::I32, :name => 'bloomFilterNbHashes', :default => 0},
|
56
|
+
BLOCKCACHEENABLED => {:type => ::Thrift::Types::BOOL, :name => 'blockCacheEnabled', :default => false},
|
57
|
+
TIMETOLIVE => {:type => ::Thrift::Types::I32, :name => 'timeToLive', :default => -1}
|
58
|
+
}
|
59
|
+
|
60
|
+
def struct_fields; FIELDS; end
|
61
|
+
|
62
|
+
def validate
|
63
|
+
end
|
64
|
+
|
65
|
+
::Thrift::Struct.generate_accessors self
|
66
|
+
end
|
67
|
+
|
68
|
+
# A TRegionInfo contains information about an HTable region.
|
69
|
+
class TRegionInfo
|
70
|
+
include ::Thrift::Struct, ::Thrift::Struct_Union
|
71
|
+
STARTKEY = 1
|
72
|
+
ENDKEY = 2
|
73
|
+
ID = 3
|
74
|
+
NAME = 4
|
75
|
+
VERSION = 5
|
76
|
+
|
77
|
+
FIELDS = {
|
78
|
+
STARTKEY => {:type => ::Thrift::Types::STRING, :name => 'startKey', :binary => true},
|
79
|
+
ENDKEY => {:type => ::Thrift::Types::STRING, :name => 'endKey', :binary => true},
|
80
|
+
ID => {:type => ::Thrift::Types::I64, :name => 'id'},
|
81
|
+
NAME => {:type => ::Thrift::Types::STRING, :name => 'name', :binary => true},
|
82
|
+
VERSION => {:type => ::Thrift::Types::BYTE, :name => 'version'}
|
83
|
+
}
|
84
|
+
|
85
|
+
def struct_fields; FIELDS; end
|
86
|
+
|
87
|
+
def validate
|
88
|
+
end
|
89
|
+
|
90
|
+
::Thrift::Struct.generate_accessors self
|
91
|
+
end
|
92
|
+
|
93
|
+
# A Mutation object is used to either update or delete a column-value.
|
94
|
+
class Mutation
|
95
|
+
include ::Thrift::Struct, ::Thrift::Struct_Union
|
96
|
+
ISDELETE = 1
|
97
|
+
COLUMN = 2
|
98
|
+
VALUE = 3
|
99
|
+
|
100
|
+
FIELDS = {
|
101
|
+
ISDELETE => {:type => ::Thrift::Types::BOOL, :name => 'isDelete', :default => false},
|
102
|
+
COLUMN => {:type => ::Thrift::Types::STRING, :name => 'column', :binary => true},
|
103
|
+
VALUE => {:type => ::Thrift::Types::STRING, :name => 'value', :binary => true}
|
104
|
+
}
|
105
|
+
|
106
|
+
def struct_fields; FIELDS; end
|
107
|
+
|
108
|
+
def validate
|
109
|
+
end
|
110
|
+
|
111
|
+
::Thrift::Struct.generate_accessors self
|
112
|
+
end
|
113
|
+
|
114
|
+
# A BatchMutation object is used to apply a number of Mutations to a single row.
|
115
|
+
class BatchMutation
|
116
|
+
include ::Thrift::Struct, ::Thrift::Struct_Union
|
117
|
+
ROW = 1
|
118
|
+
MUTATIONS = 2
|
119
|
+
|
120
|
+
FIELDS = {
|
121
|
+
ROW => {:type => ::Thrift::Types::STRING, :name => 'row', :binary => true},
|
122
|
+
MUTATIONS => {:type => ::Thrift::Types::LIST, :name => 'mutations', :element => {:type => ::Thrift::Types::STRUCT, :class => Apache::Hadoop::Hbase::Thrift::Mutation}}
|
123
|
+
}
|
124
|
+
|
125
|
+
def struct_fields; FIELDS; end
|
126
|
+
|
127
|
+
def validate
|
128
|
+
end
|
129
|
+
|
130
|
+
::Thrift::Struct.generate_accessors self
|
131
|
+
end
|
132
|
+
|
133
|
+
# Holds row name and then a map of columns to cells.
|
134
|
+
class TRowResult
|
135
|
+
include ::Thrift::Struct, ::Thrift::Struct_Union
|
136
|
+
ROW = 1
|
137
|
+
COLUMNS = 2
|
138
|
+
|
139
|
+
FIELDS = {
|
140
|
+
ROW => {:type => ::Thrift::Types::STRING, :name => 'row', :binary => true},
|
141
|
+
COLUMNS => {:type => ::Thrift::Types::MAP, :name => 'columns', :key => {:type => ::Thrift::Types::STRING, :binary => true}, :value => {:type => ::Thrift::Types::STRUCT, :class => Apache::Hadoop::Hbase::Thrift::TCell}}
|
142
|
+
}
|
143
|
+
|
144
|
+
def struct_fields; FIELDS; end
|
145
|
+
|
146
|
+
def validate
|
147
|
+
end
|
148
|
+
|
149
|
+
::Thrift::Struct.generate_accessors self
|
150
|
+
end
|
151
|
+
|
152
|
+
# An IOError exception signals that an error occurred communicating
|
153
|
+
# to the Hbase master or an Hbase region server. Also used to return
|
154
|
+
# more general Hbase error conditions.
|
155
|
+
class IOError < ::Thrift::Exception
|
156
|
+
include ::Thrift::Struct, ::Thrift::Struct_Union
|
157
|
+
def initialize(message=nil)
|
158
|
+
super()
|
159
|
+
self.message = message
|
160
|
+
end
|
161
|
+
|
162
|
+
MESSAGE = 1
|
163
|
+
|
164
|
+
FIELDS = {
|
165
|
+
MESSAGE => {:type => ::Thrift::Types::STRING, :name => 'message'}
|
166
|
+
}
|
167
|
+
|
168
|
+
def struct_fields; FIELDS; end
|
169
|
+
|
170
|
+
def validate
|
171
|
+
end
|
172
|
+
|
173
|
+
::Thrift::Struct.generate_accessors self
|
174
|
+
end
|
175
|
+
|
176
|
+
# An IllegalArgument exception indicates an illegal or invalid
|
177
|
+
# argument was passed into a procedure.
|
178
|
+
class IllegalArgument < ::Thrift::Exception
|
179
|
+
include ::Thrift::Struct, ::Thrift::Struct_Union
|
180
|
+
def initialize(message=nil)
|
181
|
+
super()
|
182
|
+
self.message = message
|
183
|
+
end
|
184
|
+
|
185
|
+
MESSAGE = 1
|
186
|
+
|
187
|
+
FIELDS = {
|
188
|
+
MESSAGE => {:type => ::Thrift::Types::STRING, :name => 'message'}
|
189
|
+
}
|
190
|
+
|
191
|
+
def struct_fields; FIELDS; end
|
192
|
+
|
193
|
+
def validate
|
194
|
+
end
|
195
|
+
|
196
|
+
::Thrift::Struct.generate_accessors self
|
197
|
+
end
|
198
|
+
|
199
|
+
# An AlreadyExists exceptions signals that a table with the specified
|
200
|
+
# name already exists
|
201
|
+
class AlreadyExists < ::Thrift::Exception
|
202
|
+
include ::Thrift::Struct, ::Thrift::Struct_Union
|
203
|
+
def initialize(message=nil)
|
204
|
+
super()
|
205
|
+
self.message = message
|
206
|
+
end
|
207
|
+
|
208
|
+
MESSAGE = 1
|
209
|
+
|
210
|
+
FIELDS = {
|
211
|
+
MESSAGE => {:type => ::Thrift::Types::STRING, :name => 'message'}
|
212
|
+
}
|
213
|
+
|
214
|
+
def struct_fields; FIELDS; end
|
215
|
+
|
216
|
+
def validate
|
217
|
+
end
|
218
|
+
|
219
|
+
::Thrift::Struct.generate_accessors self
|
220
|
+
end
|
221
|
+
|
222
|
+
end
|
223
|
+
end
|
224
|
+
end
|
225
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require 'massive_record/wrapper/connection'
|
3
|
+
require 'massive_record/wrapper/tables_collection'
|
4
|
+
require 'massive_record/wrapper/table'
|
5
|
+
require 'massive_record/wrapper/row'
|
6
|
+
require 'massive_record/wrapper/column_families_collection'
|
7
|
+
require 'massive_record/wrapper/column_family'
|
8
|
+
require 'massive_record/wrapper/cell'
|
9
|
+
require 'massive_record/wrapper/scanner'
|
10
|
+
|
11
|
+
module MassiveRecord
|
12
|
+
module Wrapper
|
13
|
+
class Base
|
14
|
+
|
15
|
+
def self.config
|
16
|
+
config = YAML.load_file(Rails.root.join('config', 'hbase.yml'))[Rails.env]
|
17
|
+
{ :host => config['host'], :port => config['port'] }
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.connection(opts = {})
|
21
|
+
conn = Connection.new(opts.empty? ? config : opts)
|
22
|
+
conn.open
|
23
|
+
conn
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module MassiveRecord
|
2
|
+
module Wrapper
|
3
|
+
class Cell
|
4
|
+
attr_writer :value
|
5
|
+
attr_accessor :created_at
|
6
|
+
|
7
|
+
class << self
|
8
|
+
def serialize_value(v)
|
9
|
+
serialize?(v) ? v.to_yaml : v.to_s.force_encoding(Encoding::BINARY)
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def serialize?(v)
|
15
|
+
[Hash, Array, NilClass].include?(v.class)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def initialize(opts = {})
|
20
|
+
@value = opts[:value]
|
21
|
+
@created_at = opts[:created_at]
|
22
|
+
end
|
23
|
+
|
24
|
+
def value
|
25
|
+
@value.is_a?(String) ? @value.to_s.force_encoding(Encoding::UTF_8) : @value
|
26
|
+
end
|
27
|
+
|
28
|
+
def deserialize_value
|
29
|
+
is_yaml? ? YAML.load(@value) : value
|
30
|
+
end
|
31
|
+
|
32
|
+
def serialize_value(v)
|
33
|
+
@value = self.class.serialize_value(v)
|
34
|
+
end
|
35
|
+
|
36
|
+
def serialized_value
|
37
|
+
self.class.serialize_value(@value)
|
38
|
+
end
|
39
|
+
|
40
|
+
def is_yaml?
|
41
|
+
@value =~ /^--- \n/ || @value =~ /^--- {}/ || @value =~ /^--- \[\]/
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module MassiveRecord
|
2
|
+
module Wrapper
|
3
|
+
class ColumnFamiliesCollection < Array
|
4
|
+
|
5
|
+
attr_accessor :table
|
6
|
+
|
7
|
+
def create(column_family, opts = {})
|
8
|
+
if column_family.is_a?(MassiveRecord::Wrapper::ColumnFamily)
|
9
|
+
self.push(column_family)
|
10
|
+
else
|
11
|
+
self.push(MassiveRecord::Wrapper::ColumnFamily.new(column_family, opts))
|
12
|
+
end
|
13
|
+
|
14
|
+
true
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module MassiveRecord
|
2
|
+
module Wrapper
|
3
|
+
class ColumnFamily
|
4
|
+
|
5
|
+
attr_accessor :name, :max_versions, :columns
|
6
|
+
|
7
|
+
def initialize(column_name, opts = {})
|
8
|
+
@name = column_name
|
9
|
+
@max_versions = opts[:max_versions] || 10
|
10
|
+
@columns = opts[:columns] || []
|
11
|
+
end
|
12
|
+
|
13
|
+
def descriptor
|
14
|
+
Apache::Hadoop::Hbase::Thrift::ColumnDescriptor.new do |col|
|
15
|
+
col.name = "#{name}:"
|
16
|
+
col.maxVersions = max_versions
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
module MassiveRecord
|
2
|
+
module Wrapper
|
3
|
+
class Connection
|
4
|
+
|
5
|
+
attr_accessor :host, :port, :timeout
|
6
|
+
|
7
|
+
def initialize(opts = {})
|
8
|
+
@timeout = 4000
|
9
|
+
@host = opts[:host]
|
10
|
+
@port = opts[:port] || 9090
|
11
|
+
end
|
12
|
+
|
13
|
+
def transport
|
14
|
+
@transport ||= Thrift::BufferedTransport.new(Thrift::Socket.new(@host, @port, @timeout))
|
15
|
+
end
|
16
|
+
|
17
|
+
def open
|
18
|
+
protocol = Thrift::BinaryProtocol.new(transport)
|
19
|
+
@client = Apache::Hadoop::Hbase::Thrift::Hbase::Client.new(protocol)
|
20
|
+
|
21
|
+
begin
|
22
|
+
transport.open()
|
23
|
+
true
|
24
|
+
rescue
|
25
|
+
raise MassiveRecord::ConnectionException.new, "Unable to connect to HBase on #{@host}, port #{@port}"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def close
|
30
|
+
@transport.close.nil?
|
31
|
+
end
|
32
|
+
|
33
|
+
def client
|
34
|
+
@client
|
35
|
+
end
|
36
|
+
|
37
|
+
def active?
|
38
|
+
@transport.open?
|
39
|
+
end
|
40
|
+
|
41
|
+
def tables
|
42
|
+
collection = TablesCollection.new
|
43
|
+
collection.connection = self
|
44
|
+
getTableNames().each{|table_name| collection.push(table_name)}
|
45
|
+
collection
|
46
|
+
end
|
47
|
+
|
48
|
+
def load_table(table_name)
|
49
|
+
MassiveRecord::Wrapper::Table.new(self, table_name)
|
50
|
+
end
|
51
|
+
|
52
|
+
# Wrapp HBase API to be able to catch errors and try reconnect
|
53
|
+
def method_missing(method, *args)
|
54
|
+
begin
|
55
|
+
open if not @client
|
56
|
+
client.send(method, *args) if @client
|
57
|
+
rescue IOError
|
58
|
+
@client = nil
|
59
|
+
open
|
60
|
+
client.send(method, *args) if @client
|
61
|
+
rescue Thrift::TransportException
|
62
|
+
@transport = nil
|
63
|
+
@client = nil
|
64
|
+
open
|
65
|
+
client.send(method, *args) if @client
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,170 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
module MassiveRecord
|
4
|
+
module Wrapper
|
5
|
+
class Row
|
6
|
+
|
7
|
+
attr_accessor :id, :column_families, :columns, :new_record, :table
|
8
|
+
|
9
|
+
def initialize(opts = {})
|
10
|
+
@id = opts[:id]
|
11
|
+
self.values = opts[:values] || {}
|
12
|
+
@table = opts[:table]
|
13
|
+
@column_families = opts[:column_families] || []
|
14
|
+
@columns = opts[:columns] || {}
|
15
|
+
@new_record = true
|
16
|
+
end
|
17
|
+
|
18
|
+
def column_names
|
19
|
+
columns.keys
|
20
|
+
end
|
21
|
+
|
22
|
+
def fetch_all_column_families
|
23
|
+
@table.fetch_column_family
|
24
|
+
fetch_column_families(@table.column_family_names)
|
25
|
+
end
|
26
|
+
|
27
|
+
def fetch_column_families(list)
|
28
|
+
@column_families = table.column_families.collect do |column_name, description|
|
29
|
+
ColumnFamily.new(column_name.split(":").first, {
|
30
|
+
:row => self,
|
31
|
+
:name => description.name,
|
32
|
+
:max_versions => description.maxVersions,
|
33
|
+
:compression => description.compression,
|
34
|
+
:in_memory => description.inMemory
|
35
|
+
# bloomFilterType, bloomFilterVectorSize, bloomFilterNbHashes, blockCacheEnabled, timeToLive
|
36
|
+
})
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
# = Parse columns / cells and create a Hash from them
|
41
|
+
def values
|
42
|
+
@columns.inject({"id" => id}) {|h, (column_name, cell)| h[column_name] = cell.deserialize_value; h}
|
43
|
+
end
|
44
|
+
|
45
|
+
def values=(data)
|
46
|
+
@values = {}
|
47
|
+
update_columns(data)
|
48
|
+
end
|
49
|
+
|
50
|
+
def update_columns(data = {})
|
51
|
+
data.each do |column_family_name, columns|
|
52
|
+
columns.each do |column_name, values|
|
53
|
+
update_column(column_family_name, column_name, values)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def update_column(column_family_name, column_name, value)
|
59
|
+
column = "#{column_family_name}:#{column_name}"
|
60
|
+
|
61
|
+
if @columns[column].nil?
|
62
|
+
@columns[column] = Cell.new({ :value => Cell.serialize_value(value), :created_at => Time.now })
|
63
|
+
else
|
64
|
+
@columns[column].serialize_value(value)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
# = Merge column values with new data : it implies that column values is a JSON encoded string
|
69
|
+
def merge_columns(data)
|
70
|
+
data.each do |column_family_name, columns|
|
71
|
+
columns.each do |column_name, values|
|
72
|
+
if values.is_a?(Hash)
|
73
|
+
unless @columns["#{column_family_name}:#{column_name}"].nil?
|
74
|
+
column_value = @columns["#{column_family_name}:#{column_name}"].deserialize_value.merge(values)
|
75
|
+
else
|
76
|
+
column_value = values
|
77
|
+
end
|
78
|
+
elsif values.is_a?(Array)
|
79
|
+
unless @columns["#{column_family_name}:#{column_name}"].nil?
|
80
|
+
column_value = @columns["#{column_family_name}:#{column_name}"].deserialize_value | values
|
81
|
+
else
|
82
|
+
column_value = values
|
83
|
+
end
|
84
|
+
else
|
85
|
+
column_value = values
|
86
|
+
end
|
87
|
+
update_column(column_family_name, column_name, column_value)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
# = Parse columns cells and save them
|
93
|
+
def save
|
94
|
+
mutations = []
|
95
|
+
|
96
|
+
@columns.each do |column_name, cell|
|
97
|
+
m = Apache::Hadoop::Hbase::Thrift::Mutation.new
|
98
|
+
m.column = column_name
|
99
|
+
m.value = cell.serialized_value
|
100
|
+
|
101
|
+
mutations.push(m)
|
102
|
+
end
|
103
|
+
|
104
|
+
@table.client.mutateRow(@table.name, id.to_s, mutations).nil?
|
105
|
+
end
|
106
|
+
|
107
|
+
|
108
|
+
#
|
109
|
+
# FIXME
|
110
|
+
#
|
111
|
+
# The thrift wrapper is only working with strings as far as I can see,
|
112
|
+
# and the atomicIncrement call on strings kinda doesn't make sense on strings
|
113
|
+
#
|
114
|
+
# For now I'll implement this without atomicIncrement, to get the behaviour we want.
|
115
|
+
# Guess this in time will either be fixed or raised an not-supported-error. If the
|
116
|
+
# latter is the case I guess we'll need to shift over to a jruby adapter and use the
|
117
|
+
# java api instead of thrift.
|
118
|
+
#
|
119
|
+
def atomic_increment(column_name, by = 1)
|
120
|
+
# @table.client.atomicIncrement(@table.name, id.to_s, column_name, by)
|
121
|
+
value_to_increment = @columns[column_name.to_s].value
|
122
|
+
|
123
|
+
raise "Value to increment (#{value_to_increment}) doesnt seem to be a number!" unless value_to_increment =~ /^\d+$/
|
124
|
+
raise "Argument by must be an integer" unless by.is_a? Fixnum
|
125
|
+
|
126
|
+
value_to_increment = value_to_increment.to_i
|
127
|
+
value_to_increment += by
|
128
|
+
value_to_increment = value_to_increment.to_s
|
129
|
+
|
130
|
+
mutation = Apache::Hadoop::Hbase::Thrift::Mutation.new
|
131
|
+
mutation.column = column_name
|
132
|
+
mutation.value = value_to_increment
|
133
|
+
|
134
|
+
if @table.client.mutateRow(@table.name, id.to_s, [mutation]).nil?
|
135
|
+
value_to_increment
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
def self.populate_from_trow_result(result, connection, table_name, column_families = [])
|
140
|
+
row = self.new
|
141
|
+
row.id = result.row
|
142
|
+
row.new_record = false
|
143
|
+
row.table = Table.new(connection, table_name)
|
144
|
+
row.column_families = column_families
|
145
|
+
|
146
|
+
result.columns.each do |name, value|
|
147
|
+
row.columns[name] = Cell.new({
|
148
|
+
:value => value.value,
|
149
|
+
:created_at => Time.at(value.timestamp / 1000, (value.timestamp % 1000) * 1000)
|
150
|
+
})
|
151
|
+
end
|
152
|
+
|
153
|
+
row
|
154
|
+
end
|
155
|
+
|
156
|
+
def destroy
|
157
|
+
@table.client.deleteAllRow(@table.name, @id).nil?
|
158
|
+
end
|
159
|
+
|
160
|
+
def new_record?
|
161
|
+
@new_record
|
162
|
+
end
|
163
|
+
|
164
|
+
def prev
|
165
|
+
self
|
166
|
+
end
|
167
|
+
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|