我正在用MRUnit测试我的mapper。我将键和值列表作为输入从测试类传递给映射器。 问题是:
String key=1234_abc;
ArrayList<KeyValue> list = new ArrayList<KeyValue>();
KeyValue k1 = new KeyValue(Bytes.toBytes(key),"cf".getBytes(), "Val1".getBytes(),Bytes.toBytes("abc.com"));
KeyValue k2 = new KeyValue(Bytes.toBytes(key), "cf".getBytes(), "Val2".getBytes(),Bytes.toBytes("165"));
Result result = new Result(list);
mapDriver.withInput(key, result);
问题是在结果对象中只保留第一个键值。其他的存储为null。
答案 0 :(得分:3)
问题是HBase以字典顺序存储列。看起来像Result(KeyValue [] kvs)或Result(List kvs)构造函数的顺序是相同的。
这是解决方案!
TreeSet<KeyValue> set = new TreeSet<KeyValue>(KeyValue.COMPARATOR);
byte[] row = Bytes.toBytes("row01");
byte[] cf = Bytes.toBytes("cf");
set.add(new KeyValue(row, cf, "cone".getBytes(), Bytes.toBytes("row01_cone_one")));
set.add(new KeyValue(row, cf, "ctwo".getBytes(), Bytes.toBytes("row01_ctwo_two")));
set.add(new KeyValue(row, cf, "cthree".getBytes(), Bytes.toBytes("row01_cthree_three")));
set.add(new KeyValue(row, cf, "cfour".getBytes(), Bytes.toBytes("row01_cfour_four")));
set.add(new KeyValue(row, cf, "cfive".getBytes(), Bytes.toBytes("row01_cfive_five")));
set.add(new KeyValue(row, cf, "csix".getBytes(), Bytes.toBytes("row01_csix_six")));
KeyValue[] kvs = new KeyValue[set.size()];
set.toArray(kvs);
Result result = new Result(kvs);
mapDriver.withInput(key, result);
希望这会有所帮助!
答案 1 :(得分:0)
我刚刚在这个问题上完成了大约6个小时的痛苦,最后发现了问题。它似乎是org.apache.hadoop.hbase.client.Result类中的一个错误,至少对于我正在使用的HBase版本(0.94.18)。
// The below line of code was failing for me when running locally under MRUnit
// but it seemed to succeed when running in production on my cluster.
// org.apache.hadoop.hbase.client.Result result passed in to this method.
Bytes.toString(result.getValue(Constants.CF1, Constants.REG_STATUS_FLAG_BYTES));
result.getValue()调用getColumnLatest(),其中包含对binarySearch()的调用。 binarySearch()方法似乎有问题,几乎总是返回错误的索引。 getColumnLatest()通过确保family和qualifier是匹配来双重检查它确实找到了正确的KeyValue。它们通常不是matchand,它返回null。
我最终重新实现了getValue()方法及其使用的3种方法,然后在单元测试中交换到功能正确的实现。可能有更好的方法来实现这一目标,但现在已经很晚了,这就是我想出来的(它确实解决了问题):
// Usage: Pass the Result into the newly created getValue() method, rather than
// calling getValue() on the Result object.
Bytes.toString(getValue(result, Constants.CF1, Constants.REG_STATUS_FLAG_BYTES));
// Reimplemented Methods:
private byte[] getValue(Result result, byte [] family, byte [] qualifier) {
KeyValue kv = getColumnLatest(result, family, qualifier);
if (kv == null) {
return null;
}
return kv.getValue();
}
private KeyValue getColumnLatest(Result result, byte[] family, byte[] qualifier) {
KeyValue [] kvs = result.raw(); // side effect possibly.
if (kvs == null || kvs.length == 0) {
return null;
}
//int pos = binarySearch(kvs, family, qualifier);
int pos = linearSearch(kvs, family, qualifier);
if (pos == -1) {
return null;
}
KeyValue kv = kvs[pos];
if (kv.matchingColumn(family, qualifier)) {
return kv;
}
return null;
}
private int linearSearch(final KeyValue [] kvs, final byte [] family,
final byte [] qualifier) {
int pos = -1;
int index = 0;
for (KeyValue kv : kvs) {
if (byteArraysEqual(family, kv.getFamily()) && byteArraysEqual(qualifier, kv.getQualifier())) {
pos = index;
break;
}
index++;
}
return pos;
}
private boolean byteArraysEqual(final byte[] ba1, final byte[] ba2) {
if (ba1 == null || ba2 == null) {
return false;
}
if (ba1.length != ba2.length) {
return false;
}
for (int i = 0; i < ba1.length; i++) {
if (ba1[i] != ba2[i]) {
return false;
}
}
return true;
}