hbase-ruby 1.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/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
|