1. 情况说明
在使用hbase-endpoint依赖中的AggregationClient类,对hbase执行sum操作时,当scan不设置 filter时可以正常得到 sum的结果,但是设置filter之后,sum的结果就为 null。
1.1 不设置filter时的执行结果
不设置filter时的代码如下:
String tableName = "hbaseTableName";
String cf = "f";
String col_ORG_ID = "ORG_ID"; // 在hbase列名是 ORG_ID
Scan scan = new Scan();
scan.addFamily(cf.getBytes());
scan.addColumn(cf.getBytes(), col_ORG_ID.getBytes());
AggregationClient ac = new AggregationClient(config.getHbaseConfiguration());
DoubleColumnInterpreter columnInterpreter = new DoubleColumnInterpreter();
Double sum = ac.sum(TableName.valueOf(tableName), columnInterpreter, scan);
log.info("sum result [{}]", sum);
上述代码会对hbase表里的ORG_ID进行求sum操作。执行sum的结果是
sum result [18.8]
可以对某列执行求sum操作。
1.2 设置filter后执行结果为 null
这是filter的代码如下:
String tableName = "hbaseTableName";
String cf = "f";
String col_ORG_ID = "ORG_ID"; // 在hbase列名是 ORG_ID
Scan scan = new Scan();
// 为scan设置filterList
List<Filter> filters = new ArrayList<>();
SingleColumnValueFilter scvf = new SingleColumnValueFilter(Bytes.toBytes(cf), Bytes.toBytes(col_ORG_ID), CompareOperator.EQUAL, Bytes.toBytes("100.05"));
filters.add(scvf);
scvf.setFilterIfMissing(true);
FilterList filterList = new FilterList(FilterList.Operator.MUST_PASS_ALL,filters);
scan.setFilter(filterList);
//对列进行求sum操作
scan.addFamily(cf.getBytes());
scan.addColumn(cf.getBytes(), col_ORG_ID.getBytes());
AggregationClient ac = new AggregationClient(config.getHbaseConfiguration());
DoubleColumnInterpreter columnInterpreter = new DoubleColumnInterpreter();
Double sum = ac.sum(TableName.valueOf(tableName), columnInterpreter, scan);
log.info("sum result [{}]", sum);
此时输出结果是: sum result [null]
为什么添加Filter后,再执行sum的结果就为null了呢。
2. 问题分析
为什么添加Filter后,再执行sum的结果就为null了呢。
经过分析,在SingleColumnValueFilter和Filter一起使用时,创建一个SingleColumnValueFilter对象,就需要在scan添加上这一列,加上如下代码就好了,因为 只有当COLUMNS中包含SingleColumnValueFilter提到的字段时, 该SingleColumnValueFilter才有效的 。(参考这篇文章的讲解):
scan.addColumn(cf.getBytes(), col_ORG_ID.getBytes());
2.2 注意事项
在创建SingleColumnValueFilter对象时,需要注意设置列的值时,注意区分数值型(Double)和字符串类型(String)的区别。
如果为这一列的值是String类型,使用下面的赋值方式:
new SingleColumnValueFilter(Bytes.toBytes(cf), Bytes.toBytes("TRX_DISC_AT" ), CompareOperator.EQUAL, Bytes.toBytes("50.3"));
如果这一列的值类型是Double,则需要使用下面的创建方法:
要将列值转化为对应的数值类型,例如Double类型:Double.valueOf("50.3")
new SingleColumnValueFilter(Bytes.toBytes(cf), Bytes.toBytes("TRX_DISC_AT" ), CompareOperator.EQUAL, Bytes.toBytes(Double.valueOf("50.3")));
这里还有一个坑,就是要添加的SingleColumnValueFilter列(例如column_02)如果是数值型,则在最终的计算结果中,也会将这一列也统计进去,即最后的结果是sum(column_01) + sum(column_02)。这时候就需要将最终的计算结果再减去这一列sum(column_02)的值。
如果在求sum时,不想统计sum(过滤列),则可以使用 SingleColumnValueExcludeFilter ,因为SingleColumnValueExcludeFilter会将该字段排除,仅仅用于过滤使用。
还是要多了解下hbase中的数据类型,例如string和数值型double的使用区别的
3. 结论
3.1 SingleColumnValueFilter列添加到scan对象中
使用SingleColumnValueFilter时,只有当scan的COLUMNS中包含SingleColumnValueFilter提到的字段时, 该SingleColumnValueFilter才有效的。[参考这里 => SingleColumnValueFilter should be able to find the column value even when it's not specifically added as input on the scan.]
即把col这一列添加到scan对象中去
//为scan添加列
scan.addColumn(cf.getBytes(), col.getBytes());
3.2 使用替代的SingleColumnValueExcludeFilter类
使用SingleColumnValueExcludeFilter这时只会返回除开AREA_ID和TIME_ID的字段, 但记录数是对的。这样就不用再手动把过滤列添加到scan对象了
3.3 的区别
请参考这一篇文章 SingleColumnValueFilter 和 SingleColumnValueExcludeFilter的区别
4. 参考链接
https://blog.csdn.net/zhangjianying/article/details/8628249 (重点参考下这篇,分析的比较详细)
http://www.bubuko.com/infodetail-542553.html
https://issues.apache.org/jira/browse/HBASE-2198
https://issues.apache.org/jira/browse/HBASE-2211
http://www.bubuko.com/infodetail-542553.html
https://bbs.csdn.net/topics/390223353