hbase-jruby 0.1.1-java → 0.1.2-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.
@@ -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)