专用过滤器非常多,初学阶段我们不可能将所有的过滤器都掌握的非常清楚。我们只需要掌握常用的就可以,掌握了常用的过滤器,其他过滤器的使用也大同小异,所以其他的过滤器咱们大致了解就行。
前缀过滤器
前缀过滤器PrefixFilter
,在构造过滤器时传入一个前缀,所有与前缀匹配的行都会被返回到客户端。该过滤器的构造函数如下:
public PrefixFilter(byte[] prefix)
使用过滤器返回行键前缀为row-1
的数据:
Filter filter = new PrefixFilter(Bytes.toBytes("row-1"));
Scan scan = new Scan();
scan.setFilter(filter);
ResultScanner scanner = table.getScanner(scan);
for (Result result : scanner) {
for(Cell cell : result.listCells()){
String family = Bytes.toString(CellUtil.cloneFamily(cell));
String qualifier = Bytes.toString(CellUtil.cloneQualifier(cell));
String value = Bytes.toString(CellUtil.cloneValue(cell));
System.out.println(family + ":" + qualifier + " " + value);
}
}
scanner.close();
这段代码搜索出来数据的行键都是以row-1
开头的。
分页过滤器
我们知道如果数据库中有非常多的数据,比如万条,一次性将所有数据全都查询出来肯定是不理智的,所以分页查询就很有必要了,分页查询也是最常用的查询方式之一。
分页过滤器PageFilter
,是常用的过滤器之一,用户可以使用这个过滤器对结果进行分页。构造函数为:PageFilter(long pageSize)
当用户创建当前过滤器实例时,需要指定pageSize
参数,这个参数可以控制每页数据的行数。
使用分页过滤器的时候客户端代码会记录本次扫描的最后一行,并在下一次获取数据时把记录的上次扫描的最后一行设为本次扫描的起始行,同时保留相同的过滤属性,然后依次进行迭代。
分页时对依次返回的行数设定了严格的限制,一次扫描所覆盖的行数很可能是多于分页大小的,一旦这种情况发生,过滤器有一种机制通知region
服务器停止扫描。
接下来我们通过示例体验一下:
byte[] POSTFIX = new byte[] { 0x00 };
Table table = conn.getTable(tableName);
Filter filter = new PageFilter(15);//构建过滤器并设置每页数据量
int totalRows = 0;
byte[] lastRow = null;
while(true){
Scan scan = new Scan();
//添加过滤器
scan.setFilter(filter);
//设置查询的起始行
if(lastRow != null){
byte[] startRow = Bytes.add(lastRow, POSTFIX);
System.out.println("start row: " +
Bytes.toStringBinary(startRow));
scan.withStartRow(startRow);
}
ResultScanner scanner = table.getScanner(scan);
int localRows = 0;
Result result;
while ((result = scanner.next()) != null) {
System.out.println(localRows++ + ": " + result);
totalRows++;
lastRow = result.getRow();
}
scanner.close();
if (localRows == 0) break;
}
这样就能实现分页查询了。
列分页过滤器
列分页过滤器ColumnPaginationFilter
,与PageFilter
类似,这个过滤器可以对一行的所有列进行分页。它的构造器需要两个参数:
ColumnPaginationFilter(int limut,int offset)
它将跳过所有偏移量小于offset
的列,并包括之后所有偏移量在limit
之前(包含limit
)的列。
我们来通过一个例子来了解一下如何使用:
Filter filter = new ColumnPaginationFilter(5,15);
Scan scan = new Scan();
scan.setFilter(filter);
ResultScanner scanner = table.getScanner(scan);
for(Result result : scanner){
System.out.println(result);
}
查询结果:
其他过滤器
过滤器 | 描述 | 构造函数 |
---|---|---|
单列值过滤器 | 用一列的值决定是否一行数据被过滤 | SingleColumnValueFilter(family, qualifier, op, value) |
单列排除过滤器 | 查询的结果会排除,该过滤器设置的列 | SingleColumnValueExcludeFilter(family, qualifier, op, value) |
行键过滤器 | 可以通过convertTokeyOnly(boolean)方法帮助调用只返回键不返回值 | KeyOnlyFilter(boolean lenAsVal) |
首次行键过滤器 | 过如果用户需要访问同一行中的第一列,则这种过滤器可以满足需求 | FirstKeyOnlyFilter() |
包含结束的过滤器 | 扫描操作中的开始行被包含到结果中,但终止行被排除在外 | InclusiveByteRange(Long first, Long last) |
时间戳过滤器 | 当用户需要在扫描结果中对版本进行细粒度的控制时 | TimeStampsFilter(List<Long>) timestamps |
列计数过滤器 | 用户可以使用这个过滤器来限制每行最多取回多少列 | ColumnCountGetFilter(int n) |
列前缀过滤器 | 通过对列名称进行前缀匹配过滤 | ColumnPrefixFilter(byte[] prefix) |
随机行过滤器 | 可以让结果中包含随机航 | RandomRowFilter(float chance) |
跳转过滤器 | 可以过滤特定数据,当过滤器发现某一行中的一列需要过滤时,会过滤掉整行的数据 | SkipFilter(Filter filter) |
实际应用中用户可能需要多个过滤器共同限制返回到客户端的结果,我们应该怎么做呢?
FilterList
我们可以使用FilterList
(过滤器列表)来组合多个过滤器,实现单个过滤器不能实现的功能。
过滤器列表提供了组合各个过滤器的功能。
与其他单一功能的过滤器一样,FilterList
类实现了Filter
接口,所以它可以通过组合多个过滤器的功能来实现某种效果。
要使用FilterList
,我们需要先知道它有哪些构造方法:
FilterList(List<Filter> rowFilters)
FilterList(Operator operator)
FilterList(Operator operator,List<Filter> rowFilters)
参数rowFilters
以列表的形式创建过滤器,参数operator
(操作符)决定了组合他们的结果,第一个参数很简单,第二个参数我们没有见过,它总共有两种取值,默认值是MUST_PASS_ALL
:
的可选枚举值
操作 | 描述 |
---|---|
MUST_PASS_ALL | 当所有过滤器都允许包含这个值时,这个值才会被包含在结果中,也就是说没有过滤器会忽略这个值 |
MUST_PASS_ONE | 只要有一个过滤器允许包括这个值,那这个值就会包含在结果中 |
当我们创建了 FilterList
实例之后,可以用以下方法添加过滤器:
void addFilter(Filter filter)
每个FilterList
只能添加一个操作符,但用户可以随意地向已经存在的FilterList
实例中添加FilterList
实例,这样可以构造一组多级的过滤器,同时它们可以与用户需要的操作符进行组合。
用户也可以通过控制List
中过滤器的顺序来进一步精确地控制过滤器的执行顺序。例如,使用 ArrayList
可以保证过滤器的执行顺序与它们添加到列表中的顺序一致。
示例:
List<Filter> filters = new ArrayList<>();
Filter rowFilter1 = new RowFilter(CompareOperator.GREATER_OR_EQUAL,
new BinaryComparator(Bytes.toBytes("row-3")));
filters.add(rowFilter1);
Filter rowFilter2 = new RowFilter(CompareOperator.LESS_OR_EQUAL, new BinaryComparator(Bytes.toBytes("row-6")));
filters.add(rowFilter2);
Filter rowFilter3 = new RowFilter(CompareOperator.EQUAL, new BinaryComparator(Bytes.toBytes("row-3")));
filters.add(rowFilter3);
FilterList filterList1 = new FilterList(filters);
Scan scan = new Scan();
scan.setFilter(filterList1);
ResultScanner scanner = table.getScanner(scan);
for (Result result : scanner) {
for(Cell cell : result.listCells()){
String family = Bytes.toString(CellUtil.cloneFamily(cell));
String qualifier = Bytes.toString(CellUtil.cloneQualifier(cell));
String value = Bytes.toString(CellUtil.cloneValue(cell));
System.out.println("\t" + family + ":" + qualifier + " " + value);
}
}
scanner.close();
FilterList filterList2 = new FilterList(FilterList.Operator.MUST_PASS_ONE,filters);
scan.setFilter(filterList2);
ResultScanner scanner2 = table.getScanner(scan);
for (Result result : scanner2) {
for(Cell cell : result.listCells()){
String family = Bytes.toString(CellUtil.cloneFamily(cell));
String qualifier = Bytes.toString(CellUtil.cloneQualifier(cell));
String value = Bytes.toString(CellUtil.cloneValue(cell));
System.out.println("\t" + family + ":" + qualifier + " " + value);
}
}
conn.close();
第一个扫描中的过滤器过滤了许多数据,正是由于列表中任意一个过滤器过滤了该数据,该数据就会被丢弃,只有当数据经过了所有过滤器的筛选才会被传回客户端。
第一种模式(MUST_PASS_ALL
)的FilterList
的作用可以类比净水器的滤芯。
数据必须通过所有滤网才能到达客户端。
第二种模式(MUST_PASS_ONE
)的FilterList
允许数据只需要通过一种过滤器的过滤就可以被返回。
执行上述代码我们会发现,使用filterList1
扫描不到任何结果,而使用filterList2
,却可以扫描到表中所有的数据(假设表的数据是从row-0 - row-100
),是为什么呢? 需要自己思考哦。