hbase-jruby 0.1.1-java → 0.1.2-java

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,8 +1,13 @@
1
1
  class HBase
2
- # Scope of table scan
2
+ # Scope of data access
3
3
  # @author Junegunn Choi <junegunn.c@gmail.com>
4
+ # @!attribute [r] table
5
+ # @return [HBase::Table] HBase::Table instance for this scope
4
6
  class Scoped
5
7
  include Enumerable
8
+ include Scoped::Aggregation
9
+
10
+ attr_reader :table
6
11
 
7
12
  # A clean HBase::Scoped object for the same table
8
13
  # @return [HBase::Scope] A clean HBase::Scoped object for the same table
@@ -14,8 +19,14 @@ class Scoped
14
19
  # @return [Fixnum, Bignum] The number of rows in the scope
15
20
  def count
16
21
  cnt = 0
17
- htable.getScanner(filtered_scan_minimum).each do
18
- cnt += 1
22
+ if block_given?
23
+ htable.getScanner(filtered_scan).each do |result|
24
+ cnt += 1 if yield(Result.send(:new, result))
25
+ end
26
+ else
27
+ htable.getScanner(filtered_scan_minimum).each do
28
+ cnt += 1
29
+ end
19
30
  end
20
31
  cnt
21
32
  end
@@ -124,26 +135,14 @@ class Scoped
124
135
  # @param [Array<Hash, FilterBase, FilterList>] filters
125
136
  # @return [HBase::Scoped] HBase::Scoped object also with the specified filters
126
137
  def filter *filters
127
- spawn :@filters, @filters + filters.map { |f|
128
- case f
129
- when Hash
130
- f.map { |col, val|
131
- cf, cq = Util.parse_column_name col
138
+ spawn :@filters, @filters + parse_filter_input(filters)
139
+ end
132
140
 
133
- case val
134
- when Array
135
- FilterList.new(FilterList::Operator::MUST_PASS_ONE,
136
- val.map { |v| filter_for cf, cq, v })
137
- else
138
- filter_for cf, cq, val
139
- end
140
- }.flatten
141
- when FilterBase, FilterList
142
- f
143
- else
144
- raise ArgumentError, "Unknown filter type"
145
- end
146
- }.flatten
141
+ # Returns an HBase::Scoped object with the additional filters which will cause early termination of scan
142
+ # @param [Array<Hash, FilterBase, FilterList>] filters
143
+ # @return [HBase::Scoped]HBase::Scoped object with the additional filters which will cause early termination of scan
144
+ def while *filters
145
+ spawn :@filters, @filters + parse_filter_input(filters).map { |filter| WhileMatchFilter.new(filter) }
147
146
  end
148
147
 
149
148
  # Returns an HBase::Scoped object with the specified row number limit
@@ -354,7 +353,7 @@ private
354
353
  when Hash
355
354
  FilterList.new(FilterList::Operator::MUST_PASS_ALL,
356
355
  val.map { |op, v|
357
- operator =
356
+ operator =
358
357
  case op
359
358
  when :gt, :>
360
359
  CompareFilter::CompareOp::GREATER
@@ -392,6 +391,12 @@ private
392
391
  end
393
392
  }
394
393
  )
394
+ when Regexp
395
+ SingleColumnValueFilter.new(
396
+ cf, cq,
397
+ CompareFilter::CompareOp::EQUAL,
398
+ RegexStringComparator.new(val.to_s)
399
+ )
395
400
  else
396
401
  SingleColumnValueFilter.new(
397
402
  cf, cq,
@@ -484,6 +489,29 @@ private
484
489
 
485
490
  [start.java, stop.stopkey_bytes_for_prefix]
486
491
  end
492
+
493
+ def parse_filter_input filters
494
+ filters.map { |f|
495
+ case f
496
+ when Hash
497
+ f.map { |col, val|
498
+ cf, cq = Util.parse_column_name col
499
+
500
+ case val
501
+ when Array
502
+ FilterList.new(FilterList::Operator::MUST_PASS_ONE,
503
+ val.map { |v| filter_for cf, cq, v })
504
+ else
505
+ filter_for cf, cq, val
506
+ end
507
+ }.flatten
508
+ when FilterBase, FilterList
509
+ f
510
+ else
511
+ raise ArgumentError, "Unknown filter type"
512
+ end
513
+ }.flatten
514
+ end
487
515
  end#Scoped
488
516
  end#HBase
489
517
 
@@ -4,11 +4,15 @@ require 'thread'
4
4
  class HBase
5
5
  # @!attribute [r] name
6
6
  # @return [String] The name of the table
7
+ # @!attribute [r] config
8
+ # @return [org.apache.hadoop.conf.Configuration]
7
9
  class Table
8
10
  attr_reader :name
11
+ attr_reader :config
9
12
 
10
13
  include Enumerable
11
14
  include Admin
15
+ include Scoped::Aggregation::Admin
12
16
 
13
17
  # Returns a read-only org.apache.hadoop.hbase.HTableDescriptor object
14
18
  # @return [org.apache.hadoop.hbase.client.UnmodifyableHTableDescriptor]
@@ -160,11 +164,59 @@ class Table
160
164
  end
161
165
  end
162
166
 
167
+ # Adds the table coprocessor to the table
168
+ # @param [String] class_name Full class name of the coprocessor
169
+ # @param [Hash] props Coprocessor properties
170
+ # @option props [String] path The path of the JAR file
171
+ # @option props [Fixnum] priority Coprocessor priority
172
+ # @option props [Hash<#to_s, #to_s>] params Arbitrary key-value parameter pairs passed into the coprocessor
173
+ def add_coprocessor! class_name, props = {}
174
+ with_admin do |admin|
175
+ while_disabled(admin) do
176
+
177
+ htd = admin.get_table_descriptor(@name.to_java_bytes)
178
+ if props.empty?
179
+ htd.addCoprocessor class_name
180
+ else
181
+ path, priority, params = props.values_at :path, :priority, :params
182
+ params = Hash[ params.map { |k, v| [k.to_s, v.to_s] } ]
183
+ htd.addCoprocessor class_name, path, priority || Coprocessor::PRIORITY_USER, params
184
+ end
185
+ admin.modifyTable @name.to_java_bytes, htd
186
+ wait_async_admin(admin)
187
+ end
188
+ end
189
+ end
190
+
191
+ # Removes the coprocessor from the table.
192
+ # @param [String] class_name Full class name of the coprocessor
193
+ # @return [nil]
194
+ def remove_coprocessor! name
195
+ unless org.apache.hadoop.hbase.HTableDescriptor.respond_to?(:removeCoprocessor)
196
+ raise NotImplementedError, "org.apache.hadoop.hbase.HTableDescriptor.removeCoprocessor not implemented"
197
+ end
198
+ with_admin do |admin|
199
+ while_disabled(admin) do
200
+ htd = admin.get_table_descriptor(@name.to_java_bytes)
201
+ htd.removeCoprocessor name
202
+ admin.modifyTable @name.to_java_bytes, htd
203
+ wait_async_admin(admin)
204
+ end
205
+ end
206
+ end
207
+
208
+ # Return if the table has the coprocessor of the given class name
209
+ # @param [String] class_name Full class name of the coprocessor
210
+ # @return [true, false]
211
+ def has_coprocessor? class_name
212
+ descriptor.hasCoprocessor(class_name)
213
+ end
214
+
163
215
  # Enables the table
164
216
  # @return [nil]
165
217
  def enable!
166
218
  with_admin do |admin|
167
- admin.enableTable @name unless admin.isTableEnabled(@name)
219
+ admin.enableTable @name unless admin.isTableEnabled(@name)
168
220
  end
169
221
  end
170
222
 
@@ -172,7 +224,7 @@ class Table
172
224
  # @return [nil]
173
225
  def disable!
174
226
  with_admin do |admin|
175
- admin.disableTable @name if admin.isTableEnabled(@name)
227
+ admin.disableTable @name if admin.isTableEnabled(@name)
176
228
  end
177
229
  end
178
230
 
@@ -190,24 +242,19 @@ class Table
190
242
  with_admin do |admin|
191
243
  raise RuntimeError, 'Table does not exist' unless admin.tableExists @name
192
244
 
193
- admin.disableTable @name if admin.isTableEnabled(@name)
245
+ admin.disableTable @name if admin.isTableEnabled(@name)
194
246
  admin.deleteTable @name
195
247
  close
196
248
  end
197
249
  end
198
250
 
199
- # @overload get(rowkey)
200
- # Single GET.
201
- # Gets a record with the given rowkey. If the record is not found, nil is returned.
202
- # @param [Object] rowkey Rowkey
203
- # @return [HBase::Result, nil]
204
- # @overload get(rowkeys)
205
- # Batch GET. Gets an array of records with the given rowkeys.
206
- # Nonexistent records will be returned as nils.
207
- # @param [Array<Object>] rowkeys Rowkeys
208
- # @return [Array<HBase::Result>]
209
- def get rowkeys
210
- each.get rowkeys
251
+ [:get, :count, :aggregate,
252
+ :range, :project, :filter, :while,
253
+ :limit, :versions, :caching, :batch
254
+ ].each do |method|
255
+ define_method(method) do |*args|
256
+ self.each.send(method, *args)
257
+ end
211
258
  end
212
259
 
213
260
  # @overload put(rowkey, data)
@@ -272,7 +319,7 @@ class Table
272
319
  rowkey, cfcq, *ts = spec
273
320
  cf, cq = Util.parse_column_name(cfcq) if cfcq
274
321
 
275
- Delete.new(Util.to_bytes rowkey).tap { |del|
322
+ Delete.new(Util.to_bytes rowkey).tap { |del|
276
323
  if !ts.empty?
277
324
  ts.each do |t|
278
325
  del.deleteColumn cf, cq, t
@@ -317,12 +364,6 @@ class Table
317
364
  end
318
365
  end
319
366
 
320
- # Returns the count of the rows in the table
321
- # @return [Fixnum]
322
- def count
323
- each.count
324
- end
325
-
326
367
  # Scan through the table
327
368
  # @yield [HBase::Result] Yields each row in the scope
328
369
  # @return [HBase::Scoped]
@@ -334,48 +375,6 @@ class Table
334
375
  end
335
376
  end
336
377
 
337
- # @see HBase::Scoped#range
338
- # @return [HBase::Scoped]
339
- def range *key_range
340
- each.range(*key_range)
341
- end
342
-
343
- # @see HBase::Scoped#project
344
- # @return [HBase::Scoped]
345
- def project *columns
346
- each.project(*columns)
347
- end
348
-
349
- # @see HBase::Scoped#filter
350
- # @return [HBase::Scoped]
351
- def filter *filters
352
- each.filter(*filters)
353
- end
354
-
355
- # @see HBase::Scoped#limit
356
- # @return [HBase::Scoped]
357
- def limit rows
358
- each.limit rows
359
- end
360
-
361
- # @see HBase::Scoped#versions
362
- # @return [HBase::Scoped]
363
- def versions vs
364
- each.versions vs
365
- end
366
-
367
- # @see HBase::Scoped#caching
368
- # @return [HBase::Scoped]
369
- def caching rows
370
- each.caching rows
371
- end
372
-
373
- # @see HBase::Scoped#batch
374
- # @return [HBase::Scoped]
375
- def batch b
376
- each.batch b
377
- end
378
-
379
378
  # Returns the underlying org.apache.hadoop.hbase.client.HTable object (local to current thread)
380
379
  # @return [org.apache.hadoop.hbase.client.HTable]
381
380
  def htable
@@ -405,7 +404,7 @@ private
405
404
 
406
405
  def while_disabled admin
407
406
  begin
408
- admin.disableTable @name if admin.isTableEnabled(@name)
407
+ admin.disableTable @name if admin.isTableEnabled(@name)
409
408
  yield
410
409
  ensure
411
410
  admin.enableTable @name
@@ -416,7 +415,14 @@ private
416
415
  Put.new(Util.to_bytes rowkey).tap { |put|
417
416
  props.each do |col, val|
418
417
  cf, cq = Util.parse_column_name(col)
419
- put.add cf, cq, Util.to_bytes(val)
418
+ case val
419
+ when Hash
420
+ val.each do |ts, v|
421
+ put.add cf, cq, ts, Util.to_bytes(v)
422
+ end
423
+ else
424
+ put.add cf, cq, Util.to_bytes(val)
425
+ end
420
426
  end
421
427
  }
422
428
  end
@@ -2,7 +2,7 @@ require 'bigdecimal'
2
2
 
3
3
  class HBase
4
4
  module Util
5
- JAVA_BYTE_ARRAY_EMPTY = [].to_java(Java::byte)
5
+ JAVA_BYTE_ARRAY_EMPTY = [].to_java(Java::byte)
6
6
  JAVA_BYTE_ARRAY_CLASS = JAVA_BYTE_ARRAY_EMPTY.java_class
7
7
 
8
8
  class << self
@@ -24,7 +24,7 @@ module Util
24
24
  when nil
25
25
  ''.to_java_bytes
26
26
  when Bignum
27
- Bytes.java_send :toBytes, [java.math.BigDecimal], java.math.BigDecimal.new(v.to_s)
27
+ raise ArgumentError, "Integer too large. Consider storing it as a BigDecimal."
28
28
  when BigDecimal
29
29
  Bytes.java_send :toBytes, [java.math.BigDecimal], v.to_java
30
30
  when java.math.BigDecimal
@@ -52,8 +52,6 @@ module Util
52
52
  Bytes.to_long val
53
53
  when :symbol, :sym
54
54
  Bytes.to_string(val).to_sym
55
- when :bignum, :bigint, :biginteger
56
- BigDecimal.new(Bytes.to_big_decimal(val).to_s).to_i
57
55
  when :bigdecimal
58
56
  BigDecimal.new(Bytes.to_big_decimal(val).to_s)
59
57
  when :float, :double
@@ -133,6 +131,10 @@ module Util
133
131
  org.apache.hadoop.hbase.KeyValue
134
132
  ]
135
133
 
134
+ imp.call HBase::Result, %w[
135
+ org.apache.hadoop.hbase.util.Bytes
136
+ ]
137
+
136
138
  imp.call HBase::ColumnKey, %w[
137
139
  java.util.Arrays
138
140
  org.apache.hadoop.hbase.util.Bytes
@@ -146,6 +148,7 @@ module Util
146
148
  org.apache.hadoop.hbase.client.Put
147
149
  org.apache.hadoop.hbase.io.hfile.Compression
148
150
  org.apache.hadoop.hbase.regionserver.StoreFile
151
+ org.apache.hadoop.hbase.Coprocessor
149
152
  ]
150
153
 
151
154
  imp.call HBase::Scoped, %w[
@@ -153,15 +156,19 @@ module Util
153
156
  org.apache.hadoop.hbase.client.Scan
154
157
  org.apache.hadoop.hbase.filter.BinaryComparator
155
158
  org.apache.hadoop.hbase.filter.ColumnPaginationFilter
156
- org.apache.hadoop.hbase.filter.MultipleColumnPrefixFilter
157
159
  org.apache.hadoop.hbase.filter.ColumnRangeFilter
158
160
  org.apache.hadoop.hbase.filter.CompareFilter
159
161
  org.apache.hadoop.hbase.filter.FilterBase
160
162
  org.apache.hadoop.hbase.filter.FilterList
161
163
  org.apache.hadoop.hbase.filter.KeyOnlyFilter
164
+ org.apache.hadoop.hbase.filter.MultipleColumnPrefixFilter
162
165
  org.apache.hadoop.hbase.filter.PrefixFilter
166
+ org.apache.hadoop.hbase.filter.RegexStringComparator
163
167
  org.apache.hadoop.hbase.filter.RowFilter
164
168
  org.apache.hadoop.hbase.filter.SingleColumnValueFilter
169
+ org.apache.hadoop.hbase.filter.WhileMatchFilter
170
+ org.apache.hadoop.hbase.client.coprocessor.AggregationClient
171
+ org.apache.hadoop.hbase.client.coprocessor.LongColumnInterpreter
165
172
  ]
166
173
  end
167
174
  end
@@ -1,5 +1,5 @@
1
1
  class HBase
2
2
  module JRuby
3
- VERSION = "0.1.1"
3
+ VERSION = "0.1.2"
4
4
  end
5
5
  end
data/lib/hbase-jruby.rb CHANGED
@@ -9,6 +9,7 @@ require "hbase-jruby/byte_array"
9
9
  require "hbase-jruby/column_key"
10
10
  require "hbase-jruby/cell"
11
11
  require "hbase-jruby/admin"
12
+ require "hbase-jruby/scoped/aggregation"
12
13
  require "hbase-jruby/scoped"
13
14
  require "hbase-jruby/table"
14
15
  require "hbase-jruby/result"
data/test/helper.rb CHANGED
@@ -38,9 +38,7 @@ class TestHBaseJRubyBase < Test::Unit::TestCase
38
38
  @table.enable! if @table.disabled?
39
39
 
40
40
  unless RECREATE
41
- @table.each do |row|
42
- @table.delete row.rowkey :raw
43
- end
41
+ @table.delete(*@table.map { |row| [row.rowkey(:raw)] })
44
42
  assert_equal 0, @table.count
45
43
  end
46
44
  end
@@ -0,0 +1,40 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $LOAD_PATH.unshift File.expand_path('..', __FILE__)
4
+ require 'helper'
5
+
6
+ class TestAggregation < TestHBaseJRubyBase
7
+ def test_aggregation
8
+ (1..100).each do |idx|
9
+ @table.put idx, 'cf1:a' => idx, 'cf1:b' => idx * 2
10
+ end
11
+
12
+ @table.enable_aggregation!
13
+
14
+ lci = org.apache.hadoop.hbase.client.coprocessor.LongColumnInterpreter.new
15
+ [nil, :fixnum, :int, :integer, lci].each do |ci|
16
+ assert_equal 100, @table.project('cf1:a').aggregate(:row_count, *[*ci])
17
+ assert_equal 5050, @table.project('cf1:a').aggregate(:sum, *[*ci])
18
+ assert_equal 1, @table.project('cf1:a').aggregate(:min, *[*ci])
19
+ assert_equal 100, @table.project('cf1:a').aggregate(:max, *[*ci])
20
+ assert_equal 50.5, @table.project('cf1:a').aggregate(:avg, *[*ci])
21
+ assert_equal 28, @table.project('cf1:a').aggregate(:std, *[*ci]).to_i # FIXME: 28 or 29?
22
+ end
23
+
24
+ [%w[cf1:a cf1:b], %w[cf1]].each do |prj|
25
+ assert_equal 5050 * 3, @table.project(*prj).aggregate(:sum)
26
+ assert_equal 1, @table.project(*prj).aggregate(:min)
27
+ assert_equal 200, @table.project(*prj).aggregate(:max)
28
+ end
29
+
30
+ # No projection
31
+ assert_raise(ArgumentError) { @table.aggregate(:sum) }
32
+ assert_raise(ArgumentError) { @table.each.aggregate(:sum) }
33
+
34
+ # Invalid type
35
+ assert_raise(ArgumentError) { @table.project('cf1:a').aggregate(:sum, :float) }
36
+
37
+ @table.drop!
38
+ end
39
+ end
40
+
data/test/test_cell.rb CHANGED
@@ -14,7 +14,6 @@ class TestCell < Test::Unit::TestCase
14
14
  'value' => :string,
15
15
  :value => :symbol,
16
16
  123 => :fixnum,
17
- 123456789012345678901234567890 => :bignum,
18
17
  123.456 => :float,
19
18
  true => :boolean,
20
19
  false => :boolean,
data/test/test_hbase.rb CHANGED
@@ -3,7 +3,7 @@
3
3
  $LOAD_PATH.unshift File.expand_path('..', __FILE__)
4
4
  require 'helper'
5
5
 
6
- class TestHBase < TestHBaseJRubyBase
6
+ class TestHBase < TestHBaseJRubyBase
7
7
  def test_tables
8
8
  assert @hbase.table_names.include?(TABLE)
9
9
  assert @hbase.tables.map(&:name).include?(TABLE)
data/test/test_scoped.rb CHANGED
@@ -3,7 +3,7 @@
3
3
  $LOAD_PATH.unshift File.expand_path('..', __FILE__)
4
4
  require 'helper'
5
5
 
6
- class TestScoped < TestHBaseJRubyBase
6
+ class TestScoped < TestHBaseJRubyBase
7
7
  def test_invalid_limit
8
8
  assert_raise(ArgumentError) { @table.limit }
9
9
  assert_raise(ArgumentError) { @table.limit(-1) }
@@ -88,9 +88,14 @@ class TestScoped < TestHBaseJRubyBase
88
88
  assert_equal 9, @table.range(111..150).filter('cf1:a' => 131...140).count
89
89
  assert_equal 2, @table.range(111..150).filter('cf1:a' => 131...140, 'cf2:b' => 132..133).count
90
90
 
91
+ # Count with block
92
+ assert_equal 5, @table.range(111..150).filter('cf1:a' => 131..140).
93
+ count { |result| result.fixnum('cf1:a') % 2 == 0 }
94
+
91
95
  # Unscope
92
96
  assert_equal 50, @table.range(111..150).filter('cf1:a' => 131...140, 'cf2:b' => 132..133).unscope.count
93
97
  end
98
+
94
99
  def test_scan
95
100
  insert = lambda do
96
101
  (40..70).each do |i|
@@ -314,5 +319,44 @@ class TestScoped < TestHBaseJRubyBase
314
319
  # batch(10).
315
320
  # map { |row| [row.rowkey(:fixnum), row.count].map(&:to_s).join ': ' }
316
321
  end
322
+
323
+ def test_while
324
+ (0...100).each do |idx|
325
+ @table.put idx, 'cf1:a' => idx % 10, 'cf2:b' => 'Hello'
326
+ end
327
+
328
+ assert_equal 20, @table.filter('cf1:a' => { lte: 1 }, 'cf2:b' => 'Hello').count
329
+ assert_equal 2, @table.while( 'cf1:a' => { lte: 1 }, 'cf2:b' => 'Hello').count
330
+
331
+ # while == filter for gets
332
+ assert_equal 20, @table.filter('cf1:a' => { lte: 1 }, 'cf2:b' => 'Hello').get((0..100).to_a).compact.length
333
+ assert_equal 20, @table.while( 'cf1:a' => { lte: 1 }, 'cf2:b' => 'Hello').get((0..100).to_a).compact.length
334
+ end
335
+
336
+ def test_min_max
337
+ (0...100).each do |idx|
338
+ @table.put idx, 'cf1:a' => 1
339
+ assert_equal 0, @table.to_a.reverse.min.rowkey(:fixnum)
340
+ assert_equal idx, @table.to_a.reverse.max.rowkey(:fixnum)
341
+ end
342
+ end
343
+
344
+ def test_regex
345
+ ('aa'..'zz').each do |rowkey|
346
+ @table.put rowkey, 'cf1:a' => rowkey
347
+ end
348
+
349
+ assert_equal 1, @table.filter('cf1:a' => /gg/).count
350
+ assert_equal 1, @table.filter('cf1:a' => /GG/i).count
351
+ assert_equal 51, @table.filter('cf1:a' => /g/).count
352
+ assert_equal 0, @table.filter('cf1:a' => /G/).count
353
+ assert_equal 51, @table.filter('cf1:a' => /G/i).count
354
+ assert_equal 26, @table.filter('cf1:a' => /g./).count
355
+ assert_equal 26, @table.filter('cf1:a' => /^g/).count
356
+ assert_equal 26, @table.filter('cf1:a' => /g$/).count
357
+ assert_equal 2, @table.filter('cf1:a' => /gg|ff/).count
358
+ assert_equal 28, @table.filter('cf1:a' => ['aa', 'cc', /^g/]).count
359
+ assert_equal 54, @table.filter('cf1:a' => ['aa', 'cc', /^g/, { gte: 'xa', lt: 'y'}]).count
360
+ end
317
361
  end
318
362
 
data/test/test_table.rb CHANGED
@@ -2,8 +2,9 @@
2
2
 
3
3
  $LOAD_PATH.unshift File.expand_path('..', __FILE__)
4
4
  require 'helper'
5
+ require 'bigdecimal'
5
6
 
6
- class TestScoped < TestHBaseJRubyBase
7
+ class TestScoped < TestHBaseJRubyBase
7
8
  def test_table
8
9
  @hbase.table(TABLE) do |table|
9
10
  assert_equal TABLE, table.name
@@ -23,14 +24,12 @@ class TestScoped < TestHBaseJRubyBase
23
24
  end
24
25
 
25
26
  def test_put_then_get
26
- bignum = 123456789123456789123456789
27
27
  # Single record put
28
28
  assert_equal 1, @table.put('row1',
29
29
  'cf1:a' => 2,
30
30
  'cf1:b' => 'b',
31
31
  'cf1:c' => 6.28,
32
32
  'cf1:d' => false,
33
- 'cf1:e' => bignum + 1,
34
33
  'cf1:f' => :bol,
35
34
  'cf1:g' => BigDecimal.new("456.123"),
36
35
  'cf1:str1' => "Goodbye", 'cf1:str2' => "Cruel world")
@@ -39,7 +38,6 @@ class TestScoped < TestHBaseJRubyBase
39
38
  'cf1:b' => 'a',
40
39
  'cf1:c' => 3.14,
41
40
  'cf1:d' => true,
42
- 'cf1:e' => bignum,
43
41
  'cf1:f' => :sym,
44
42
  'cf1:g' => BigDecimal.new("123.456"),
45
43
  'cf1:str1' => "Hello", 'cf1:str2' => "World")
@@ -49,13 +47,16 @@ class TestScoped < TestHBaseJRubyBase
49
47
  'row3' => { 'cf1:a' => 4, 'cf1:b' => 'c', 'cf1:c' => 6.28 })
50
48
 
51
49
  # single-get (latest version)
50
+ result = @table.get('row1')
51
+ assert_equal result, result.each
52
+
53
+ assert_equal 'row1', @table.get('row1').rowkey
52
54
  assert_equal 'row1', @table.get('row1').rowkey
53
55
  assert_equal 1, @table.get('row1').fixnum('cf1:a')
54
56
  assert_equal 'a', @table.get('row1').string('cf1:b')
55
57
  assert_equal 'a', String.from_java_bytes(@table.get('row1').raw('cf1:b'))
56
58
  assert_equal 3.14, @table.get('row1').float('cf1:c')
57
59
  assert_equal true, @table.get('row1').boolean('cf1:d')
58
- assert_equal bignum, @table.get('row1').bignum('cf1:e')
59
60
  assert_equal :sym, @table.get('row1').symbol('cf1:f')
60
61
  assert_equal BigDecimal.new("123.456"), @table.get('row1').bigdecimal('cf1:g')
61
62
 
@@ -68,7 +69,6 @@ class TestScoped < TestHBaseJRubyBase
68
69
  assert_equal %w[a b], @table.get('row1').raws('cf1:b').values.map { |v| String.from_java_bytes v }
69
70
  assert_equal [3.14, 6.28], @table.get('row1').floats('cf1:c').values
70
71
  assert_equal [true, false], @table.get('row1').booleans('cf1:d').values
71
- assert_equal [bignum, bignum + 1], @table.get('row1').bignums('cf1:e').values
72
72
  assert_equal [:sym, :bol], @table.get('row1').symbols('cf1:f').values
73
73
  assert_equal [
74
74
  BigDecimal.new("123.456"),
@@ -77,9 +77,9 @@ class TestScoped < TestHBaseJRubyBase
77
77
  assert @table.get('row1').fixnums('cf1:a').keys.all? { |k| k.instance_of? Fixnum }
78
78
 
79
79
  # single-get-multi-col-multi=ver
80
- ret = @table.get('row1').strings(['cf1:str1', 'cf1:str2'])
81
- assert_equal ['Hello', 'World'], ret.map(&:values).map(&:first)
82
- assert_equal ['Goodbye', 'Cruel world'], ret.map(&:values).map(&:last)
80
+ rets = @table.get('row1').strings(['cf1:str1', 'cf1:str2'])
81
+ assert_equal ['Hello', 'World'], rets.map(&:values).map(&:first)
82
+ assert_equal ['Goodbye', 'Cruel world'], rets.map(&:values).map(&:last)
83
83
 
84
84
  # multi-get
85
85
  assert_equal %w[row1 row2 row3], @table.get(['row1', 'row2', 'row3']).map { |r| r.rowkey }
@@ -91,10 +91,45 @@ class TestScoped < TestHBaseJRubyBase
91
91
  assert_equal nil, @table.get('row1').symbol('cf1:xxx')
92
92
  assert_equal nil, @table.get('row1').integer('cf1:xxx')
93
93
 
94
+ # Unavailable columns (plural form)
95
+ assert_equal({}, @table.get('row1').strings('cf1:xxx'))
96
+ assert_equal({}, @table.get('row1').strings('cfx:xxx'))
97
+
94
98
  # Row not found
95
99
  assert_equal nil, @table.get('xxx')
96
100
  end
97
101
 
102
+ # Put added after a delete is overshadowed if its timestamp is older than than that of the tombstone
103
+ # https://issues.apache.org/jira/browse/HBASE-2847
104
+ def test_put_delete_put
105
+ pend("https://issues.apache.org/jira/browse/HBASE-2847") do
106
+ data = { 'cf1:pdp' => { 1250000000000 => 'A1' } }
107
+ @table.put :rowkey => data
108
+ assert_equal 'A1', @table.get(:rowkey).string('cf1:pdp')
109
+ @table.delete :rowkey
110
+ assert_nil @table.get(:rowkey)
111
+ @table.put :rowkey => data
112
+ assert_equal 'A1', @table.get(:rowkey).string('cf1:pdp')
113
+ end
114
+ end
115
+
116
+ def test_put_timestamp
117
+ rowkey = :test_put_timestamp
118
+ @table.put rowkey => {
119
+ 'cf1:b' => 'B1',
120
+ 'cf1:a' => {
121
+ 1250000000000 => 'A1',
122
+ 1260000000000 => 'A2',
123
+ 1270000000000 => 'A3',
124
+ },
125
+ }
126
+
127
+ assert_equal [1270000000000, 'A3'], @table.get(rowkey).strings('cf1:a').first
128
+ assert_equal 'A2', @table.get(rowkey).strings('cf1:a')[1260000000000]
129
+ assert_equal [1250000000000, 'A1'], @table.get(rowkey).strings('cf1:a').to_a.last
130
+ assert_equal ['B1'], @table.get(rowkey).strings('cf1:b').values
131
+ end
132
+
98
133
  def test_to_hash
99
134
  data = {
100
135
  'cf1:a' => 'Hello',
@@ -102,7 +137,7 @@ class TestScoped < TestHBaseJRubyBase
102
137
  'cf1:c' => 3.14,
103
138
  'cf2:d' => :world,
104
139
  'cf2:e' => false,
105
- 'cf3:f' => 1234567890123456789012345678901234567890,
140
+ 'cf3:f' => BigDecimal.new(1234567890123456789012345678901234567890),
106
141
  'cf3' => true
107
142
  }
108
143
  schema = {
@@ -111,7 +146,7 @@ class TestScoped < TestHBaseJRubyBase
111
146
  'cf1:c' => :float,
112
147
  HBase::ColumnKey(:cf2, :d) => :symbol,
113
148
  HBase::ColumnKey(:cf2, :e) => :boolean,
114
- HBase::ColumnKey(:cf3, :f) => :biginteger,
149
+ HBase::ColumnKey(:cf3, :f) => :bigdecimal,
115
150
  'cf3' => :boolean
116
151
  }
117
152
  @table.put('row1', data)