HBase - Filter - 过滤器的介绍以及使用

1 过滤器
HBase 的基本 API,包括增、删、改、查等。
增、删都是相对简单的操作,与传统的 RDBMS 相比,这里的查询操作略显苍白,只能根据特性的行键进行查询(Get)或者根据行键的范围来查询(Scan)。
HBase 不仅提供了这些简单的查询,而且提供了更加高级的过滤器(Filter)来查询。

1.1 过滤器的两类参数
过滤器可以根据列族、列、版本等更多的条件来对数据进行过滤,基于 HBase 本身提供的三维有序(行键,列,版本有序),这些过滤器可以高效地完成查询过滤的任务,带有过滤器条件的 RPC 查询请求会把过滤器分发到各个 RegionServer(这是一个服务端过滤器),这样也可以降低网络传输的压力。
使用过滤器至少需要两类参数:

1.1.1 一类是抽象的操作符
HBase 提供了枚举类型的变量来表示这些抽象的操作符:
LESS
LESS_OR_EQUAL
EQUAL
NOT_EQUAL
GREATER_OR_EQUAL
GREATER
NO_OP

1.1.2 另一类是比较器
代表具体的逻辑,例如字节级的比较,字符串级的比较等。

1.2 比较器
比较器作为过滤器的核心组成之一,用于处理具体的比较逻辑,例如字节级的比较,字符串级的比较等。

1.2.1 RegexStringComparator
支持正则表达式的值比较

Scan scan = new Scan();
RegexStringComparator comp = new RegexStringComparator("you."); // 以 you 开头的字符串
SingleColumnValueFilter filter = new SingleColumnValueFilter(Bytes.toBytes("family"), Bytes.toBytes("qualifier"), CompareOp.EQUAL, comp);
scan.setFilter(filter);

1.2.2 SubStringComparator
用于监测一个子串是否存在于值中,并且不区分大小写。

Scan scan = new Scan();
SubstringComparator comp = new SubstringComparator("1129"); // 查找包含 1129 的字符串
SingleColumnValueFilter filter = new SingleColumnValueFilter(Bytes.toBytes("family"), Bytes.toBytes("qualifier"), CompareOp.EQUAL, comp);
scan.setFilter(filter);

1.2.3 BinaryPrefixComparator
前缀二进制比较器。与二进制比较器不同的是,只比较前缀是否相同。

Scan scan = new Scan();
BinaryPrefixComparator comp = new BinaryPrefixComparator(Bytes.toBytes("yting")); //
SingleColumnValueFilter filter = new SingleColumnValueFilter(Bytes.toBytes("family"), Bytes.toBytes("qualifier"),  CompareOp.EQUAL, comp);
scan.setFilter(filter);

1.2.4 BinaryComparator
二进制比较器,用于按字典顺序比较 Byte 数据值。
Scan scan = new Scan();
BinaryComparator comp = new BinaryComparator(Bytes.toBytes("xmei")); //
ValueFilter filter = new ValueFilter(CompareOp.EQUAL, comp);
scan.setFilter(filter);

1.3 列值过滤器

1.3.1 SingleColumnValueFilter
SingleColumnValueFilter 用于测试值的情况(相等,不等,范围 、、、)

下面一个检测列族 family 下的列 qualifier 的列值和字符串 "my-value" 相等的部分示例代码 : 
Scan scan = new Scan();
SingleColumnValueFilter filter = new SingleColumnValueFilter(Bytes.toBytes("family"), Bytes.toBytes("qualifier"), CompareOp.EQUAL, Bytes.toBytes("my-value"));
scan.setFilter(filter);

1.3.2 SingleColumnValueExcludeFilter
跟 SingleColumnValueFilter 功能一样,只是不查询出该列的值。

下面的代码就不会查询出 family 列族下 qualifier 列的值(列都不会查出来)
Scan scan = new Scan();
SingleColumnValueExcludeFilter filter = new SingleColumnValueExcludeFilter(Bytes.toBytes("family"), Bytes.toBytes("qualifier"), CompareOp.EQUAL, Bytes.toBytes("my-value"));
scan.setFilter(filter);


1.4 键值元数据过滤器
HBase 采用 "键值对" 保存内部数据,键值元数据过滤器评估一行的 "键" 是否保存在(如 ColumnFamily:Column qualifiers)。

1.4.1 FamilyFilter
用于过滤列族(通常在 Scan 过程中通过设定某些列族来实现该功能,而不是直接使用该过滤器)。

Scan scan = new Scan();
FamilyFilter filter = new FamilyFilter(CompareOp.EQUAL, new BinaryComparator(Bytes.toBytes("my-family"))); // 列族为 my-family
scan.setFilter(filter);

1.4.2 QualifierFilter
用于列名(Qualifier)过滤。

Scan scan = new Scan();
QualifierFilter filter = new QualifierFilter(CompareOp.EQUAL, new BinaryComparator(Bytes.toBytes("my-column"))); // 列名为 my-column
scan.setFilter(filter);

1.4.3 ColumnPrefixFilter 
用于列名(Qualifier)前缀过滤,即包含某个前缀的所有列名。

Scan scan = new Scan();
  ColumnPrefixFilter filter = new ColumnPrefixFilter(Bytes.toBytes("my-prefix")); // 前缀为 my-prefix
  scan.setFilter(filter);

1.4.4 MultipleColumnPrefixFilter
MultipleColumnPrefixFilter 与 ColumnPrefixFilter  的行为类似,但可以指定多个列名(Qualifier)前缀。

Scan scan = new Scan();
byte[][] prefixes = new byte[][]{Bytes.toBytes("my-prefix-1"), Bytes.toBytes("my-prefix-2")};
MultipleColumnPrefixFilter filter = new MultipleColumnPrefixFilter(prefixes); // 不解释,你懂的 、、、
scan.setFilter(filter);

1..4.5 ColumnRangeFilter
该过滤器可以进行高效的列名内部扫描。(为何是高效呢???因为列名是已经按字典排序好的)HBase-0.9.2 版本引入该功能。

Scan scan = new Scan();
boolean minColumnInclusive = true;
boolean maxColumnInclusive = true;
ColumnRangeFilter filter = new ColumnRangeFilter(Bytes.toBytes("minColumn"), minColumnInclusive, Bytes.toBytes("maxColumn"), maxColumnInclusive);
scan.setFilter(filter);

1.6 DependentColumnFilter 
该过滤器尝试找到该列所在的每一行,并返回该行具有相同时间戳的全部键值对。

Scan scan = new Scan();
DependentColumnFilter filter = new DependentColumnFilter(Bytes.toBytes("family"), Bytes.toBytes("qualifier"));
scan.setFilter(filter);

1.5 行键过滤器

1.5.1 RowFilter 
行键过滤器,一般来讲,执行 Scan 使用 startRow/stopRow 方式比较好,而 RowFilter 过滤器也可以完成对某一行的过滤。

Scan scan = new Scan();
RowFilter filter = new RowFilter(CompareOp.EQUAL, new BinaryComparator(Bytes.toBytes("my-row-1")));
scan.setFilter(filter);

1.5.2 RandomRowFilter
该过滤器是随机选择一行的过滤器。参数 chance 是一个浮点值,介于 0.1 和 1.0 之间。

Scan scan = new Scan();
float chance = 0.5f;
RandomRowFilter filter = new RandomRowFilter(chance); // change 在 0.1 ~ 1.0 之间的浮点值
scan.setFilter(filter);

1.6 功能过滤器

1.6.1 PageFilter
用于按行分页。

long pageSize = 10;
int totalRowsCount = 0;
PageFilter filter = new PageFilter(pageSize);
byte[] lastRow = null;
while(true) {
 Scan scan = new Scan();
 scan.setFilter(filter);
 if(lastRow != null) {
  byte[] postfix = Bytes.toBytes("postfix");
  byte[] startRow = Bytes.add(lastRow, postfix);
  scan.setStartRow(startRow);
  System.out.println("start row : " + Bytes.toString(startRow));
 }
 
 ResultScanner scanner = _hTable.getScanner(scan);
 int localRowsCount = 0;
 for(Result result : scanner) {
  System.out.println(localRowsCount++ + " : " + result);
  totalRowsCount++;
  lastRow = result.getRow(); // ResultScanner 的结果集是排序好的,这样就可以取到最后一个 row 了
 }
 scanner.close();
 
 if(localRowsCount == 0) break;
}
System.out.println("total rows is : " + totalRowsCount);

1.6.2 FirstKeyOnlyFilter
该过滤器只查询每个行键的第一个键值对,在统计计数的时候提高效率。(HBase-Coprocessor 做 RowCount 的时候可以提高效率)。
Scan scan = new Scan();
FirstKeyOnlyFilter filter = new FirstKeyOnlyFilter(); // 只查询每个行键的第一个键值对
scan.setFilter(filter);

1.6.3 KeyOnlyFilter
Scan scan = new Scan();
KeyOnlyFilter filter = new KeyOnlyFilter(); // 只查询每行键值对中有 "键" 元数据信息,不显示值,可以提升扫描的效率
scan.setFilter(filter);

1.6.4 InclusiveStopFilter
常规的 Scan 包含 start-row 但不包含 stop-row,如果使用该过滤器便可以包含 stop-row。

Scan scan = new Scan();
InclusiveStopFilter filter = new InclusiveStopFilter(Bytes.toBytes("stopRowKey"));
scan.setFilter(filter);

1.6.5 ColumnPaginationFilter
按列分页过滤器,针对列数量很多的情况使用。

Scan scan = new Scan();
int limit = 0;
int columnOffset = 0;
ColumnPaginationFilter filter = new ColumnPaginationFilter(limit, columnOffset);
scan.setFilter(filter);

2 自定义过滤器
做法 : 继承 FilterBase,然后打成 jar 放到 $HBASE_HOEM/lib 目录下去(注意:需要重启 HBase 集群)

HBase为筛选数据提供了一组过滤器,通过这个过滤器可以在HBase中的数据的多个维度(行,列,数据版本)上进行对数据的筛选操作,也就是说过滤器最终能够筛选的数据能够细化到具体的一个存储单元格上(由行键,列明,时间戳定位)。通常来说,通过行键,值来筛选数据的应用场景较多。


1. RowFilter:筛选出匹配的所有的行,对于这个过滤器的应用场景,是非常直观的:使用BinaryComparator可以筛选出具有某个行键的行,或者通过改变比较运算符(下面的例子中是CompareFilter.CompareOp.EQUAL)来筛选出符合某一条件的多条数据,以下就是筛选出行键为row1的一行数据:

[java] view plain copy
Filter rf = new RowFilter(CompareFilter.CompareOp.EQUAL, new BinaryComparator(Bytes.toBytes("row1"))); // OK 筛选出匹配的所有的行  

2. PrefixFilter:筛选出具有特定前缀的行键的数据。这个过滤器所实现的功能其实也可以由RowFilter结合RegexComparator来实现,不过这里提供了一种简便的使用方法,以下过滤器就是筛选出行键以row为前缀的所有的行:
[java] view plain copy
Filter pf = new PrefixFilter(Bytes.toBytes("row")); // OK  筛选匹配行键的前缀成功的行  

3. KeyOnlyFilter:这个过滤器唯一的功能就是只返回每行的行键,值全部为空,这对于只关注于行键的应用场景来说非常合适,这样忽略掉其值就可以减少传递到客户端的数据量,能起到一定的优化作用:
[java] view plain copy
Filter kof = new KeyOnlyFilter(); // OK 返回所有的行,但值全是空  

4. RandomRowFilter:从名字上就可以看出其大概的用法,本过滤器的作用就是按照一定的几率(<=0会过滤掉所有的行,>=1会包含所有的行)来返回随机的结果集,对于同样的数据集,多次使用同一个RandomRowFilter会返回不通的结果集,对于需要随机抽取一部分数据的应用场景,可以使用此过滤器:
[java] view plain copy
Filter rrf = new RandomRowFilter((float) 0.8); // OK 随机选出一部分的行  

5. InclusiveStopFilter:扫描的时候,我们可以设置一个开始行键和一个终止行键,默认情况下,这个行键的返回是前闭后开区间,即包含起始行,但不包含终止行,如果我们想要同时包含起始行和终止行,那么我们可以使用此过滤器:
[java] view plain copy
Filter isf = new InclusiveStopFilter(Bytes.toBytes("row1")); // OK 包含了扫描的上限在结果之内  

6. FirstKeyOnlyFilter:如果你只想返回的结果集中只包含第一列的数据,那么这个过滤器能够满足你的要求。它在找到每行的第一列之后会停止扫描,从而使扫描的性能也得到了一定的提升:
[java] view plain copy
Filter fkof = new FirstKeyOnlyFilter(); // OK 筛选出第一个每个第一个单元格  

7. ColumnPrefixFilter:顾名思义,它是按照列名的前缀来筛选单元格的,如果我们想要对返回的列的前缀加以限制的话,可以使用这个过滤器:
[java] view plain copy
Filter cpf = new ColumnPrefixFilter(Bytes.toBytes("qual1")); // OK 筛选出前缀匹配的列  

8. ValueFilter:按照具体的值来筛选单元格的过滤器,这会把一行中值不能满足的单元格过滤掉,如下面的构造器,对于每一行的一个列,如果其对应的值不包含ROW2_QUAL1,那么这个列就不会返回给客户端:
[java] view plain copy
Filter vf = new ValueFilter(CompareFilter.CompareOp.EQUAL, new SubstringComparator("ROW2_QUAL1")); // OK 筛选某个(值的条件满足的)特定的单元格  

9. ColumnCountGetFilter:这个过滤器来返回每行最多返回多少列,并在遇到一行的列数超过我们所设置的限制值的时候,结束扫描操作:
[java] view plain copy
Filter ccf = new ColumnCountGetFilter(2); // OK 如果突然发现一行中的列数超过设定的最大值时,整个扫描操作会停止  

10. SingleColumnValueFilter:用一列的值决定这一行的数据是否被过滤。在它的具体对象上,可以调用setFilterIfMissing(true)或者setFilterIfMissing(false),默认的值是false,其作用是,对于咱们要使用作为条件的列,如果这一列本身就不存在,那么如果为true,这样的行将会被过滤掉,如果为false,这样的行会包含在结果集中。

[java] view plain copy
SingleColumnValueFilter scvf = new SingleColumnValueFilter(  
        Bytes.toBytes("colfam1"),   
        Bytes.toBytes("qual2"),   
        CompareFilter.CompareOp.NOT_EQUAL,   
        new SubstringComparator("BOGUS"));  
scvf.setFilterIfMissing(false);  
scvf.setLatestVersionOnly(true); // OK  

11. SingleColumnValueExcludeFilter:这个与10种的过滤器唯一的区别就是,作为筛选条件的列的不会包含在返回的结果中。
12. SkipFilter:这是一种附加过滤器,其与ValueFilter结合使用,如果发现一行中的某一列不符合条件,那么整行就会被过滤掉:

[java] view plain copy
Filter skf = new SkipFilter(vf); // OK 发现某一行中的一列需要过滤时,整个行就会被过滤掉  

13. WhileMatchFilter:这个过滤器的应用场景也很简单,如果你想要在遇到某种条件数据之前的数据时,就可以使用这个过滤器;当遇到不符合设定条件的数据的时候,整个扫描也就结束了:
[java] view plain copy
Filter wmf = new WhileMatchFilter(rf); // OK 类似于Python itertools中的takewhile  

14. FilterList:用于综合使用多个过滤器。其有两种关系:FilterList.Operator.MUST_PASS_ONE和FilterList.Operator.MUST_PASS_ALL,默认的是FilterList.Operator.MUST_PASS_ALL,顾名思义,它们分别是AND和OR的关系,并且FilterList可以嵌套使用FilterList,使我们能够表达更多的需求:

[java] view plain copy
List<Filter> filters = new ArrayList<Filter>();  
filters.add(rf);  
filters.add(vf);  
FilterList fl = new FilterList(FilterList.Operator.MUST_PASS_ALL, filters); // OK 综合使用多个过滤器, AND 和 OR 两种关系  

以上,是对于HBase内置的过滤器的部分总结,以下代码是数据写入代码:

[java] view plain copy
package com.reyun.hbase;  
  
import java.io.IOException;  
import org.apache.hadoop.conf.Configuration;  
import org.apache.hadoop.hbase.HBaseConfiguration;  
import org.apache.hadoop.hbase.client.HTable;  
import org.apache.hadoop.hbase.client.Put;  
import org.apache.hadoop.hbase.util.Bytes;  
  
public class HBaseDataFeeding {  
    private final static byte[] ROW1 = Bytes.toBytes("row1");  
    private final static byte[] ROW2 = Bytes.toBytes("row2");  
    private final static byte[] COLFAM1 = Bytes.toBytes("colfam1");  
    private final static byte[] COLFAM2 = Bytes.toBytes("colfam2");  
    private final static byte[] QUAL1 = Bytes.toBytes("qual1");  
    private final static byte[] QUAL2 = Bytes.toBytes("qual2");  
      
      
    public static void main(String[] args) throws IOException {  
        Configuration conf = HBaseConfiguration.create();  
        HTable table = new HTable(conf, "testtable");  
        table.setAutoFlushTo(false);  
        Put put_row1 = new Put(ROW1);  
        put_row1.add(COLFAM1, QUAL1, Bytes.toBytes("ROW1_QUAL1_VAL"));  
        put_row1.add(COLFAM1, QUAL2, Bytes.toBytes("ROW1_QUAL2_VAL"));  
          
        Put put_row2 = new Put(ROW2);  
        put_row2.add(COLFAM1, QUAL1, Bytes.toBytes("ROW2_QUAL1_VAL"));  
        put_row2.add(COLFAM1, QUAL2, Bytes.toBytes("ROW2_QUAL2_VAL"));  
          
        try{  
            table.put(put_row1);  
            table.put(put_row2);  
        }finally{  
            table.close();  
        }  
    }  
  
}  

以下是过滤器测试代码,可以通过修改代码,更换过滤器来看到具体的效果:

[java] view plain copy
package com.reyun.hbase;  
  
import java.io.IOException;  
import java.util.ArrayList;  
import java.util.List;  
  
import org.apache.hadoop.conf.Configuration;  
import org.apache.hadoop.hbase.Cell;  
import org.apache.hadoop.hbase.CellUtil;  
import org.apache.hadoop.hbase.HBaseConfiguration;  
import org.apache.hadoop.hbase.client.HTable;  
import org.apache.hadoop.hbase.client.Result;  
import org.apache.hadoop.hbase.client.ResultScanner;  
import org.apache.hadoop.hbase.client.Scan;  
import org.apache.hadoop.hbase.filter.BinaryComparator;  
import org.apache.hadoop.hbase.filter.ColumnCountGetFilter;  
import org.apache.hadoop.hbase.filter.ColumnPrefixFilter;  
import org.apache.hadoop.hbase.filter.CompareFilter;  
import org.apache.hadoop.hbase.filter.Filter;  
import org.apache.hadoop.hbase.filter.FilterList;  
import org.apache.hadoop.hbase.filter.FirstKeyOnlyFilter;  
import org.apache.hadoop.hbase.filter.InclusiveStopFilter;  
import org.apache.hadoop.hbase.filter.KeyOnlyFilter;  
import org.apache.hadoop.hbase.filter.PageFilter;  
import org.apache.hadoop.hbase.filter.PrefixFilter;  
import org.apache.hadoop.hbase.filter.RandomRowFilter;  
import org.apache.hadoop.hbase.filter.RowFilter;  
import org.apache.hadoop.hbase.filter.SkipFilter;  
import org.apache.hadoop.hbase.filter.ValueFilter;  
import org.apache.hadoop.hbase.filter.SingleColumnValueFilter;  
import org.apache.hadoop.hbase.filter.SubstringComparator;  
import org.apache.hadoop.hbase.filter.WhileMatchFilter;  
import org.apache.hadoop.hbase.util.Bytes;  
  
public class HBaseScannerTest {  
  
    public static void main(String[] args) throws IOException, IllegalAccessException {  
        Configuration conf = HBaseConfiguration.create();  
        HTable table = new HTable(conf, "testtable");  
        table.setAutoFlushTo(false);  
          
        Scan scan1 = new Scan();  
        SingleColumnValueFilter scvf = new SingleColumnValueFilter(  
                Bytes.toBytes("colfam1"),   
                Bytes.toBytes("qual2"),   
                CompareFilter.CompareOp.NOT_EQUAL,   
                new SubstringComparator("BOGUS"));  
        scvf.setFilterIfMissing(false);  
        scvf.setLatestVersionOnly(true); // OK  
        Filter ccf = new ColumnCountGetFilter(2); // OK 如果突然发现一行中的列数超过设定的最大值时,整个扫描操作会停止  
        Filter vf = new ValueFilter(CompareFilter.CompareOp.EQUAL, new SubstringComparator("ROW2_QUAL1")); // OK 筛选某个(值的条件满足的)特定的单元格  
        Filter cpf = new ColumnPrefixFilter(Bytes.toBytes("qual2")); // OK 筛选出前缀匹配的列  
        Filter fkof = new FirstKeyOnlyFilter(); // OK 筛选出第一个每个第一个单元格  
        Filter isf = new InclusiveStopFilter(Bytes.toBytes("row1")); // OK 包含了扫描的上限在结果之内  
        Filter rrf = new RandomRowFilter((float) 0.8); // OK 随机选出一部分的行  
        Filter kof = new KeyOnlyFilter(); // OK 返回所有的行,但值全是空  
        Filter pf = new PrefixFilter(Bytes.toBytes("row")); // OK  筛选匹配行键的前缀成功的行  
        Filter rf = new RowFilter(CompareFilter.CompareOp.NOT_EQUAL, new BinaryComparator(Bytes.toBytes("row1"))); // OK 筛选出匹配的所有的行  
        Filter wmf = new WhileMatchFilter(rf); // OK 类似于Python itertools中的takewhile  
        Filter skf = new SkipFilter(vf); // OK 发现某一行中的一列需要过滤时,整个行就会被过滤掉  
          
        List<Filter> filters = new ArrayList<Filter>();  
        filters.add(rf);  
        filters.add(vf);  
        FilterList fl = new FilterList(FilterList.Operator.MUST_PASS_ALL, filters); // OK 综合使用多个过滤器, AND 和 OR 两种关系  
          
        scan1.  
        setStartRow(Bytes.toBytes("row1")).  
        setStopRow(Bytes.toBytes("row3")).  
        setFilter(scvf);   
        ResultScanner scanner1 = table.getScanner(scan1);  
          
        for(Result res : scanner1){  
            for(Cell cell : res.rawCells()){  
                System.out.println("KV: " + cell + ", Value: " + Bytes.toString(CellUtil.cloneValue(cell)));  
            }  
            System.out.println("------------------------------------------------------------");  
        }  
          
        scanner1.close();  
        table.close();  
    }  
   
}  

引言 -- 参数基础
有两个参数类在各类Filter中经常出现,统一介绍下:
(1)比较运算符 CompareFilter.CompareOp
比较运算符用于定义比较关系,可以有以下几类值供选择:
EQUAL                                  相等
GREATER                              大于
GREATER_OR_EQUAL           大于等于
LESS                                      小于
LESS_OR_EQUAL                  小于等于
NOT_EQUAL                        不等于

(2)比较器  ByteArrayComparable
通过比较器可以实现多样化目标匹配效果,比较器有以下子类可以使用:
BinaryComparator               匹配完整字节数组 
BinaryPrefixComparator     匹配字节数组前缀 
BitComparator
NullComparator
RegexStringComparator    正则表达式匹配
SubstringComparator        子串匹配

1. 结构(Structural)过滤器--FilterList
FilterList 代表一个过滤器链,它可以包含一组即将应用于目标数据集的过滤器,过滤器间具有“与” FilterList.Operator.MUST_PASS_ALL 和“或” FilterList.Operator.MUST_PASS_ONE 关系。

官网实例代码,两个“或”关系的过滤器的写法:
FilterList list = new FilterList(FilterList.Operator.MUST_PASS_ONE);   //数据只要满足一组过滤器中的一个就可以
SingleColumnValueFilter filter1 = new SingleColumnValueFilter(
cf,
column,
CompareOp.EQUAL,
Bytes.toBytes("my value")
);
list.add(filter1);
SingleColumnValueFilter filter2 = new SingleColumnValueFilter(
cf,
column,
CompareOp.EQUAL,
Bytes.toBytes("my other value")
);
list.add(filter2);
Scan scan = new Scan();
scan.setFilter(list);
2. 列值过滤器--SingleColumnValueFilter
SingleColumnValueFilter 用于测试列值相等 (CompareOp.EQUAL ), 不等 (CompareOp.NOT_EQUAL),或单侧范围 (e.g., CompareOp.GREATER)。
构造函数:
(1)比较的关键字是一个字符数组
SingleColumnValueFilter(byte[] family, byte[] qualifier, CompareFilter.CompareOp compareOp, byte[] value)
(2)比较的关键字是一个比较器(比较器下一小节做介绍)
SingleColumnValueFilter(byte[] family, byte[] qualifier, CompareFilter.CompareOp compareOp, ByteArrayComparable comparator)
2.1.第一种构造函数情况 -- 比较的关键字是字符数组
官网示例代码,检查列值和字符串'my value' 相等:
SingleColumnValueFilter filter = new SingleColumnValueFilter(
cf,
column,
CompareOp.EQUAL,
Bytes.toBytes("my value")
);
scan.setFilter(filter);

个人实测代码:
        HTable table = HBaseDAO.getHTable("147patents");
        FilterList filterList = new FilterList(FilterList.Operator.MUST_PASS_ALL);  
        SingleColumnValueFilter filter = new SingleColumnValueFilter(
                Bytes.toBytes("patentinfo"),
                Bytes.toBytes("CREATE_TIME"),
                CompareOp.EQUAL,
                Bytes.toBytes("2013-06-08")
                );
        filterList.addFilter(filter);
        Scan scan = new Scan();
        scan.setFilter(filterList);
        ResultScanner rs = table.getScanner(scan);
        for (Result r : rs) {
            System.out.println("Scan: " + r);
        }
        table.close();  
注意:还是大写问题,HBase的列名必须大写!

2.2.第二种构造函数情况 -- 比较的关键字是比较器ByteArrayComparable
该章节主要是针对SingleColumnValueFilter的第二种构造函数使用情况做了一些举例:
(1)支持值比较的正则表达式 -- RegexStringComparator
官网示例代码:
RegexStringComparator comp = new RegexStringComparator("my.");   //任意以my打头的值
SingleColumnValueFilter filter = new SingleColumnValueFilter(
cf,
column,
CompareOp.EQUAL,
comp
);
scan.setFilter(filter);

个人实测代码:
        HTable table = HBaseDAO.getHTable("147patents");
        FilterList filterList = new FilterList(FilterList.Operator.MUST_PASS_ALL);
        
        RegexStringComparator comp = new RegexStringComparator("2013-06-1.");
        
        SingleColumnValueFilter filter = new SingleColumnValueFilter(
                Bytes.toBytes("patentinfo"),
                Bytes.toBytes("CREATE_TIME"),
                CompareOp.EQUAL,
                comp
                );
        filterList.addFilter(filter);
        Scan scan = new Scan();
        scan.setFilter(filterList);
        ResultScanner rs = table.getScanner(scan);
        for (Result r : rs) {
            System.out.println("Scan: " + r);
        }
        table.close();  

(2)检测一个子串是否存在于值中(大小写不敏感) -- SubstringComparator
官网示例代码:
SubstringComparator comp = new SubstringComparator("y val");   // looking for 'my value'
SingleColumnValueFilter filter = new SingleColumnValueFilter(
cf,
column,
CompareOp.EQUAL,
comp
);
scan.setFilter(filter);

个人实测代码:
        HTable table = HBaseDAO.getHTable("147patents");
        FilterList filterList = new FilterList(FilterList.Operator.MUST_PASS_ALL);
        
//        RegexStringComparator comp = new RegexStringComparator("2013-06-1.");
        SubstringComparator comp = new SubstringComparator("2013-06-1");
        
        SingleColumnValueFilter filter = new SingleColumnValueFilter(
                Bytes.toBytes("patentinfo"),
                Bytes.toBytes("CREATE_TIME"),
                CompareOp.EQUAL,
                comp
                );
        filterList.addFilter(filter);
        Scan scan = new Scan();
        scan.setFilter(filterList);
        ResultScanner rs = table.getScanner(scan);
        for (Result r : rs) {
            System.out.println("Scan: " + r);
        }
        table.close(); 

(3)BinaryComparator
二进制比较器,用得较少,有需要请自行查阅官网:http://hbase.apache.org/apidocs/org/apache/hadoop/hbase/filter/BinaryComparator.html

(4)BinaryPrefixComparator
二进制前缀比较器,用得较少,有需要请自行查阅官网:http://hbase.apache.org/apidocs/org/apache/hadoop/hbase/filter/BinaryPrefixComparator.html
3. 键值元数据
由于HBase 采用键值对保存内部数据,键值元数据过滤器评估一行的键(ColumnFamily:Qualifiers)是否存在 , 对应前节所述值的情况。
3.1. 基于列族过滤数据的FamilyFilter
构造函数:
FamilyFilter(CompareFilter.CompareOp familyCompareOp, ByteArrayComparable familyComparator)

个人实测代码:
        HTable table = HBaseDAO.getHTable("147patents");
        /**
         * FamilyFilter构造函数中第二个参数是ByteArrayComparable类型
         * ByteArrayComparable类参见“引言-参数基础”章节
         * 下面仅以最可能用到的BinaryComparator、BinaryPrefixComparator举例:
         */
        FamilyFilter ff = new FamilyFilter(
                CompareFilter.CompareOp.EQUAL , 
                new BinaryComparator(Bytes.toBytes("pat"))   //表中不存在pat列族,过滤结果为空
                );
        FamilyFilter ff1 = new FamilyFilter(
                CompareFilter.CompareOp.EQUAL , 
                new BinaryPrefixComparator(Bytes.toBytes("pat"))   //表中存在以pat打头的列族patentinfo,过滤结果为该列族所有行
                );
        Scan scan = new Scan();
        scan.setFilter(ff1);
        ResultScanner rs = table.getScanner(scan); 

注意:
如果希望查找的是一个已知的列族,则使用 scan.addFamily(family)  比使用过滤器效率更高;
由于目前HBase对多列族支持不完善,所以该过滤器目前用途不大。
3.2. 基于限定符Qualifier(列)过滤数据的QualifierFilter
构造函数:
QualifierFilter(CompareFilter.CompareOp op, ByteArrayComparable qualifierComparator)

个人实测代码:
        HTable table = HBaseDAO.getHTable("147patents");
        /**
         * QualifierFilter构造函数中第二个参数是ByteArrayComparable类型
         * ByteArrayComparable类有以下子类可以使用:
         * *******************************************
         * BinaryComparator  匹配完整字节数组, 
         * BinaryPrefixComparator  匹配开始的部分字节数组, 
         * BitComparator, 
         * NullComparator, 
         * RegexStringComparator,   正则表达式匹配
         * SubstringComparator
         * *******************************************
         * 下面仅以最可能用到的BinaryComparator、BinaryPrefixComparator举例:
         */
        QualifierFilter ff = new QualifierFilter(
                CompareFilter.CompareOp.EQUAL , 
                new BinaryComparator(Bytes.toBytes("belong"))   //表中不存在belong列,过滤结果为空
                );
        QualifierFilter ff1 = new QualifierFilter(
                CompareFilter.CompareOp.EQUAL , 
                new BinaryPrefixComparator(Bytes.toBytes("BELONG"))   //表中存在以BELONG打头的列BELONG_SITE,过滤结果为所有行的该列数据
                );
        Scan scan = new Scan();
        scan.setFilter(ff1);
        ResultScanner rs = table.getScanner(scan);  
说明:
一旦涉及到列(Qualifier),HBase就只认大写字母了!
该过滤器应该比FamilyFilter更常用!
3.3. 基于列名(即Qualifier)前缀过滤数据的ColumnPrefixFilter  ( 该功能用QualifierFilter也能实现 )
构造函数:
ColumnPrefixFilter(byte[] prefix) 

注意:
一个列名是可以出现在多个列族中的,该过滤器将返回所有列族中匹配的列。

官网示例代码,查找所有"abc"打头的列:
HTableInterface t = ...;
byte[] row = ...;
byte[] family = ...;
byte[] prefix = Bytes.toBytes("abc");
Scan scan = new Scan(row, row); // (optional) limit to one row
scan.addFamily(family); // (optional) limit to one family
Filter f = new ColumnPrefixFilter(prefix);
scan.setFilter(f);
scan.setBatch(10); // set this if there could be many columns returned
ResultScanner rs = t.getScanner(scan);
for (Result r = rs.next(); r != null; r = rs.next()) {
  for (KeyValue kv : r.raw()) {
    // each kv represents a column
  }
}
rs.close();

个人实测代码:
        HTable table = HBaseDAO.getHTable("147patents");
        //返回所有行中以BELONG打头的列的数据  
        ColumnPrefixFilter ff1 = new ColumnPrefixFilter(Bytes.toBytes("BELONG"));
        Scan scan = new Scan();
        scan.setFilter(ff1);
        ResultScanner rs = table.getScanner(scan);  

3.4. 基于多个列名(即Qualifier)前缀过滤数据的MultipleColumnPrefixFilter
说明:
MultipleColumnPrefixFilter 和 ColumnPrefixFilter 行为差不多,但可以指定多个前缀。

官方示例代码,查找所有"abc"或"xyz"打头的列:
HTableInterface t = ...;
byte[] row = ...;
byte[] family = ...;
byte[][] prefixes = new byte[][] {Bytes.toBytes("abc"), Bytes.toBytes("xyz")};
Scan scan = new Scan(row, row); // (optional) limit to one row
scan.addFamily(family); // (optional) limit to one family
Filter f = new MultipleColumnPrefixFilter(prefixes);
scan.setFilter(f);
scan.setBatch(10); // set this if there could be many columns returned
ResultScanner rs = t.getScanner(scan);
for (Result r = rs.next(); r != null; r = rs.next()) {
  for (KeyValue kv : r.raw()) {
    // each kv represents a column
  }
}
rs.close();

个人实测代码:
        HTable table = HBaseDAO.getHTable("147patents");
        
        byte[][] prefixes = new byte[][] {Bytes.toBytes("BELONG"), Bytes.toBytes("CREATE")};
        //返回所有行中以BELONG或者CREATE打头的列的数据
        MultipleColumnPrefixFilter ff = new MultipleColumnPrefixFilter(prefixes);

        Scan scan = new Scan();
        scan.setFilter(ff);
        ResultScanner rs = table.getScanner(scan);  

3.5. 基于列范围(不是行范围)过滤数据ColumnRangeFilter
说明:
可用于获得一个范围的列,例如,如果你的一行中有百万个列,但是你只希望查看列名为bbbb到dddd的范围
该方法从 HBase 0.92 版本开始引入
一个列名是可以出现在多个列族中的,该过滤器将返回所有列族中匹配的列

构造函数:
ColumnRangeFilter(byte[] minColumn, boolean minColumnInclusive, byte[] maxColumn, boolean maxColumnInclusive)
参数解释:
minColumn - 列范围的最小值,如果为空,则没有下限;
minColumnInclusive - 列范围是否包含minColumn ;
maxColumn - 列范围最大值,如果为空,则没有上限;
maxColumnInclusive - 列范围是否包含maxColumn 。

官网示例代码,查找列名在"bbbb"到"dddd"范围的数据:
HTableInterface t = ...;
byte[] row = ...;
byte[] family = ...;
byte[] startColumn = Bytes.toBytes("bbbb");
byte[] endColumn = Bytes.toBytes("bbdd");
Scan scan = new Scan(row, row); // (optional) limit to one row
scan.addFamily(family); // (optional) limit to one family
Filter f = new ColumnRangeFilter(startColumn, true, endColumn, true);
scan.setFilter(f);
scan.setBatch(10); // set this if there could be many columns returned
ResultScanner rs = t.getScanner(scan);
for (Result r = rs.next(); r != null; r = rs.next()) {
  for (KeyValue kv : r.raw()) {
    // each kv represents a column
  }
}
rs.close();

个人实测代码:
        HTable table = HBaseDAO.getHTable("147patents");
        
        byte[] startColumn = Bytes.toBytes("C");
        byte[] endColumn = Bytes.toBytes("D");
        //返回所有列中从C到D打头的范围的数据,实际返回类似CREATOR、CREATE_TIME、CHANNEL_CODE等列的数据
        ColumnRangeFilter ff = new ColumnRangeFilter(startColumn, true, endColumn, true);
        
        Scan scan = new Scan();
        scan.setFilter(ff);
        ResultScanner rs = table.getScanner(scan);  
4. RowKey
当需要根据行键特征查找一个范围的行数据时,使用Scan的startRow和stopRow会更高效,但是,startRow和stopRow只能匹配行键的开始字符,而不能匹配中间包含的字符:
        byte[] startColumn = Bytes.toBytes("aaa");
        byte[] endColumn = Bytes.toBytes("bbb");
        Scan scan = new Scan(startColumn,endColumn);
  
当需要针对行键进行更复杂的过滤时,可以使用RowFilter:

构造函数:
RowFilter(CompareFilter.CompareOp rowCompareOp, ByteArrayComparable rowComparator)
参数解释参见“引言-参数基础”章节。

个人实测代码:
        HTable table = HBaseDAO.getHTable("147patents");
        /**
         * rowkey格式为:创建日期_发布日期_ID_TITLE
         * 目标:查找  发布日期  为  2013-07-16  的数据
         */
        RowFilter rf = new RowFilter(
                CompareFilter.CompareOp.EQUAL , 
                new SubstringComparator("_2013-07-16_")   
                );
        Scan scan = new Scan();
        scan.setFilter(rf);
        ResultScanner rs = table.getScanner(scan);  
注意:
测试过程中尝试通过组合使用两个RowFilter(CompareFilter.CompareOp参数分别为GREATER_OR_EQUAL和LESS_OR_EQUAL),和SubstringComparator,过滤找出指定发布时间范围内的数据,但结果比较意外,不是预想的数据,估计比较运算符GREATER_OR_EQUAL和LESS_OR_EQUAL和比较器SubstringComparator组合使用效果不太好,慎用。
5.PageFilter
指定页面行数,返回对应行数的结果集。
需要注意的是,该过滤器并不能保证返回的结果行数小于等于指定的页面行数,因为过滤器是分别作用到各个region server的,它只能保证当前region返回的结果行数不超过指定页面行数。

构造函数:
PageFilter(long pageSize)

实测代码(从“2013-07-26”行开始,取5行):
            Scan scan = new Scan();
            scan.setStartRow(Bytes.toBytes("2013-07-26"));
            PageFilter pf = new PageFilter(5L);
            scan.setFilter(pf);
            ResultScanner rs = table.getScanner(scan);
            for (Result r : rs) {
                for (Cell cell : r.rawCells()) {
                    System.out.println("Rowkey : " + Bytes.toString(r.getRow())
                            + "   Familiy:Quilifier : "
                            + Bytes.toString(CellUtil.cloneQualifier(cell))
                            + "   Value : "
                            + Bytes.toString(CellUtil.cloneValue(cell))
                            + "   Time : " + cell.getTimestamp());
                }
            }  
注意:
由于该过滤器并不能保证返回的结果行数小于等于指定的页面行数,所以更好的返回指定行数的办法是ResultScanner.next(int nbRows) ,即:
            ResultScanner rs = table.getScanner(scan);
            for (Result r : rs.next(5)) {
                for (Cell cell : r.rawCells()) {
                    System.out.println("Rowkey : " + Bytes.toString(r.getRow())
                            + "   Familiy:Quilifier : "
                            + Bytes.toString(CellUtil.cloneQualifier(cell))
                            + "   Value : "
                            + Bytes.toString(CellUtil.cloneValue(cell))
                            + "   Time : " + cell.getTimestamp());
                }
            }  

6.SkipFilter
根据整行中的每个列来做过滤,只要存在一列不满足条件,整行都被过滤掉。
例如,如果一行中的所有列代表的是不同物品的重量,则真实场景下这些数值都必须大于零,我们希望将那些包含任意列值为0的行都过滤掉。
在这个情况下,我们结合ValueFilter和SkipFilter共同实现该目的:
scan.setFilter(new SkipFilter(new ValueFilter(CompareOp.NOT_EQUAL,new BinaryComparator(Bytes.toBytes(0))));

构造函数:
SkipFilter(Filter filter) 

个人实测代码:
目前的数据:
hbase(main):009:0> scan 'rd_ns:itable'
ROW                         COLUMN+CELL
 100001                     column=info:address, timestamp=1405417403438, value=anywhere
 100001                     column=info:age, timestamp=1405417403438, value=24
 100001                     column=info:name, timestamp=1405417403438, value=zhangtao
 100002                     column=info:address, timestamp=1405417426693, value=shangdi
 100002                     column=info:age, timestamp=1405417426693, value=28
 100002                     column=info:name, timestamp=1405417426693, value=shichao
 100003                     column=info:address, timestamp=1405494141522, value=huilongguan
 100003                     column=info:age, timestamp=1405494999631, value=29
 100003                     column=info:name, timestamp=1405494141522, value=liyang
3 row(s) in 0.0190 seconds

执行以下代码:
        Configuration conf = HBaseConfiguration.create();
        HTable table = new HTable(conf, "rd_ns:itable");
        Scan scan = new Scan();
        scan.setFilter(new SkipFilter(new ValueFilter(CompareOp.NOT_EQUAL,
                new BinaryComparator(Bytes.toBytes("28")))));
        ResultScanner rs = table.getScanner(scan);
        for (Result r : rs) {
            for (Cell cell : r.rawCells()) {
                System.out.println("Rowkey : " + Bytes.toString(r.getRow())
                        + "   Familiy:Quilifier : "
                        + Bytes.toString(CellUtil.cloneQualifier(cell))
                        + "   Value : "
                        + Bytes.toString(CellUtil.cloneValue(cell))
                        + "   Time : " + cell.getTimestamp());
            }
        }
        table.close();  
输出结果(整个100002行被过滤掉了):
Rowkey : 100001   Familiy:Quilifier : address   Value : anywhere   Time : 1405417403438
Rowkey : 100001   Familiy:Quilifier : age   Value : 24   Time : 1405417403438
Rowkey : 100001   Familiy:Quilifier : name   Value : zhangtao   Time : 1405417403438
Rowkey : 100003   Familiy:Quilifier : address   Value : huilongguan   Time : 1405494141522
Rowkey : 100003   Familiy:Quilifier : age   Value : 29   Time : 1405494999631
Rowkey : 100003   Familiy:Quilifier : name   Value : liyang   Time : 1405494141522  
7. Utility--FirstKeyOnlyFilter
该过滤器仅仅返回每一行中的第一个cell的值,可以用于高效的执行行数统计操作。
估计实战意义不大。

构造函数:
public FirstKeyOnlyFilter()

个人实测代码:
        HTable table = HBaseDAO.getHTable("147patents");
        FirstKeyOnlyFilter fkof = new FirstKeyOnlyFilter();
        Scan scan = new Scan();
        scan.setFilter(fkof);
        ResultScanner rs = table.getScanner(scan);  

8. 取得查询结果
无论是官网的Ref Guide还是网上流传的大部分博客中,输出查询结果的代码都是:
for (Result r = rs.next(); r != null; r = rs.next()) {
  for (KeyValue kv : r.raw()) {
    // each kv represents a column
  }
}

但查看最新的API可知Result实例的raw()方法已经不建议使用了:
raw() Deprecated. as of 0.96, use rawCells()

0.96以后版本正确的获取结果代码如下:
        for (Result r : rs) {
            for (Cell cell : r.rawCells()) {
                System.out.println(
                        "Rowkey : "+Bytes.toString(r.getRow())+
                        "Familiy:Quilifier : "+Bytes.toString(CellUtil.cloneQualifier(cell))+
                        "Value : "+Bytes.toString(CellUtil.cloneValue(cell))
                        );
            }
        }  
---------------------
作者:首席撩妹指导官
来源:CSDN
原文:https://blog.csdn.net/qq_36864672/article/details/78624856?utm_source=copy
版权声明:本文为博主原创文章,转载请附上博文链接!

posted on 2018-10-16 22:21  liangxb  阅读(2452)  评论(0编辑  收藏  举报