hbase-jruby 0.2.6-java → 0.3.0-java

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.
@@ -0,0 +1,115 @@
1
+ require 'forwardable'
2
+
3
+ class HBase
4
+ class Schema
5
+ extend Forwardable
6
+ def_delegators :@schema, :inspect, :to_s
7
+
8
+ def initialize
9
+ @schema = {}
10
+ @lookup = {}
11
+ end
12
+
13
+ # [cq]
14
+ # [cf:cq]
15
+ # @param [Symbol] table
16
+ # @param [Hash] definition
17
+ def []= table, definition
18
+ if definition.nil? || definition.empty?
19
+ delete table
20
+ return nil
21
+ end
22
+
23
+ unless definition.is_a?(Hash)
24
+ raise ArgumentError, 'invalid schema definition: Hash required'
25
+ end
26
+ definition = definition.dup.freeze
27
+ lookup = empty_lookup_table
28
+
29
+ definition.each do |cf, cols|
30
+ unless [Symbol, String].any? { |k| cf.is_a? k }
31
+ raise ArgumentError,
32
+ "invalid schema: use String or Symbol for column family name"
33
+ end
34
+
35
+ # CF:CQ => Type shortcut
36
+ cf = cf.to_s
37
+ if cf.index(':')
38
+ cf, q = key.to_s.split ':', 2
39
+ cols = { q => cols }
40
+ else
41
+ raise ArgumentError, "invalid schema: expected Hash" unless cols.is_a?(Hash)
42
+ end
43
+
44
+ # Family => { Column => Type }
45
+ cols.each do |cq, type|
46
+ raise ArgumentError, "invalid schema" unless type.is_a?(Symbol)
47
+
48
+ # Pattern
49
+ case cq
50
+ when Regexp
51
+ lookup[:pattern][cq] = [cf, nil, type]
52
+ # Exact
53
+ when String, Symbol
54
+ cq = cq.to_s
55
+ cfcq = [cf, cq].join(':')
56
+ [cq, cq.to_sym, cfcq].each do |key|
57
+ lookup[:exact][key] = [cf, cq.to_sym, type]
58
+ end
59
+ else
60
+ raise ArgumentError, "invalid schema"
61
+ end
62
+ end
63
+ end
64
+
65
+ table = table.to_sym
66
+ @lookup[table] = lookup
67
+ @schema[table] = definition
68
+ end
69
+
70
+ # @private
71
+ # @param [Symbol] table
72
+ # @return [Array] CF, CQ, Type. When not found, nil.
73
+ def lookup table, col
74
+ return nil unless lookup = @lookup[table]
75
+
76
+ if match = lookup[:exact][col]
77
+ return match
78
+ elsif pair = lookup[:pattern].find { |k, v| col.to_s =~ k }
79
+ return pair[1].dup.tap { |e| e[1] = col.to_sym }
80
+ end
81
+ end
82
+
83
+ # @private
84
+ # @param [Symbol] table
85
+ def lookup_and_parse table, col
86
+ cf, cq, type = lookup table, col
87
+ cf, cq = Util.parse_column_name(cf ? [cf, cq] : col)
88
+ return [cf, cq, type]
89
+ end
90
+
91
+ # Delete schema for the table
92
+ # @param [Symbol] table
93
+ def delete table
94
+ table = table.to_sym
95
+ @lookup.delete table
96
+ @schema.delete table
97
+ nil
98
+ end
99
+
100
+ # @return [Hash]
101
+ def to_h
102
+ @schema
103
+ end
104
+
105
+ private
106
+ def empty_lookup_table
107
+ {
108
+ :exact => {},
109
+ :pattern => {},
110
+ }
111
+ end
112
+
113
+ end
114
+ end
115
+
@@ -17,6 +17,20 @@ module Aggregation
17
17
  cpc = 'org.apache.hadoop.hbase.coprocessor.AggregateImplementation'
18
18
  add_coprocessor! cpc, &block unless has_coprocessor?(cpc)
19
19
  end
20
+
21
+ # Disables aggregation support for the table (asynchronous)
22
+ # @return [nil]
23
+ def disable_aggregation
24
+ cpc = 'org.apache.hadoop.hbase.coprocessor.AggregateImplementation'
25
+ remove_coprocessor cpc if has_coprocessor?(cpc)
26
+ end
27
+
28
+ # Disables aggregation support for the table (synchronous)
29
+ # @return [nil]
30
+ def disable_aggregation! &block
31
+ cpc = 'org.apache.hadoop.hbase.coprocessor.AggregateImplementation'
32
+ remove_coprocessor! cpc, &block if has_coprocessor?(cpc)
33
+ end
20
34
  end
21
35
 
22
36
  # Performs aggregation with coprocessor
@@ -24,7 +24,7 @@ class Scoped
24
24
  if block_given?
25
25
  scanner = htable.getScanner(filtered_scan)
26
26
  scanner.each do |result|
27
- cnt += 1 if yield(Row.send(:new, result))
27
+ cnt += 1 if yield(Row.send(:new, @table, result))
28
28
  end
29
29
  else
30
30
  scanner = htable.getScanner(filtered_scan_minimum)
@@ -53,11 +53,11 @@ class Scoped
53
53
  case rowkeys
54
54
  when Array
55
55
  htable.get(rowkeys.map { |rk| getify rk }).map { |result|
56
- result.isEmpty ? nil : Row.new(result)
56
+ result.isEmpty ? nil : Row.send(:new, @table, result)
57
57
  }
58
58
  else
59
59
  result = htable.get(getify rowkeys)
60
- result.isEmpty ? nil : Row.new(result)
60
+ result.isEmpty ? nil : Row.send(:new, @table, result)
61
61
  end
62
62
  end
63
63
 
@@ -67,17 +67,15 @@ class Scoped
67
67
  def each
68
68
  check_closed
69
69
 
70
- if block_given?
71
- begin
72
- scanner = htable.getScanner(filtered_scan)
73
- scanner.each do |result|
74
- yield Row.send(:new, result)
75
- end
76
- ensure
77
- scanner.close if scanner
70
+ return enum_for(:each) unless block_given?
71
+
72
+ begin
73
+ scanner = htable.getScanner(filtered_scan)
74
+ scanner.each do |result|
75
+ yield Row.send(:new, @table, result)
78
76
  end
79
- else
80
- self
77
+ ensure
78
+ scanner.close if scanner
81
79
  end
82
80
  end
83
81
 
@@ -208,7 +206,10 @@ class Scoped
208
206
  end
209
207
  end
210
208
  end
211
- spawn :@project, @project + columns
209
+ spawn :@project, @project + columns.map { |c|
210
+ cf, cq, type = @table.lookup_schema(c)
211
+ cf ? [cf, cq] : c
212
+ }
212
213
  end
213
214
 
214
215
  # Returns an HBase::Scoped object with the specified version number limit.
@@ -408,10 +409,10 @@ private
408
409
  }
409
410
  end
410
411
 
411
- def filter_for cf, cq, val
412
+ def filter_for cf, cq, type, val
412
413
  case val
413
414
  when Range
414
- min, max = [val.begin, val.end].map { |k| Util.to_bytes k }
415
+ min, max = [val.begin, val.end].map { |k| Util.to_typed_bytes type, k }
415
416
  FilterList.new(FilterList::Operator::MUST_PASS_ALL, [
416
417
  SingleColumnValueFilter.new(
417
418
  cf, cq,
@@ -440,7 +441,7 @@ private
440
441
  CompareFilter::CompareOp::NOT_EQUAL
441
442
  else
442
443
  if val.length == 1
443
- return filter_for(cf, cq, Util.to_bytes(val))
444
+ return filter_for(cf, cq, nil, Util.to_typed_bytes(type, val))
444
445
  else
445
446
  raise ArgumentError, "Unknown operator: #{op}"
446
447
  end
@@ -456,11 +457,11 @@ private
456
457
  FilterList::Operator::MUST_PASS_ONE
457
458
  end,
458
459
  v.map { |vv|
459
- SingleColumnValueFilter.new(cf, cq, operator, Util.to_bytes(vv))
460
+ SingleColumnValueFilter.new(cf, cq, operator, Util.to_typed_bytes(type, vv))
460
461
  }
461
462
  )
462
463
  else
463
- SingleColumnValueFilter.new(cf, cq, operator, Util.to_bytes(v))
464
+ SingleColumnValueFilter.new(cf, cq, operator, Util.to_typed_bytes(type, v))
464
465
  end
465
466
  }
466
467
  )
@@ -474,7 +475,7 @@ private
474
475
  SingleColumnValueFilter.new(
475
476
  cf, cq,
476
477
  CompareFilter::CompareOp::EQUAL,
477
- Util.to_bytes(val))
478
+ Util.to_typed_bytes(type, val))
478
479
  end
479
480
  end
480
481
 
@@ -593,14 +594,14 @@ private
593
594
  case f
594
595
  when Hash
595
596
  f.map { |col, val|
596
- cf, cq = Util.parse_column_name col
597
+ cf, cq, type = @table.lookup_and_parse col
597
598
 
598
599
  case val
599
600
  when Array
600
601
  FilterList.new(FilterList::Operator::MUST_PASS_ONE,
601
- val.map { |v| filter_for cf, cq, v })
602
+ val.map { |v| filter_for cf, cq, type, v })
602
603
  else
603
- filter_for cf, cq, val
604
+ filter_for cf, cq, type, val
604
605
  end
605
606
  }.flatten
606
607
  when FilterBase, FilterList
@@ -622,6 +623,12 @@ private
622
623
  end
623
624
  end
624
625
 
626
+ # @private
627
+ def typed_bytes col, v
628
+ _, _, type = @table.lookup_schema(col)
629
+ Util.to_typed_bytes(type || :raw, v)
630
+ end
631
+
625
632
  def check_closed
626
633
  raise RuntimeError, "HBase connection is already closed" if @table.closed?
627
634
  end
@@ -44,16 +44,16 @@ class Table
44
44
  :time_range, :at
45
45
  ].each do |method|
46
46
  define_method(method) do |*args|
47
- self.each.send(method, *args)
47
+ self.scoped.send(method, *args)
48
48
  end
49
49
  end
50
50
 
51
51
  def with_java_scan &block
52
- self.each.with_java_scan(&block)
52
+ self.scoped.with_java_scan(&block)
53
53
  end
54
54
 
55
55
  def with_java_get &block
56
- self.each.with_java_get(&block)
56
+ self.scoped.with_java_get(&block)
57
57
  end
58
58
 
59
59
  # Performs PUT operations
@@ -122,7 +122,9 @@ class Table
122
122
 
123
123
  htable.delete specs.map { |spec|
124
124
  rowkey, cfcq, *ts = spec
125
- cf, cq = Util.parse_column_name(cfcq) if cfcq
125
+ if cfcq
126
+ cf, cq, _ = lookup_and_parse cfcq
127
+ end
126
128
 
127
129
  Delete.new(Util.to_bytes rowkey).tap { |del|
128
130
  if !ts.empty?
@@ -163,20 +165,30 @@ class Table
163
165
  # @param [Hash] column_by_hash Column expression to increment amount pairs
164
166
  # @example
165
167
  # table.increment('a000', 'cf1:col1' => 1, 'cf1:col2' => 2)
168
+ # @overload increment(inc_spec)
169
+ # Increase values of multiple columns from multiple rows.
170
+ # @param [Hash] inc_spec { rowkey => { col => by } }
171
+ # @example
172
+ # table.increment 'a000' => { 'cf1:col1' => 1, 'cf1:col2' => 2 },
173
+ # 'a001' => { 'cf1:col1' => 3, 'cf1:col2' => 4 }
166
174
  def increment rowkey, *args
167
175
  check_closed
168
176
 
169
- if args.first.is_a?(Hash)
177
+ if args.empty? && rowkey.is_a?(Hash)
178
+ rowkey.each do |key, spec|
179
+ increment key, spec
180
+ end
181
+ elsif args.first.is_a?(Hash)
170
182
  cols = args.first
171
183
  htable.increment Increment.new(Util.to_bytes rowkey).tap { |inc|
172
184
  cols.each do |col, by|
173
- cf, cq = Util.parse_column_name(col)
185
+ cf, cq, _ = lookup_and_parse col
174
186
  inc.addColumn cf, cq, by
175
187
  end
176
188
  }
177
189
  else
178
190
  col, by = args
179
- cf, cq = Util.parse_column_name(col)
191
+ cf, cq = lookup_and_parse col
180
192
  htable.incrementColumnValue Util.to_bytes(rowkey), cf, cq, by || 1
181
193
  end
182
194
  end
@@ -184,23 +196,32 @@ class Table
184
196
  # Scan through the table
185
197
  # @yield [row] Yields each row in the scope
186
198
  # @yieldparam [HBase::Row] row
199
+ # @return [Enumerator]
200
+ def each &block
201
+ scoped.each(&block)
202
+ end
203
+
204
+ # Returns HBase::Scoped object for this table
187
205
  # @return [HBase::Scoped]
188
- def each
189
- check_closed
206
+ def scoped
207
+ Scoped.send(:new, self)
208
+ end
190
209
 
191
- if block_given?
192
- Scoped.send(:new, self).each { |r| yield r }
193
- else
194
- Scoped.send(:new, self)
195
- end
210
+ def lookup_schema col
211
+ @hbase.schema.lookup @name_sym, col
212
+ end
213
+
214
+ def lookup_and_parse col
215
+ @hbase.schema.lookup_and_parse @name_sym, col
196
216
  end
197
217
 
198
218
  private
199
219
  def initialize hbase, config, htable_pool, name
200
- @hbase = hbase
201
- @config = config
202
- @pool = htable_pool
203
- @name = name.to_s
220
+ @hbase = hbase
221
+ @config = config
222
+ @pool = htable_pool
223
+ @name = name.to_s
224
+ @name_sym = name.to_sym
204
225
  end
205
226
 
206
227
  def check_closed
@@ -210,21 +231,22 @@ private
210
231
  def putify rowkey, props
211
232
  Put.new(Util.to_bytes rowkey).tap { |put|
212
233
  props.each do |col, val|
213
- cf, cq = Util.parse_column_name(col)
234
+ cf, cq, type = lookup_and_parse col
235
+
214
236
  case val
215
237
  when Hash
216
238
  val.each do |t, v|
217
239
  case t
218
240
  # Timestamp / Ruby Time
219
241
  when Time, Fixnum
220
- put.add cf, cq, time_to_long(t), Util.to_bytes(v)
242
+ put.add cf, cq, time_to_long(t), Util.to_typed_bytes(type, v)
221
243
  # Types: :byte, :short, :int, ...
222
244
  else
223
- put.add cf, cq, Util.to_bytes(t => v)
245
+ put.add cf, cq, Util.to_typed_bytes(t, v)
224
246
  end
225
247
  end
226
248
  else
227
- put.add cf, cq, Util.to_bytes(val)
249
+ put.add cf, cq, Util.to_typed_bytes(type, val)
228
250
  end
229
251
  end
230
252
  }
@@ -65,6 +65,41 @@ module Util
65
65
  end
66
66
  end
67
67
 
68
+ def to_typed_bytes type, val
69
+ return Util.to_bytes val if type.nil?
70
+
71
+ import_java_classes!
72
+ case type
73
+ when :string, :str, :symbol, :sym
74
+ val.to_s.to_java_bytes
75
+ when :byte
76
+ [val].to_java(Java::byte)
77
+ when :boolean, :bool
78
+ Bytes.to_bytes val
79
+ when :int
80
+ Bytes.java_send :toBytes, [Java::int], val
81
+ when :short
82
+ Bytes.java_send :toBytes, [Java::short], val
83
+ when :long, :fixnum
84
+ Bytes.java_send :toBytes, [Java::long], val
85
+ when :float, :double
86
+ Bytes.java_send :toBytes, [Java::double], val
87
+ when :bigdecimal
88
+ case val
89
+ when BigDecimal
90
+ Bytes.java_send :toBytes, [java.math.BigDecimal], val.to_java
91
+ when java.math.BigDecimal
92
+ Bytes.java_send :toBytes, [java.math.BigDecimal], val
93
+ else
94
+ raise ArgumentError, "not BigDecimal"
95
+ end
96
+ when :raw
97
+ val
98
+ else
99
+ raise ArgumentError, "invalid type: #{type}"
100
+ end
101
+ end
102
+
68
103
  # Returns Ruby object decoded from the byte array according to the given type
69
104
  # @param [Symbol, Class] type Type to convert to
70
105
  # @param [byte[]] val Java byte array
@@ -92,7 +127,7 @@ module Util
92
127
  Bytes.to_double val
93
128
  when :boolean, :bool
94
129
  Bytes.to_boolean val
95
- when :raw
130
+ when :raw, nil
96
131
  val
97
132
  else
98
133
  raise ArgumentError, "Invalid type: #{type}"
@@ -113,8 +148,6 @@ module Util
113
148
  # @param [Object, Array, KeyValue] col
114
149
  def parse_column_name col
115
150
  case col
116
- when ColumnKey
117
- return col.cf.to_java_bytes, col.cq(:raw)
118
151
  when KeyValue
119
152
  return col.getFamily, col.getQualifier
120
153
  when Array
@@ -122,8 +155,9 @@ module Util
122
155
  when '', nil
123
156
  raise ArgumentError, "Column family not specified"
124
157
  else
125
- cf, cq = KeyValue.parseColumn(col.to_s.to_java_bytes)
126
- cq = JAVA_BYTE_ARRAY_EMPTY if cq.nil? && col.to_s[-1, 1] == ':'
158
+ col = col.to_s
159
+ cf, cq = KeyValue.parseColumn(col.to_java_bytes)
160
+ cq = JAVA_BYTE_ARRAY_EMPTY if cq.nil? && col[-1, 1] == ':'
127
161
  return cf, cq
128
162
  end
129
163
  end
@@ -1,5 +1,5 @@
1
1
  class HBase
2
2
  module JRuby
3
- VERSION = '0.2.6'
3
+ VERSION = '0.3.0'
4
4
  end
5
5
  end
data/lib/hbase-jruby.rb CHANGED
@@ -2,19 +2,19 @@ unless RUBY_PLATFORM =~ /java/
2
2
  raise LoadError, 'Only supports JRuby'
3
3
  end
4
4
 
5
- require "hbase-jruby/version"
6
- require "hbase-jruby/dependency"
7
- require "hbase-jruby/util"
8
- require "hbase-jruby/byte_array"
9
- require "hbase-jruby/column_key"
10
- require "hbase-jruby/cell"
11
- require "hbase-jruby/admin"
12
- require "hbase-jruby/scoped/aggregation"
13
- require "hbase-jruby/scoped"
14
- require "hbase-jruby/table"
15
- require "hbase-jruby/table/admin"
16
- require "hbase-jruby/table/inspection"
17
- require "hbase-jruby/row"
5
+ require 'hbase-jruby/version'
6
+ require 'hbase-jruby/dependency'
7
+ require 'hbase-jruby/util'
8
+ require 'hbase-jruby/byte_array'
9
+ require 'hbase-jruby/cell'
10
+ require 'hbase-jruby/admin'
11
+ require 'hbase-jruby/scoped/aggregation'
12
+ require 'hbase-jruby/scoped'
13
+ require 'hbase-jruby/schema'
14
+ require 'hbase-jruby/table'
15
+ require 'hbase-jruby/table/admin'
16
+ require 'hbase-jruby/table/inspection'
17
+ require 'hbase-jruby/row'
18
18
  require 'hbase-jruby/hbase'
19
19
 
20
20
  HBase.import_java_classes!
data/test/helper.rb CHANGED
@@ -7,10 +7,16 @@ SimpleCov.start
7
7
 
8
8
  RECREATE = false
9
9
 
10
+ unless defined? Enumerator
11
+ Enumerator = Enumerable::Enumerator
12
+ end
13
+
10
14
  $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
11
15
 
12
16
  require "hbase-jruby"
13
- HBase.resolve_dependency!(ENV.fetch('HBASE_JRUBY_TEST_DIST'), :verbose => true)
17
+ if dist = ENV['HBASE_JRUBY_TEST_DIST']
18
+ HBase.resolve_dependency!(dist, :verbose => true)
19
+ end
14
20
  HBase.log4j = { 'log4j.threshold' => 'ERROR' }
15
21
 
16
22
  class TestHBaseJRubyBase < Test::Unit::TestCase
@@ -30,7 +30,7 @@ class TestAggregation < TestHBaseJRubyBase
30
30
 
31
31
  # No projection
32
32
  assert_raise(ArgumentError) { @table.aggregate(:sum) }
33
- assert_raise(ArgumentError) { @table.each.aggregate(:sum) }
33
+ assert_raise(ArgumentError) { @table.scoped.aggregate(:sum) }
34
34
 
35
35
  # Invalid type
36
36
  assert_raise(ArgumentError) { @table.project('cf1:a').aggregate(:sum, :float) }
@@ -39,7 +39,7 @@ class TestByteArray < Test::Unit::TestCase
39
39
  end
40
40
 
41
41
  def test_concat
42
- concat = HBase::ByteArray(100) + HBase::ByteArray(200)
42
+ concat = HBase::ByteArray(100) + HBase::ByteArray[200]
43
43
  assert_instance_of HBase::ByteArray, concat
44
44
  assert_equal 16, concat.to_java_bytes.to_a.length
45
45
 
data/test/test_cell.rb CHANGED
@@ -4,7 +4,7 @@ $LOAD_PATH.unshift File.expand_path('..', __FILE__)
4
4
  require 'helper'
5
5
  require 'bigdecimal'
6
6
 
7
- class TestCell < Test::Unit::TestCase
7
+ class TestCell < TestHBaseJRubyBase
8
8
  import org.apache.hadoop.hbase.KeyValue
9
9
  Util = HBase::Util
10
10
 
@@ -23,9 +23,9 @@ class TestCell < Test::Unit::TestCase
23
23
  { :byte => 100 } => :byte
24
24
  }.each do |value, type|
25
25
  kv = KeyValue.new("rowkey".to_java_bytes, "hello".to_java_bytes, "world".to_java_bytes, ts, Util.to_bytes(value))
26
- cell = HBase::Cell.new(kv)
26
+ cell = HBase::Cell.new(@table, kv) # FIXME
27
27
 
28
- assert_equal "rowkey", cell.rowkey
28
+ assert_equal "rowkey", cell.rowkey(:string)
29
29
  assert_equal "hello", cell.cf, cell.family
30
30
  assert_equal "world", cell.cq, cell.qualifier
31
31
  assert_equal ts, cell.ts
@@ -35,7 +35,6 @@ class TestCell < Test::Unit::TestCase
35
35
  assert_equal value, cell.send(type)
36
36
  end
37
37
  assert HBase::Util.java_bytes?(cell.raw)
38
- assert_equal HBase::ColumnKey.new(:hello, :world), cell.column_key
39
38
  assert_instance_of String, cell.inspect
40
39
  end
41
40
  end
@@ -51,7 +50,7 @@ class TestCell < Test::Unit::TestCase
51
50
  KeyValue.new("rowkey".to_java_bytes, "apple".to_java_bytes, "beta".to_java_bytes, ts, val),
52
51
  KeyValue.new("rowkey".to_java_bytes, "banana".to_java_bytes, "beta".to_java_bytes, ts, val),
53
52
  KeyValue.new("rowkey".to_java_bytes, "banana".to_java_bytes, "gamma".to_java_bytes, ts, val),
54
- ].map { |kv| HBase::Cell.new kv }
53
+ ].map { |kv| HBase::Cell.new @table, kv }
55
54
 
56
55
  assert_equal cells, cells.reverse.sort
57
56
  end