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.
- data/CHANGELOG.md +17 -0
- data/README.md +126 -33
- data/hbase-jruby.gemspec +1 -0
- data/lib/hbase-jruby/admin.rb +1 -0
- data/lib/hbase-jruby/cell.rb +3 -11
- data/lib/hbase-jruby/result.rb +27 -51
- data/lib/hbase-jruby/scoped/aggregation.rb +47 -0
- data/lib/hbase-jruby/scoped.rb +51 -23
- data/lib/hbase-jruby/table.rb +72 -66
- data/lib/hbase-jruby/util.rb +12 -5
- data/lib/hbase-jruby/version.rb +1 -1
- data/lib/hbase-jruby.rb +1 -0
- data/test/helper.rb +1 -3
- data/test/test_aggregation.rb +40 -0
- data/test/test_cell.rb +0 -1
- data/test/test_hbase.rb +1 -1
- data/test/test_scoped.rb +45 -1
- data/test/test_table.rb +46 -11
- data/test/test_table_admin.rb +22 -0
- data/test/test_util.rb +2 -3
- metadata +37 -27
data/lib/hbase-jruby/scoped.rb
CHANGED
@@ -1,8 +1,13 @@
|
|
1
1
|
class HBase
|
2
|
-
# Scope of
|
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
|
-
|
18
|
-
|
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
|
128
|
-
|
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
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
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
|
|
data/lib/hbase-jruby/table.rb
CHANGED
@@ -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
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
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
|
-
|
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
|
data/lib/hbase-jruby/util.rb
CHANGED
@@ -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
|
-
|
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
|
data/lib/hbase-jruby/version.rb
CHANGED
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.
|
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
data/test/test_hbase.rb
CHANGED
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
|
-
|
81
|
-
assert_equal ['Hello', 'World'],
|
82
|
-
assert_equal ['Goodbye', 'Cruel world'],
|
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) => :
|
149
|
+
HBase::ColumnKey(:cf3, :f) => :bigdecimal,
|
115
150
|
'cf3' => :boolean
|
116
151
|
}
|
117
152
|
@table.put('row1', data)
|