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添加上这一列,加上如下代码就好了:

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 的区别

 

4. 参考链接

  (重点参考下这篇,分析的比较详细)

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