hbase-ruby 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +11 -0
- data/MIT-LICENSE +20 -0
- data/README.textile +81 -0
- data/Rakefile +24 -0
- data/VERSION +1 -0
- data/lib/hbase.rb +44 -0
- data/lib/hbase/client.rb +80 -0
- data/lib/hbase/exception.rb +19 -0
- data/lib/hbase/model.rb +19 -0
- data/lib/hbase/model/column.rb +9 -0
- data/lib/hbase/model/column_descriptor.rb +32 -0
- data/lib/hbase/model/region_descriptor.rb +9 -0
- data/lib/hbase/model/row.rb +11 -0
- data/lib/hbase/model/scanner.rb +13 -0
- data/lib/hbase/model/table_descriptor.rb +8 -0
- data/lib/hbase/operation/meta_operation.rb +10 -0
- data/lib/hbase/operation/row_operation.rb +83 -0
- data/lib/hbase/operation/scanner_operation.rb +82 -0
- data/lib/hbase/operation/table_operation.rb +95 -0
- data/lib/hbase/request.rb +7 -0
- data/lib/hbase/request/basic_request.rb +27 -0
- data/lib/hbase/request/meta_request.rb +17 -0
- data/lib/hbase/request/row_request.rb +34 -0
- data/lib/hbase/request/scanner_request.rb +25 -0
- data/lib/hbase/request/table_request.rb +43 -0
- data/lib/hbase/response.rb +7 -0
- data/lib/hbase/response/basic_response.rb +16 -0
- data/lib/hbase/response/meta_response.rb +35 -0
- data/lib/hbase/response/row_response.rb +31 -0
- data/lib/hbase/response/scanner_response.rb +37 -0
- data/lib/hbase/response/table_response.rb +26 -0
- data/spec/hbase/model/column_descriptor_spec.rb +23 -0
- data/spec/hbase/model/column_spec.rb +12 -0
- data/spec/hbase/model/region_descriptor_spec.rb +4 -0
- data/spec/hbase/model/row_spec.rb +12 -0
- data/spec/hbase/model/scanner.rb +7 -0
- data/spec/hbase/model/table_descriptor_spec.rb +12 -0
- data/spec/hbase/operation/meta_operation_spec.rb +18 -0
- data/spec/hbase/operation/row_operation_spec.rb +39 -0
- data/spec/hbase/operation/scanner_operation_spec.rb +81 -0
- data/spec/hbase/operation/table_operation_spec.rb +57 -0
- data/spec/hbase/record_spec.rb +25 -0
- data/spec/hbase/request/meta_request_spec.rb +10 -0
- data/spec/hbase/request/row_request_spec.rb +5 -0
- data/spec/hbase/request/scanner_request_spec.rb +5 -0
- data/spec/hbase/request/table_request_spec.rb +4 -0
- data/spec/hbase/response/meta_response_spec.rb +4 -0
- data/spec/hbase/response/row_response_spec.rb +4 -0
- data/spec/hbase/response/scanner_response_spec.rb +4 -0
- data/spec/hbase/response/table_response_spec.rb +4 -0
- data/spec/spec.opts +5 -0
- data/spec/spec_helper.rb +7 -0
- data/tasks/rspec.rake +7 -0
- metadata +147 -0
@@ -0,0 +1,82 @@
|
|
1
|
+
module HBase
|
2
|
+
module Operation
|
3
|
+
module ScannerOperation
|
4
|
+
# Trying to maintain some API stability for now
|
5
|
+
def open_scanner(table_name, columns, start_row, stop_row = nil, timestamp = nil)
|
6
|
+
warn "[DEPRECATION] This method is deprecated. Use #open_scanner(table_name, options) instead."
|
7
|
+
|
8
|
+
open_scanner(table_name, {:columns => columns, :start_row => start_row, :stop_row => stop_row, :timestamp => timestamp})
|
9
|
+
end
|
10
|
+
|
11
|
+
def open_scanner(table_name, options = {})
|
12
|
+
raise ArgumentError, "options should be given as a Hash" unless options.instance_of? Hash
|
13
|
+
columns = options.delete(:columns)
|
14
|
+
|
15
|
+
begin
|
16
|
+
request = Request::ScannerRequest.new(table_name)
|
17
|
+
|
18
|
+
xml_data = "<?xml version='1.0' encoding='UTF-8' standalone='yes'?><Scanner "
|
19
|
+
options.each do |key,value|
|
20
|
+
if Model::Scanner::AVAILABLE_OPTS.include? key
|
21
|
+
xml_data << "#{Model::Scanner::AVAILABLE_OPTS[key]}='"
|
22
|
+
xml_data << ( (key == :batch) ? value.to_s : [value.to_s].flatten.pack('m') )
|
23
|
+
xml_data << "' "
|
24
|
+
else
|
25
|
+
warn "[open_scanner] Received invalid option key :#{key}"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
if columns
|
29
|
+
xml_data << ">"
|
30
|
+
[columns].flatten.each do |col|
|
31
|
+
xml_data << "<column>#{[col].flatten.pack('m')}</column>"
|
32
|
+
end
|
33
|
+
xml_data << "</Scanner>"
|
34
|
+
else
|
35
|
+
xml_data << "/>"
|
36
|
+
end
|
37
|
+
|
38
|
+
scanner = Response::ScannerResponse.new(post_response(request.open, xml_data), :open_scanner).parse
|
39
|
+
scanner.table_name = table_name
|
40
|
+
scanner
|
41
|
+
rescue Net::ProtocolError => e
|
42
|
+
if e.to_s.include?("TableNotFoundException")
|
43
|
+
raise TableNotFoundError, "Table #{table_name} Not Found!"
|
44
|
+
else
|
45
|
+
raise StandardError, e.to_s
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def get_rows(scanner, limit = nil)
|
51
|
+
warn "[DEPRECATION] Use of 'limit' here is deprecated. Instead, define the batch size when creating the scanner." if limit
|
52
|
+
begin
|
53
|
+
request = Request::ScannerRequest.new(scanner.table_name)
|
54
|
+
rows = Response::ScannerResponse.new(get(request.get_rows(scanner)), :get_rows).parse
|
55
|
+
rows.each do |row|
|
56
|
+
row.table_name = scanner.table_name
|
57
|
+
end
|
58
|
+
rows
|
59
|
+
rescue StandardError => e
|
60
|
+
if e.to_s.include?("TableNotFoundException")
|
61
|
+
raise TableNotFoundError, "Table #{table_name} Not Found!"
|
62
|
+
else
|
63
|
+
raise StandardError, e.to_s
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def close_scanner(scanner)
|
69
|
+
begin
|
70
|
+
request = Request::ScannerRequest.new(scanner.table_name)
|
71
|
+
Response::ScannerResponse.new(delete_response(request.close(scanner)), :close_scanner).parse
|
72
|
+
rescue StandardError => e
|
73
|
+
if e.to_s.include?("TableNotFoundException")
|
74
|
+
raise TableNotFoundError, "Table #{table_name} Not Found!"
|
75
|
+
else
|
76
|
+
raise StandardError, e.to_s
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
module HBase
|
2
|
+
module Operation
|
3
|
+
module TableOperation
|
4
|
+
def show_table(name)
|
5
|
+
begin
|
6
|
+
request = Request::TableRequest.new(name)
|
7
|
+
table_descriptor = Response::TableResponse.new(get(request.show)).parse
|
8
|
+
rescue Net::ProtocolError => e
|
9
|
+
raise TableNotFoundError, "Table '#{name}' Not found"
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def create_table(name, *args)
|
14
|
+
request = Request::TableRequest.new(name)
|
15
|
+
|
16
|
+
raise StandardError, "Table name must be of type String" unless name.instance_of? String
|
17
|
+
|
18
|
+
begin
|
19
|
+
xml_data = "<?xml version='1.0' encoding='UTF-8' standalone='yes'?><TableSchema name='#{name}' IS_META='false' IS_ROOT='false'>"
|
20
|
+
for arg in args
|
21
|
+
if arg.instance_of? String
|
22
|
+
xml_data << "<ColumnSchema name='#{arg}' />"
|
23
|
+
elsif arg.instance_of? Hash
|
24
|
+
xml_data << "<ColumnSchema "
|
25
|
+
|
26
|
+
arg.each do |k,v|
|
27
|
+
if Model::ColumnDescriptor::AVAILABLE_OPTS.include? k
|
28
|
+
xml_data << "#{Model::ColumnDescriptor::AVAILABLE_OPTS[k]}='#{v}' "
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
xml_data << "/>"
|
33
|
+
else
|
34
|
+
raise StandardError, "#{arg.class.to_s} of #{arg.to_s} is not of Hash Type"
|
35
|
+
end
|
36
|
+
end
|
37
|
+
xml_data << "</TableSchema>"
|
38
|
+
Response::TableResponse.new(post(request.create, xml_data))
|
39
|
+
rescue Net::ProtocolError => e
|
40
|
+
if e.to_s.include?("TableExistsException")
|
41
|
+
raise TableExistsError, "Table '#{name}' already exists"
|
42
|
+
else
|
43
|
+
raise TableFailCreateError, e.message
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def alter_table(name, *args)
|
49
|
+
raise StandardError, "Table name must be of type String" unless name.instance_of? String
|
50
|
+
|
51
|
+
request = Request::TableRequest.new(name)
|
52
|
+
|
53
|
+
begin
|
54
|
+
xml_data = construct_xml_stream(name, *args)
|
55
|
+
Response::TableResponse.new(put(request.update, xml_data))
|
56
|
+
rescue Net::ProtocolError => e
|
57
|
+
if e.to_s.include?("TableNotFoundException")
|
58
|
+
raise TableNotFoundError, "Table '#{name}' not exists"
|
59
|
+
else
|
60
|
+
raise TableFailCreateError, e.message
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def delete_table(name, columns = nil)
|
66
|
+
begin
|
67
|
+
request = Request::TableRequest.new(name)
|
68
|
+
Response::TableResponse.new(delete(request.delete(columns)))
|
69
|
+
rescue Net::ProtocolError => e
|
70
|
+
if e.to_s.include?("TableNotFoundException")
|
71
|
+
raise TableNotFoundError, "Table '#{name}' not exists"
|
72
|
+
elsif e.to_s.include?("TableNotDisabledException")
|
73
|
+
raise TableNotDisabledError, "Table '#{name}' not disabled"
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def destroy_table(name, columns = nil)
|
79
|
+
delete_table(name, columns)
|
80
|
+
end
|
81
|
+
|
82
|
+
def enable_table(name)
|
83
|
+
warn "[DEPRECATION] Explicitly enabling tables isn't required anymore. HBase Stargate will enable/disable as needed."
|
84
|
+
end
|
85
|
+
|
86
|
+
def disable_table(name)
|
87
|
+
warn "[DEPRECATION] Explicitly disabling tables isn't required anymore. HBase Stargate will enable/disable as needed."
|
88
|
+
end
|
89
|
+
|
90
|
+
def table_regions(name, start_row = nil, end_row = nil)
|
91
|
+
end
|
92
|
+
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'cgi'
|
2
|
+
|
3
|
+
module HBase
|
4
|
+
module Request
|
5
|
+
class BasicRequest
|
6
|
+
attr_reader :path
|
7
|
+
|
8
|
+
def initialize(path)
|
9
|
+
@path = path
|
10
|
+
end
|
11
|
+
|
12
|
+
|
13
|
+
protected
|
14
|
+
|
15
|
+
def pack_params columns
|
16
|
+
if columns.is_a? String
|
17
|
+
columns = [columns]
|
18
|
+
elsif columns.is_a? Array
|
19
|
+
else
|
20
|
+
raise StandardError, "Only String or Array type allows for columns"
|
21
|
+
end
|
22
|
+
|
23
|
+
columns.join(',')
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module HBase
|
2
|
+
module Request
|
3
|
+
class RowRequest < BasicRequest
|
4
|
+
attr_reader :table_name
|
5
|
+
attr_reader :name
|
6
|
+
attr_reader :timestamp
|
7
|
+
|
8
|
+
def initialize(table_name, name, timestamp=nil)
|
9
|
+
@table_name, @name, @timestamp = CGI.escape(table_name), CGI.escape(name), timestamp
|
10
|
+
path = "/#{@table_name}/#{@name}"
|
11
|
+
super(path)
|
12
|
+
end
|
13
|
+
|
14
|
+
def show(columns = nil, options = { })
|
15
|
+
@path << "/#{pack_params(columns)}" if columns
|
16
|
+
@path << "/#{@timestamp}" if @timestamp
|
17
|
+
@path << "?v=#{options[:version]}" if options[:version]
|
18
|
+
@path
|
19
|
+
end
|
20
|
+
|
21
|
+
def create(columns = nil)
|
22
|
+
@path << "/#{pack_params(columns)}" if columns
|
23
|
+
@path << "/#{@timestamp}" if @timestamp
|
24
|
+
@path
|
25
|
+
end
|
26
|
+
|
27
|
+
def delete(columns = nil)
|
28
|
+
@path << "/#{pack_params(columns)}" if columns
|
29
|
+
@path << "/#{@timestamp}" if @timestamp
|
30
|
+
@path
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module HBase
|
2
|
+
module Request
|
3
|
+
class ScannerRequest < BasicRequest
|
4
|
+
attr_reader :table_name
|
5
|
+
|
6
|
+
def initialize(table_name)
|
7
|
+
@table_name = CGI.escape(table_name)
|
8
|
+
path = "/#{@table_name}/scanner"
|
9
|
+
super(path)
|
10
|
+
end
|
11
|
+
|
12
|
+
def open
|
13
|
+
@path
|
14
|
+
end
|
15
|
+
|
16
|
+
def get_rows(scanner)
|
17
|
+
@path = URI.parse(scanner.scanner_url).path
|
18
|
+
end
|
19
|
+
|
20
|
+
def close(scanner)
|
21
|
+
@path = URI.parse(scanner.scanner_url).path
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module HBase
|
2
|
+
module Request
|
3
|
+
class TableRequest < BasicRequest
|
4
|
+
attr_reader :name
|
5
|
+
attr_reader :body
|
6
|
+
|
7
|
+
def initialize(name)
|
8
|
+
super("")
|
9
|
+
@name = CGI.escape(name) if name
|
10
|
+
end
|
11
|
+
|
12
|
+
def show
|
13
|
+
@path << "/#{name}/schema"
|
14
|
+
end
|
15
|
+
|
16
|
+
def regions(start_row = nil, end_row = nil)
|
17
|
+
#TODO no handle the args!
|
18
|
+
@path << "/#{name}/regions"
|
19
|
+
end
|
20
|
+
|
21
|
+
def create
|
22
|
+
@path << "/#{name}/schema"
|
23
|
+
end
|
24
|
+
|
25
|
+
def update
|
26
|
+
@path << "/#{name}"
|
27
|
+
end
|
28
|
+
|
29
|
+
def enable
|
30
|
+
@path << "/#{name}/enable"
|
31
|
+
end
|
32
|
+
|
33
|
+
def disable
|
34
|
+
@path << "/#{name}/disable"
|
35
|
+
end
|
36
|
+
|
37
|
+
def delete(columns = nil)
|
38
|
+
warn "[DEPRECATION] the use of the 'columns' argument is deprecated. Please use the delete method without any arguments." if columns
|
39
|
+
@path << "/#{name}/schema"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module HBase
|
2
|
+
module Response
|
3
|
+
class MetaResponse < BasicResponse
|
4
|
+
attr_reader :method
|
5
|
+
|
6
|
+
def initialize(raw_data, method)
|
7
|
+
@method = method
|
8
|
+
super(raw_data)
|
9
|
+
end
|
10
|
+
|
11
|
+
def parse_content(raw_data)
|
12
|
+
case @method
|
13
|
+
when :list_tables
|
14
|
+
if raw_data.blank? || raw_data == "null" # "null" from json
|
15
|
+
puts "There are no available tables in the HBase"
|
16
|
+
return []
|
17
|
+
end
|
18
|
+
|
19
|
+
tables = []
|
20
|
+
doc = JSON.parse(raw_data)
|
21
|
+
|
22
|
+
doc["table"].each do |table|
|
23
|
+
name = table["name"].strip rescue nil
|
24
|
+
t = Model::TableDescriptor.new(:name => name)
|
25
|
+
tables << t
|
26
|
+
end
|
27
|
+
|
28
|
+
tables
|
29
|
+
else
|
30
|
+
puts "method '#{@method}' not supported yet"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module HBase
|
2
|
+
module Response
|
3
|
+
class RowResponse < BasicResponse
|
4
|
+
def parse_content(raw_data)
|
5
|
+
doc = JSON.parse(raw_data)
|
6
|
+
rows = doc["Row"]
|
7
|
+
|
8
|
+
model_rows = []
|
9
|
+
rows.each do |row|
|
10
|
+
rname = row["key"].strip.unpack("m").first
|
11
|
+
count = row["Cell"].size
|
12
|
+
columns = []
|
13
|
+
|
14
|
+
row["Cell"].each do |col|
|
15
|
+
name = col["column"].strip.unpack('m').first
|
16
|
+
value = col["$"].strip.unpack('m').first rescue nil
|
17
|
+
timestamp = col["timestamp"].to_i
|
18
|
+
|
19
|
+
columns << HBase::Model::Column.new( :name => name,
|
20
|
+
:value => value,
|
21
|
+
:timestamp => timestamp)
|
22
|
+
end
|
23
|
+
|
24
|
+
model_rows << HBase::Model::Row.new(:name => rname, :total_count => count, :columns => columns)
|
25
|
+
end
|
26
|
+
|
27
|
+
model_rows
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module HBase
|
2
|
+
module Response
|
3
|
+
class ScannerResponse < BasicResponse
|
4
|
+
attr_reader :method
|
5
|
+
|
6
|
+
def initialize(raw_data, method)
|
7
|
+
@method = method
|
8
|
+
super(raw_data)
|
9
|
+
end
|
10
|
+
|
11
|
+
def parse_content(raw_data)
|
12
|
+
case @method
|
13
|
+
when :open_scanner
|
14
|
+
case raw_data
|
15
|
+
when Net::HTTPCreated
|
16
|
+
HBase::Model::Scanner.new(:scanner_url => raw_data["Location"])
|
17
|
+
else
|
18
|
+
raise StandardError, "Unable to open scanner. Received the following message: #{raw_data.message}"
|
19
|
+
end
|
20
|
+
when :get_rows
|
21
|
+
# Dispatch it to RowResponse, since that method is made
|
22
|
+
# to deal with rows already.
|
23
|
+
RowResponse.new(raw_data).parse
|
24
|
+
when :close_scanner
|
25
|
+
case raw_data
|
26
|
+
when Net::HTTPOK
|
27
|
+
return true
|
28
|
+
else
|
29
|
+
raise StandardError, "Unable to close scanner. Received the following message: #{raw_data.message}"
|
30
|
+
end
|
31
|
+
else
|
32
|
+
puts "method '#{@method}' not supported yet"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|