HBase 過濾器 (二)

繼  HBase 過濾器 (一)

1.3 專用過濾器 (Dedicated Filters)

----
HBase 提供的第二類過濾器直接基於 FilterBase 並實現了更特定的應用場景。其中的很多過濾器事實上只適用於執行掃描操作時,因爲它們過濾整個行。
對於 get() 調用,這些過濾器的限制過於苛刻:包括所有行,後者什麼也不包括。


    ■ 前綴過濾器 (PrefixFilter)
    -------------------------------------------------------------------------------------------------------------------------------------
    實例化過濾器時給定一個行的前綴,所有行鍵匹配這個前綴的行返回給客戶端:
    
        PrefixFilter(final byte[] prefix)

    示例: Example using the prefix based filter

    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.rawCells()) {
            System.out.println("Cell: " + cell + ", Value: " +
                Bytes.toString(cell.getValueArray(), cell.getValueOffset(),
                cell.getValueLength()));
        }
    }
    scanner.close();
    
    Get get = new Get(Bytes.toBytes("row-5"));
    get.setFilter(filter);
    Result result = table.get(get);
    
    for (Cell cell : result.rawCells()) {
        System.out.println("Cell: " + cell + ", Value: " +
        Bytes.toString(cell.getValueArray(), cell.getValueOffset(),
        cell.getValueLength()));
    }

    輸出:
    Results of scan:
    Cell: row-1/colfam1:col-1/1427280142327/Put/vlen=7/seqid=0, Value: val-1.1
    Cell: row-1/colfam1:col-10/1427280142379/Put/vlen=8/seqid=0, Value:    val-1.10
    ...
    Cell: row-1/colfam2:col-8/1427280142375/Put/vlen=7/seqid=0, Value:    val-1.8
    Cell: row-1/colfam2:col-9/1427280142377/Put/vlen=7/seqid=0, Value:    val-1.9
    Cell: row-10/colfam1:col-1/1427280142530/Put/vlen=8/seqid=0, Value: val-10.1
    Cell: row-10/colfam1:col-10/1427280142546/Put/vlen=9/seqid=0, Value: val-10.10
    ...
    Cell: row-10/colfam2:col-8/1427280142542/Put/vlen=8/seqid=0, Value:    val-10.8
    Cell: row-10/colfam2:col-9/1427280142544/Put/vlen=8/seqid=0, Value:    val-10.9
    Result of get:

    需要注意的是,get() 方法並沒有返回任何結果,因爲它請求的行與過濾器的前綴不匹配。這個過濾器在使用 get() 方法時作用不大,但在掃描操作中
    非常有用。

    當掃描器遇到一個比前綴大的行鍵時掃描結束。與 start row 配合使用,此過濾器會提升掃描的整體性能,因爲它知道什麼時候忽略掉所有剩下的行。

    
    ■ 分頁過濾器 (PageFilter)
    -------------------------------------------------------------------------------------------------------------------------------------
    可以使用這個過濾器對結果按行分頁。創建該過濾器實例時,要指定 pageSize 參數,控制返回每頁包含的行數。
    
        PageFilter(final long pageSize)
        
        NOTE:
        ---------------------------------------------------------------------------------------------------------------------------------        
        在物理上分離的服務器上進行過濾有一個根本性的問題。過濾器併發地運行在不同的分區服務器上,並且不能跨越這種邊界保持它們的當前狀態。因
        此,每一個過濾器要求掃描到至少到 pageCount 行纔會結束掃描。

    客戶端代碼需要記住返回的最後的行,然後,在另一次迭代要開始時,以此來設定掃描的起始行(start row),保持相同的過濾器屬性。

    示例: Example using a filter to paginate through rows
    
    private static final byte[] POSTFIX = new byte[] { 0x00 };
    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.setStartRow(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;
    }
    System.out.println("total rows: " + totalRows);
    
    輸出:
    Adding rows to table...
    0: keyvalues={row-1/colfam1:col-1/1427280402935/Put/vlen=7/    seqid=0, ...}
    1: keyvalues={row-10/colfam1:col-1/1427280403125/Put/vlen=8/seqid=0, ...}
    ...
    14: keyvalues={row-110/colfam1:col-1/1427280404601/Put/vlen=9/seqid=0, ...}
    start row: row-110\x00
    0: keyvalues={row-111/colfam1:col-1/1427280404615/Put/vlen=9/seqid=0, ...}
    1: keyvalues={row-112/colfam1:col-1/1427280404628/Put/vlen=9/seqid=0, ...}
    ...
    14: keyvalues={row-124/colfam1:col-1/1427280404786/Put/vlen=9/seqid=0, ...}
    start row: row-124\x00
    0: keyvalues={row-125/colfam1:col-1/1427280404799/Put/vlen=9/seqid=0, ...}
    ...
    start row: row-999\x00
    total rows: 1000

    HBase 中的行鍵是按字典序排序的,因此返回的結果也是如此排序,並且起始行是包括在結果中的。需要拼接一個 0 字節到前一個 row key.
    這確保最後看到的行鍵被忽略,並且下一個,按排序次序,能被找到。內容爲 0 的字節只有很少的增加量,因此可安全地用於重置掃描邊界。



    ■ 鍵過濾器 (KeyOnlyFilter)
    -------------------------------------------------------------------------------------------------------------------------------------
    有些應用只需要訪問每個 cell 的 key, 而忽略其實際的值。KeyOnlyFilter 提供這種功能。構造器:
    
        KeyOnlyFilter()
        KeyOnlyFilter(boolean lenAsVal)

    可選的 lenAsVal 參數,用於處理內部轉換調用 as-is, 控制對每一個 Cell 實例的值部分的處理。默認值爲 false, 簡單地設置爲 0 長度的值,而
    相反爲 true 則設置值爲原始值的長度表示。

    示例: Only returns the first found cell from each row
    int rowCount = 0;
    for (Result result : scanner) {
        for (Cell cell : result.rawCells()) {
            System.out.println("Cell: " + cell + ", Value: " + (
            cell.getValueLength() > 0 ?
            Bytes.toInt(cell.getValueArray(), cell.getValueOffset(),
            cell.getValueLength()) : "n/a" ));
        }
        rowCount++;
    }
    System.out.println("Total num of rows: " + rowCount);
    scanner.close();
    }
    
    public static void main(String[] args) throws IOException {
    
        Configuration conf = HBaseConfiguration.create();
        HBaseHelper helper = HBaseHelper.getHelper(conf);
    
        helper.dropTable("testtable");
        helper.createTable("testtable", "colfam1");
        System.out.println("Adding rows to table...");
        helper.fillTableRandom("testtable", /* row */ 1, 5, 0,
        /* col */ 1, 30, 0, /* val */ 0, 10000, 0, true, "colfam1");
        Connection connection = ConnectionFactory.createConnection(conf);
        table = connection.getTable(TableName.valueOf("testtable"));
        System.out.println("Scan #1");
        
        Filter filter1 = new KeyOnlyFilter();
        scan(filter1);
        
        Filter filter2 = new KeyOnlyFilter(true);
        scan(filter2);

    輸出:
    Results of scan:
    Cell: row-0/colfam1:col-17/6/Put/vlen=0/seqid=0, Value: n/a
    Cell: row-0/colfam1:col-27/3/Put/vlen=0/seqid=0, Value: n/a
    ...
    Cell: row-4/colfam1:col-3/2/Put/vlen=0/seqid=0, Value: n/a
    Cell: row-4/colfam1:col-5/16/Put/vlen=0/seqid=0, Value: n/a
    Total num of rows: 5
    Scan #2
    Results of scan:
    Cell: row-0/colfam1:col-17/6/Put/vlen=4/seqid=0, Value: 8
    Cell: row-0/colfam1:col-27/3/Put/vlen=4/seqid=0, Value: 6
    ...
    Cell: row-4/colfam1:col-3/2/Put/vlen=4/seqid=0, Value: 7
    Cell: row-4/colfam1:col-5/16/Put/vlen=4/seqid=0, Value: 8
    Total num of rows: 5


    ■ 首行鍵過濾器 (FirstKeyOnlyFilter)
    -------------------------------------------------------------------------------------------------------------------------------------
    雖然這個名稱隱含着 KeyValue, 或 key only, 這兩者都是用詞不當。這個過濾器返回它找到的行中的第一個 cell, 及其所有的細節,包括值,或許它
    應命名爲 FirstCellFilter 之類的名稱。
    
    如果需要訪問每一行的第一個列(因爲 HBase 隱式排序的), 這個過濾器可以提供這類功能。通常,這個過濾器用於行計數(row counter) 類型的應用中,
    因爲它只需要檢查一個行是否存在。在面向列的數據庫中,一個行實際上是由列組成的,並且如果沒有列,行業就不存在。
    
    另一可能使用的場景依賴於列是按詞彙表排序的,並且設置 column qualifier 爲紀元值。這會使排序的列最早的時間戳名最新獲取到。配合使用這個
    過濾器,一次掃描就可以從每一行上獲取最早的列。更有趣的是,如果反轉列限定符的時間戳設置,那麼久可以在一次掃描中獲取每一行中最新的項目。

    這個類使用了過濾器框架提供的另一個優化特性:它在檢查完第一個列之後會通知 region server 結束對當前行的掃描,並跳到下一行,與全表掃描相比,
    其性能得到了提升。
    
    示例: Only returns the first found cell from each row
    
    Filter filter = new FirstKeyOnlyFilter();
    
    Scan scan = new Scan();
    scan.setFilter(filter);
    
    ResultScanner scanner = table.getScanner(scan);
    int rowCount = 0;
    for (Result result : scanner) {
        for (Cell cell : result.rawCells()) {
            System.out.println("Cell: " + cell + ", Value: " +
                Bytes.toString(cell.getValueArray(), cell.getValueOffset(), cell.getValueLength()));
        }
        rowCount++;
    }
    System.out.println("Total num of rows: " + rowCount);
    scanner.close();

    輸出:
    Adding rows to table...
    Results of scan:
    Cell: row-0/colfam1:col-10/19/Put/vlen=6/seqid=0, Value: val-76
    Cell: row-1/colfam1:col-0/0/Put/vlen=6/seqid=0, Value: val-19
    ...
    Cell: row-8/colfam1:col-10/4/Put/vlen=6/seqid=0, Value: val-35
    Cell: row-9/colfam1:col-1/5/Put/vlen=5/seqid=0, Value: val-0
    Total num of rows: 30

    顯示出每行只有一個 cell 返回


    ■ FirstKeyValueMatchingQualifiersFilter
    -------------------------------------------------------------------------------------------------------------------------------------
    這個過濾器是 FirstKeyOnlyFilter 的擴展,但不是返回找到的第一個 cell, 而是返回一個行中所有的列,直到一個給定的列限定符(column qualifier)
    如果行中沒有這個限定符,返回所有的列。這個過濾器主要用於 rowcounter shell command。
    
    構造器:
        
        FirstKeyValueMatchingQualifiersFilter(Set<byte[]> qualifiers)

    示例: Returns all columns, or up to the first found reference qualifier, for each row
    
    Set<byte[]> quals = new HashSet<byte[]>();
    quals.add(Bytes.toBytes("col-2"));
    quals.add(Bytes.toBytes("col-4"));
    quals.add(Bytes.toBytes("col-6"));
    quals.add(Bytes.toBytes("col-8"));
    Filter filter = new FirstKeyValueMatchingQualifiersFilter(quals);
    
    Scan scan = new Scan();
    scan.setFilter(filter);
    ResultScanner scanner = table.getScanner(scan);
    
    int rowCount = 0;
    for (Result result : scanner) {
        for (Cell cell : result.rawCells()) {
            System.out.println("Cell: " + cell + ", Value: " +
            Bytes.toString(cell.getValueArray(), cell.getValueOffset(),
            cell.getValueLength()));
        }
        rowCount++;
    }
    System.out.println("Total num of rows: " + rowCount);
    scanner.close();

    輸出:
    Adding rows to table...
    Results of scan:
    Cell: row-0/colfam1:col-0/1/Put/vlen=6/seqid=0, Value: val-48
    Cell: row-0/colfam1:col-1/4/Put/vlen=6/seqid=0, Value: val-78
    Cell: row-0/colfam1:col-5/1/Put/vlen=6/seqid=0, Value: val-62
    Cell: row-0/colfam1:col-6/6/Put/vlen=5/seqid=0, Value: val-6
    Cell: row-10/colfam1:col-1/3/Put/vlen=6/seqid=0, Value: val-73
    Cell: row-10/colfam1:col-6/5/Put/vlen=6/seqid=0, Value: val-11
    ...
    Cell: row-6/colfam1:col-1/0/Put/vlen=6/seqid=0, Value: val-39
    Cell: row-7/colfam1:col-9/6/Put/vlen=6/seqid=0, Value: val-57
    Cell: row-8/colfam1:col-0/2/Put/vlen=6/seqid=0, Value: val-90
    Cell: row-8/colfam1:col-1/4/Put/vlen=6/seqid=0, Value: val-92
    Cell: row-8/colfam1:col-6/4/Put/vlen=6/seqid=0, Value: val-12
    Cell: row-9/colfam1:col-1/5/Put/vlen=6/seqid=0, Value: val-35
    Cell: row-9/colfam1:col-2/2/Put/vlen=6/seqid=0, Value: val-22
    Total num of rows: 47



    ■ 包含結束的過濾器 (InclusiveStopFilter)
    -------------------------------------------------------------------------------------------------------------------------------------
    一個掃描的行邊界是包含起始行(inclusive for the start row), 而不包含結束行(exclusive for the stop row). 可以使用這個過濾器改變結束行的
    語義,即包含給定的結束行。
    
    示例: Example using a filter to include a stop row
    
    Filter filter = new InclusiveStopFilter(Bytes.toBytes("row-5"));
    
    Scan scan = new Scan();
    scan.setStartRow(Bytes.toBytes("row-3"));
    scan.setFilter(filter);
    
    ResultScanner scanner = table.getScanner(scan);
    for (Result result : scanner) {
        System.out.println(result);
    }
    scanner.close();

    輸出:
    Adding rows to table...
    Results of scan:
    keyvalues={row-3/colfam1:col-1/1427282689001/Put/vlen=7/seqid=0}
    keyvalues={row-30/colfam1:col-1/1427282689069/Put/vlen=8/seqid=0}
    ...
    keyvalues={row-48/colfam1:col-1/1427282689100/Put/vlen=8/seqid=0}
    keyvalues={row-49/colfam1:col-1/1427282689102/Put/vlen=8/seqid=0}
    keyvalues={row-5/colfam1:col-1/1427282689004/Put/vlen=7/seqid=0}


    ■ FuzzyRowFilter
    -------------------------------------------------------------------------------------------------------------------------------------
    這個過濾器作用於行鍵,但是以一種模糊的形式(in a fuzzy manner)。它需要一個應返回的行鍵的列表,加上一個相應的 byte[] 數組用於表示行鍵中
    每一個字節的重要性。構造器如下:
    
        FuzzyRowFilter(List<Pair<byte[], byte[]>> fuzzyKeysData)

    fuzzyKeysData 指定一個行鍵字節的重要性,接受兩個值之一:
    
    0    :指示在行鍵中同一位置的字節必須匹配 as-is
    1    :意思是不關心對應的行鍵字節,並且總是接受

    示例: Example filtering by column prefix

    List<Pair<byte[], byte[]>> keys = new ArrayList<Pair<byte[], byte[]>>();
    
    keys.add(new Pair<byte[], byte[]>(Bytes.toBytes("row-?5"), new byte[] { 0, 0, 0, 0, 1, 0 }));
    Filter filter = new FuzzyRowFilter(keys);
    
    Scan scan = new Scan()
        .addColumn(Bytes.toBytes("colfam1"), Bytes.toBytes("col-5"))
        .setFilter(filter);
        
    ResultScanner scanner = table.getScanner(scan);
    
    for (Result result : scanner) {
        System.out.println(result);
    }
    scanner.close();

    輸出:
    Adding rows to table...
    Results of scan:
    keyvalues={row-05/colfam1:col-01/1/Put/vlen=9/seqid=0,
    row-05/colfam1:col-02/2/Put/vlen=9/seqid=0,
    ...
    row-05/colfam1:col-09/9/Put/vlen=9/seqid=0,
    row-05/colfam1:col-10/10/Put/vlen=9/seqid=0}
    keyvalues={row-15/colfam1:col-01/1/Put/vlen=9/seqid=0,
    row-15/colfam1:col-02/2/Put/vlen=9/seqid=0,
    ...
    row-15/colfam1:col-09/9/Put/vlen=9/seqid=0,
    row-15/colfam1:col-10/10/Put/vlen=9/seqid=0}

    測試代碼寫入 20 行到表中,名稱爲 row-01 到 row-20. 我們要獲取匹配模式爲 row-?5 的所有的行。


    ■ ColumnCountGetFilter
    -------------------------------------------------------------------------------------------------------------------------------------
    可以使用這個過濾器只獲取一個每行有指定的最大數量列的數據。
    
        ColumnCountGetFilter(final int n)
        
    一旦找到某一行匹配最大數量的列數整個掃描就停止,因此對於 scan 操作沒什麼用處,它只是用於在 get() 調用中測試過濾器。


    ■ ColumnPrefixFilter
    -------------------------------------------------------------------------------------------------------------------------------------
    類似於 PrefixFilter, 這個過濾器通過對列名稱進行前綴匹配過濾。需要指定一個前綴來創建過濾器:
    
        ColumnPrefixFilter(final byte[] prefix)
        
    所有與設定的前綴匹配的列都包含在結果中。

    示例: Example filtering by column prefix, selects all columns starting with col-1.

    Filter filter = new ColumnPrefixFilter(Bytes.toBytes("col-1"));
    
    Scan scan = new Scan();
    scan.setFilter(filter);
    ResultScanner scanner = table.getScanner(scan);
    for (Result result : scanner) {
        System.out.println(result);
    }
    
    scanner.close();

    輸出:
    Adding rows to table...
    Results of scan:
    keyvalues={row-1/colfam1:col-1/1/Put/vlen=7/seqid=0,
    row-1/colfam1:col-10/10/Put/vlen=8/seqid=0,
    ...
    row-1/colfam1:col-19/19/Put/vlen=8/seqid=0}
    ...


    ■ MultipleColumnPrefixFilter
    -------------------------------------------------------------------------------------------------------------------------------------
    這個過濾器是 ColumnPrefixFilter 的直接擴展,運行應用請求一個列限定符的前綴列表,而不僅僅是一個前綴。
    
        MultipleColumnPrefixFilter(final byte[][] prefixes)
        
    示例: Example filtering by column prefix, adds two column prefixes, and also a row prefix to limit the output.
    
    Filter filter = new MultipleColumnPrefixFilter(new byte[][] {
    Bytes.toBytes("col-1"), Bytes.toBytes("col-2")
    });
    
    Scan scan = new Scan()
    //Limit to rows starting with a specific prefix
    .setRowPrefixFilter(Bytes.toBytes("row-1"))
    .setFilter(filter);
    
    ResultScanner scanner = table.getScanner(scan);
    for (Result result : scanner) {
        System.out.print(Bytes.toString(result.getRow()) + ": ");
            for (Cell cell : result.rawCells()) {
                System.out.print(Bytes.toString(cell.getQualifierArray(),
                cell.getQualifierOffset(), cell.getQualifierLength()) + ",
                ");
        }
    
    System.out.println();
    }
    scanner.close();

    
    輸出:

    Adding rows to table...
    Results of scan:
    row-1: col-1, col-10, col-11, col-12, col-13, col-14, col-15,
    col-16,
    col-17, col-18, col-19, col-2, col-20, col-21, col-22, col-23,
    col-24,
    col-25, col-26, col-27, col-28, col-29,
    row-10: col-1, col-10, col-11, col-12, col-13, col-14, col-15,
    col-16,
    col-17, col-18, col-19, col-2, col-20, col-21, col-22, col-23,
    col-24,
    col-25, col-26, col-27, col-28, col-29,
    row-18: col-1, col-10, col-11, col-12, col-13, col-14, col-15,
    col-16,
    col-17, col-18, col-19, col-2, col-20, col-21, col-22, col-23,
    col-24,
    col-25, col-26, col-27, col-28, col-29,
    row-19: col-1, col-10, col-11, col-12, col-13, col-14, col-15,
    col-16,
    col-17, col-18, col-19, col-2, col-20, col-21, col-22, col-23,
    col-24,
    col-25, col-26, col-27, col-28, col-29,


    ■ ColumnRangeFilter
    -------------------------------------------------------------------------------------------------------------------------------------
    這個過濾器類似於兩個 QualifierFilter 實例一起工作,一個用於檢查低邊界,另一個用於檢查高邊界。兩個實例通過提供的BinaryPrefixComparator
    分別爲 LESS_OR_EQUAL 和 GREATER_OR_EQUAL 兩個比較操作符:

        ColumnRangeFilter(final byte[] minColumn, boolean minColumnInclusive,
                        final byte[] maxColumn, boolean maxColumnInclusive)

    應提供可選的 minColumn 和 maxColumn 以及 boolean 值 minColumnInclusive 和 maxColumnInclusive 用於標誌排除或者包含。如果沒有指定
    minColumn 則從 table 的開始掃描,如果沒有指定 maxColumn,則掃描到 table 的末尾。
    
    示例: Example filtering by columns within a given range
    
    Filter filter = new ColumnRangeFilter(Bytes.toBytes("col-05"), true, Bytes.toBytes("col-11"), false);
    
    Scan scan = new Scan()
    .setStartRow(Bytes.toBytes("row-03"))
    .setStopRow(Bytes.toBytes("row-05"))
    .setFilter(filter);
    
    ResultScanner scanner = table.getScanner(scan);
    for (Result result : scanner) {
        System.out.println(result);
    }
    scanner.close();

    輸出:
    Adding rows to table...
    Results of scan:
    keyvalues={row-03/colfam1:col-05/5/Put/vlen=9/seqid=0,
    row-03/colfam1:col-06/6/Put/vlen=9/seqid=0,
    row-03/colfam1:col-07/7/Put/vlen=9/seqid=0,
    row-03/colfam1:col-08/8/Put/vlen=9/seqid=0,
    row-03/colfam1:col-09/9/Put/vlen=9/seqid=0,
    row-03/colfam1:col-10/10/Put/vlen=9/seqid=0}
    keyvalues={row-04/colfam1:col-05/5/Put/vlen=9/seqid=0,
    row-04/colfam1:col-06/6/Put/vlen=9/seqid=0,
    row-04/colfam1:col-07/7/Put/vlen=9/seqid=0,
    row-04/colfam1:col-08/8/Put/vlen=9/seqid=0,
    row-04/colfam1:col-09/9/Put/vlen=9/seqid=0,
    row-04/colfam1:col-10/10/Put/vlen=9/seqid=0}

    ■ 單列值過濾器 (SingleColumnValueFilter)
    -------------------------------------------------------------------------------------------------------------------------------------
    當用一個列的值精確匹配來決定整個行是否返回到結果中時,使用這個過濾器。需要首先指定要跟蹤的列,然後指定要檢查的值。
    
    SingleColumnValueFilter(final byte[] family, final byte[] qualifier, final CompareOp compareOp, final byte[] value)
    
    SingleColumnValueFilter(final byte[] family, final byte[] qualifier, final CompareOp compareOp, final ByteArrayComparable comparator)
    protected SingleColumnValueFilter(final byte[] family, final byte[] qualifier, final CompareOp compareOp,
        ByteArrayComparable comparator, final boolean filterIfMissing, final boolean latestVersionOnly)
        
    第一個構造器很簡單,在內部創建 BinaryComparator 實例。第二個構造器與一直使用的基於 CompareFilter 的參數相同。雖然 SingleColumnValueFilter
    不是直接繼承自 CompareFilter, 它們還是具有相同的構造器參數類型。第三個構造器,增添了兩個額外的 boolean 標誌,這兩個標誌也可以在過濾器實例
    構造之後,通過 getter 和 setter 方法設置:
    
        boolean getFilterIfMissing()
        void setFilterIfMissing(boolean filterIfMissing)
        
        boolean getLatestVersionOnly()
        void setLatestVersionOnly(boolean latestVersionOnly)

    前面的方法用於控制如果行中根本沒有指定的列如何處理。默認是包含在結果中,但可以通過 setFilterIfMissing(true) 來反轉這種行爲,那樣,所有沒有
    引用列的行都從結果中移除。
    
    通過 setLatestVersionOnly(false), 默認爲 true, 可以改變過濾器的默認行爲,也就是隻檢查引用列的最新版本,改變默認行爲只會,也會在檢查中包含
    以前的版本。

    示例: Example using a filter to return only rows with a given value in a given column
    
    SingleColumnValueFilter filter = new SingleColumnValueFilter(
                                        Bytes.toBytes("colfam1"),
                                        Bytes.toBytes("col-5"),
                                        CompareFilter.CompareOp.NOT_EQUAL,
                                        new SubstringComparator("val-5"));
    filter.setFilterIfMissing(true);
    
    Scan scan = new Scan();
    scan.setFilter(filter);
    
    ResultScanner scanner = table.getScanner(scan);
    for (Result result : scanner) {
        for (Cell cell : result.rawCells()) {
            System.out.println("Cell: " + cell + ", Value: " +
            Bytes.toString(cell.getValueArray(), cell.getValueOffset(),
            cell.getValueLength()));
        }
    }
    scanner.close();
    
    Get get = new Get(Bytes.toBytes("row-6"));
    get.setFilter(filter);
    
    Result result = table.get(get);
    System.out.println("Result of get: ");
    
    for (Cell cell : result.rawCells()) {
        System.out.println("Cell: " + cell + ", Value: " +
        Bytes.toString(cell.getValueArray(), cell.getValueOffset(),
        cell.getValueLength()));
    }

    輸出:
    Adding rows to table...
    Results of scan:
    Cell: row-1/colfam1:col-1/1427279447557/Put/vlen=7/seqid=0, Value: val-1.1
    Cell: row-1/colfam1:col-10/1427279447613/Put/vlen=8/seqid=0, Value:    val-1.10
    ...
    Cell: row-4/colfam2:col-8/1427279447667/Put/vlen=7/seqid=0, Value: val-4.8
    Cell: row-4/colfam2:col-9/1427279447669/Put/vlen=7/seqid=0, Value: val-4.9
    Cell: row-6/colfam1:col-1/1427279447692/Put/vlen=7/seqid=0, Value: val-6.1
    Cell: row-6/colfam1:col-10/1427279447709/Put/vlen=8/seqid=0, Value: val-6.10
    ...
    Cell: row-9/colfam2:col-8/1427279447759/Put/vlen=7/seqid=0, Value: val-9.8
    Cell: row-9/colfam2:col-9/1427279447761/Put/vlen=7/seqid=0, Value: val-9.9
    Result of get:
    Cell: row-6/colfam1:col-1/1427279447692/Put/vlen=7/seqid=0, Value: val-6.1
    Cell: row-6/colfam1:col-10/1427279447709/Put/vlen=8/seqid=0, Value: val-6.10
    ...
    Cell: row-6/colfam2:col-8/1427279447705/Put/vlen=7/seqid=0, Value: val-6.8
    Cell: row-6/colfam2:col-9/1427279447707/Put/vlen=7/seqid=0, Value: val-6.9



    ■ 單列排除過濾器 (SingleColumnValueExcludeFilter)
    -------------------------------------------------------------------------------------------------------------------------------------
    SingleColumnValueExcludeFilter 繼承自 SingleColumnValueFilter,經擴展後提供一種稍微不同的語義:參考列從結果中排除。換句話說,用戶可以
    使用與 SingleColumnValueFilter 相同的構造器、方法以及特性來控制此過濾器的工作。唯一不同的是,客戶端的 Result 實例中,永遠不會獲得作爲
    檢查目標的參考列,因爲它是在結果中被排除的。



    ■ 時間戳過濾器 (TimestampsFilter)
    -------------------------------------------------------------------------------------------------------------------------------------
    當需要在掃描結果中對版本進行細粒度的控制時,這個過濾器可以滿足需求。需要傳入一個裝載了時間戳的 List 實例:
    
        TimestampsFilter(List<Long> timestamps)
        
    如前所述,一個版本(version) 是指一個列在一個特定時間的值,因此用一個時間戳(timestamp) 來表示。當過濾器請求一系列的時間戳時,它會找到與
    其中時間戳精確匹配的列版本。
    
    示例: Example filtering data by timestamps, sets up a filter with three timestamps and adds a time range to the second scan

    List<Long> ts = new ArrayList<Long>();
    //Add timestamps to the list.
    ts.add(new Long(5));
    ts.add(new Long(10));
    ts.add(new Long(15));
    Filter filter = new TimestampsFilter(ts);
    
    Scan scan1 = new Scan();
    //Add the filter to an otherwise default Scan instance.
    scan1.setFilter(filter);
    ResultScanner scanner1 = table.getScanner(scan1);
    for (Result result : scanner1) {
        System.out.println(result);
    }
    scanner1.close();
    
    Scan scan2 = new Scan();
    scan2.setFilter(filter);
    //Also add a time range to verify how it affects the filter
    scan2.setTimeRange(8, 12);
    ResultScanner scanner2 = table.getScanner(scan2);
    for (Result result : scanner2) {
        System.out.println(result);
    }
    scanner2.close();

    輸出:
    Adding rows to table...
    Results of scan #1:
    keyvalues={row-1/colfam1:col-10/10/Put/vlen=8/seqid=0,
    row-1/colfam1:col-15/15/Put/vlen=8/seqid=0,
    row-1/colfam1:col-5/5/Put/vlen=7/seqid=0}
    keyvalues={row-100/colfam1:col-10/10/Put/vlen=10/seqid=0,
    row-100/colfam1:col-15/15/Put/vlen=10/seqid=0,
    row-100/colfam1:col-5/5/Put/vlen=9/seqid=0}
    ...
    keyvalues={row-99/colfam1:col-10/10/Put/vlen=9/seqid=0,
    row-99/colfam1:col-15/15/Put/vlen=9/seqid=0,
    row-99/colfam1:col-5/5/Put/vlen=8/seqid=0}
    Results of scan #2:
    keyvalues={row-1/colfam1:col-10/10/Put/vlen=8/seqid=0}
    keyvalues={row-10/colfam1:col-10/10/Put/vlen=9/seqid=0}
    ...
    keyvalues={row-98/colfam1:col-10/10/Put/vlen=9/seqid=0}
    keyvalues={row-99/colfam1:col-10/10/Put/vlen=9/seqid=0}



    ■ 隨機行過濾器 (RandomRowFilter)
    -------------------------------------------------------------------------------------------------------------------------------------
    最後,有一種過濾器可以讓結果中包含隨機行。構造器需要傳入參數 chance, 取值在 0.0 到 1.0 之間。
    
        RandomRowFilter(float chance)
    
    在過濾器內部會使用 Java 的 Random.nextFloat() 調用來決定一行是否被過濾,使用這個方法的結果會與設定的 chance 進行比較。如果 chance 值
    爲負值會導致所有結果被過濾掉,相反,如果 chance 爲大於 1.0 則結果集中包含所有行。
    
    示例: Example filtering rows randomly
    
    Filter filter = new RandomRowFilter(0.5f);
    for (int loop = 1; loop <= 3; loop++) {
        Scan scan = new Scan();
        scan.setFilter(filter);
        ResultScanner scanner = table.getScanner(scan);
        for (Result result : scanner) {
            System.out.println(Bytes.toString(result.getRow()));
        }
        scanner.close();
    }

    輸出:
    Adding rows to table...
    Results of scan for loop: 1
    row-1
    row-10
    row-3
    row-9
    Results of scan for loop: 2
    row-10
    row-2
    row-3
    row-5
    row-6
    row-8
    Results of scan for loop: 3
    row-1
    row-3
    row-4
    row-8
    row-9



1.4 裝飾過濾器 (Decorating Filters)
-----------------------------------------------------------------------------------------------------------------------------------------
HBase 提供的過濾器已十分強大,有時需要對這些過濾器進行修改、或者擴展,以對過濾器的行爲進行更多地控制其返回結果非常有用。這些額外的控制不
依賴於過濾器本身,而是應用到過濾器上。這就是裝飾過濾器(decorating filter)這組類的作用。

裝飾過濾器和其它單一目的的過濾器類似,實現 Filter 接口。因此,它們可以用於這些過濾器的替代,配合它們所封裝的過濾器實例的行爲。


    ■ SkipFilter
    -------------------------------------------------------------------------------------------------------------------------------------
    這個過濾器封裝了一個給定的過濾器並擴展它,當封裝的過濾器建議某個 Cell 被忽略時,排除一整行。換句話說,只要一個過濾器指出某個列在其行中
    被忽略,則整個行就被忽略。
    
        
        NOTE:
        ---------------------------------------------------------------------------------------------------------------------------------    
        被封裝的過濾器必須實現 filterKeyValue() 方法,否則 SkipFilter 不會如期工作。這是因爲 SkipFilter 只檢查該方法的返回結果以決定如何
        處理當前的行。

    下面的示例配合使用帶有一個 ValueFilter 的 SkipFilter, 首先選擇所有非零值的列(column), 之後丟棄所有其它不含有某個匹配值的行。
    
    示例: Example of using a filter to skip entire rows based on another filter’s results
    
    Filter filter1 = new ValueFilter(CompareFilter.CompareOp.NOT_EQUAL, new BinaryComparator(Bytes.toBytes("val-0")));
    Scan scan = new Scan();
    //Only add the ValueFilter to the first scan.
    scan.setFilter(filter1);
    ResultScanner scanner1 = table.getScanner(scan);
    for (Result result : scanner1) {
        for (Cell cell : result.rawCells()) {
            System.out.println("Cell: " + cell + ", Value: " +
            Bytes.toString(cell.getValueArray(), cell.getValueOffset(),
            cell.getValueLength()));
        }
    }
    scanner1.close();

    //Add the decorating skip filter for the second scan.
    Filter filter2 = new SkipFilter(filter1);
    scan.setFilter(filter2);
    ResultScanner scanner2 = table.getScanner(scan);
    for (Result result : scanner2) {
        for (Cell cell : result.rawCells()) {
            System.out.println("Cell: " + cell + ", Value: " +
            Bytes.toString(cell.getValueArray(), cell.getValueOffset(),
            cell.getValueLength()));
        }
    }
    scanner2.close();

    輸出:
    Adding rows to table...
    Results of scan #1:
    Cell: row-01/colfam1:col-01/1/Put/vlen=5/seqid=0, Value: val-4
    Cell: row-01/colfam1:col-02/2/Put/vlen=5/seqid=0, Value: val-4
    Cell: row-01/colfam1:col-03/3/Put/vlen=5/seqid=0, Value: val-1
    Cell: row-01/colfam1:col-04/4/Put/vlen=5/seqid=0, Value: val-3
    Cell: row-01/colfam1:col-05/5/Put/vlen=5/seqid=0, Value: val-1
    Cell: row-02/colfam1:col-01/1/Put/vlen=5/seqid=0, Value: val-1
    Cell: row-02/colfam1:col-03/3/Put/vlen=5/seqid=0, Value: val-2
    Cell: row-02/colfam1:col-04/4/Put/vlen=5/seqid=0, Value: val-4
    Cell: row-02/colfam1:col-05/5/Put/vlen=5/seqid=0, Value: val-2
    ...
    Cell: row-30/colfam1:col-01/1/Put/vlen=5/seqid=0, Value: val-2
    Cell: row-30/colfam1:col-02/2/Put/vlen=5/seqid=0, Value: val-4
    Cell: row-30/colfam1:col-03/3/Put/vlen=5/seqid=0, Value: val-4
    Cell: row-30/colfam1:col-05/5/Put/vlen=5/seqid=0, Value: val-4
    Total cell count for scan #1: 124
    Results of scan #2:
    Cell: row-01/colfam1:col-01/1/Put/vlen=5/seqid=0, Value: val-4
    Cell: row-01/colfam1:col-02/2/Put/vlen=5/seqid=0, Value: val-4
    Cell: row-01/colfam1:col-03/3/Put/vlen=5/seqid=0, Value: val-1
    Cell: row-01/colfam1:col-04/4/Put/vlen=5/seqid=0, Value: val-3
    Cell: row-01/colfam1:col-05/5/Put/vlen=5/seqid=0, Value: val-1
    Cell: row-06/colfam1:col-01/1/Put/vlen=5/seqid=0, Value: val-4
    Cell: row-06/colfam1:col-02/2/Put/vlen=5/seqid=0, Value: val-4
    Cell: row-06/colfam1:col-03/3/Put/vlen=5/seqid=0, Value: val-4
    Cell: row-06/colfam1:col-04/4/Put/vlen=5/seqid=0, Value: val-3
    Cell: row-06/colfam1:col-05/5/Put/vlen=5/seqid=0, Value: val-2
    ...
    Cell: row-28/colfam1:col-01/1/Put/vlen=5/seqid=0, Value: val-2
    Cell: row-28/colfam1:col-02/2/Put/vlen=5/seqid=0, Value: val-1
    Cell: row-28/colfam1:col-03/3/Put/vlen=5/seqid=0, Value: val-2
    Cell: row-28/colfam1:col-04/4/Put/vlen=5/seqid=0, Value: val-4
    Cell: row-28/colfam1:col-05/5/Put/vlen=5/seqid=0, Value: val-2
    Total cell count for scan #2: 55


    
    ■ WhileMatchFilter
    -------------------------------------------------------------------------------------------------------------------------------------
    這個過濾器與上一個類似,不過當一條數據被過濾掉時,它會直接放棄本次掃描操作。它使用其封裝的過濾器來檢查它是否通過其 row key 來忽略一行,
    或者某個 Cell 的檢查忽略某個行的列。
    
    示例: Example of using a filter to skip entire rows based on another filter’s results

    Filter filter1 = new RowFilter(CompareFilter.CompareOp.NOT_EQUAL,
    new BinaryComparator(Bytes.toBytes("row-05")));
    Scan scan = new Scan();
    scan.setFilter(filter1);
    ResultScanner scanner1 = table.getScanner(scan);
    for (Result result : scanner1) {
        for (Cell cell : result.rawCells()) {
            System.out.println("Cell: " + cell + ", Value: " +
            Bytes.toString(cell.getValueArray(), cell.getValueOffset(),
            cell.getValueLength()));
        }
    }
    scanner1.close();
    Filter filter2 = new WhileMatchFilter(filter1);

    scan.setFilter(filter2);
    ResultScanner scanner2 = table.getScanner(scan);
    for (Result result : scanner2) {
        for (Cell cell : result.rawCells()) {
        System.out.println("Cell: " + cell + ", Value: " +
        Bytes.toString(cell.getValueArray(), cell.getValueOffset(),
        cell.getValueLength()));
        }
    }
    scanner2.close();

    輸出:
    Adding rows to table...
    Results of scan #1:
    Cell: row-01/colfam1:col-01/1/Put/vlen=9/seqid=0, Value: val-01.01
    Cell: row-02/colfam1:col-01/1/Put/vlen=9/seqid=0, Value: val-02.01
    Cell: row-03/colfam1:col-01/1/Put/vlen=9/seqid=0, Value: val-03.01
    Cell: row-04/colfam1:col-01/1/Put/vlen=9/seqid=0, Value: val-04.01
    Cell: row-06/colfam1:col-01/1/Put/vlen=9/seqid=0, Value: val-06.01
    Cell: row-07/colfam1:col-01/1/Put/vlen=9/seqid=0, Value: val-07.01
    Cell: row-08/colfam1:col-01/1/Put/vlen=9/seqid=0, Value: val-08.01
    Cell: row-09/colfam1:col-01/1/Put/vlen=9/seqid=0, Value: val-09.01
    Cell: row-10/colfam1:col-01/1/Put/vlen=9/seqid=0, Value: val-10.01
    Total cell count for scan #1: 9
    Results of scan #2:
    Cell: row-01/colfam1:col-01/1/Put/vlen=9/seqid=0, Value: val-01.01
    Cell: row-02/colfam1:col-01/1/Put/vlen=9/seqid=0, Value: val-02.01
    Cell: row-03/colfam1:col-01/1/Put/vlen=9/seqid=0, Value: val-03.01
    Cell: row-04/colfam1:col-01/1/Put/vlen=9/seqid=0, Value: val-04.01
    Total cell count for scan #2: 4



1.5 FilterList
-----------------------------------------------------------------------------------------------------------------------------------------
到目前爲止,已經看到了過濾器如何在一個表的各種維度上進行過濾工作,從行,到列,再到一個列的各種版本的值。在實際應用中,可能需要多個過濾器
共同限制返回到客戶端的結果,FilterList 提供了這項功能。構造器:

    FilterList(final List<Filter> rowFilters)
    FilterList(final Filter... rowFilters)
    FilterList(final Operator operator)
    FilterList(final Operator operator, final List<Filter> rowFilters)
    FilterList(final Operator operator, final Filter... rowFilters)


rowFilters 參數指定過濾器列表,用於一起訪問,使用 operator 來聯合它們的結果。可能的值如下表:

    Possible values for the FilterList.Operator enumeration
    +---------------+--------------------------------------------------------------------------------------------------
    | Operator        | Description
    +---------------+--------------------------------------------------------------------------------------------------
    | MUST_PASS_ALL    | A value is only included in the result when all filters agree to do so
    +---------------+--------------------------------------------------------------------------------------------------
    | MUST_PASS_ONE    | As soon as a value was allowed to pass one of the filters, it is included in the overall result.
    +---------------+--------------------------------------------------------------------------------------------------

默認值爲 MUST_PASS_ALL。

在 FilterList 實例已經創建之後向其添加過濾器,可以通過如下方法:

    void addFilter(Filter filter)
    
可以更進一步控制列表中所包含的過濾器執行的順序,選擇一個合適的 List 實現,例如,ArrayList 會保證過濾器的執行順序與它們加入列表的順序相同。

示例: Example of using a filter list to combine single purpose filters

    List<Filter> filters = new ArrayList<Filter>();

    Filter filter1 = new RowFilter(CompareFilter.CompareOp.GREATER_OR_EQUAL, new BinaryComparator(Bytes.toBytes("row-03")));
    filters.add(filter1);

    Filter filter2 = new RowFilter(CompareFilter.CompareOp.LESS_OR_EQUAL, new BinaryComparator(Bytes.toBytes("row-06")));
    filters.add(filter2);

    Filter filter3 = new QualifierFilter(CompareFilter.CompareOp. EQUAL, new RegexStringComparator("col-0[03]"));
    filters.add(filter3);

    FilterList filterList1 = new FilterList(filters);
    Scan scan = new Scan();
    scan.setFilter(filterList1);

    ResultScanner scanner1 = table.getScanner(scan);
    for (Result result : scanner1) {
        for (Cell cell : result.rawCells()) {
            System.out.println("Cell: " + cell + ", Value: " +
            Bytes.toString(cell.getValueArray(), cell.getValueOffset(),
            cell.getValueLength()));
        }
    }
    scanner1.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.rawCells()) {
            System.out.println("Cell: " + cell + ", Value: " +
            Bytes.toString(cell.getValueArray(), cell.getValueOffset(),
            cell.getValueLength()));
        }
    }
    scanner2.close();

輸出:

    Adding rows to table...
    Results of scan #1 - MUST_PASS_ALL:
    Cell: row-03/colfam1:col-03/3/Put/vlen=9/seqid=0, Value: val-03.03
    258 Chapter 4: Client API: Advanced Features
    Cell: row-04/colfam1:col-03/3/Put/vlen=9/seqid=0, Value: val-04.03
    Cell: row-05/colfam1:col-03/3/Put/vlen=9/seqid=0, Value: val-05.03
    Cell: row-06/colfam1:col-03/3/Put/vlen=9/seqid=0, Value: val-06.03
    Total cell count for scan #1: 4
    Results of scan #2 - MUST_PASS_ONE:
    Cell: row-01/colfam1:col-01/1/Put/vlen=9/seqid=0, Value: val-01.01
    Cell: row-01/colfam1:col-02/2/Put/vlen=9/seqid=0, Value: val-01.02
    ...
    Cell: row-10/colfam1:col-04/4/Put/vlen=9/seqid=0, Value: val-10.04
    Cell: row-10/colfam1:col-05/5/Put/vlen=9/seqid=0, Value: val-10.05
    Total cell count for scan #2: 50


1.6 自定義過濾器 (Custom Filters)
-----------------------------------------------------------------------------------------------------------------------------------------
用戶可能需要按各自的需求實現自定義過濾器,可以實現 Filter 接口,或者直接繼承 FilterBase 類,該類已經爲接口中所有成員方法提供了默認實現。
Filter 接口有如下結構:

    public abstract class Filter {
        public enum ReturnCode {
            INCLUDE, INCLUDE_AND_NEXT_COL, SKIP, NEXT_COL, NEXT_ROW,
            SEEK_NEXT_USING_HINT
        }
        
        public void reset() throws IOException
        public boolean filterRowKey(byte[] buffer, int offset, int length) throws IOException
        public boolean filterAllRemaining() throws IOException
        public ReturnCode filterKeyValue(final Cell v) throws IOException
        public Cell transformCell(final Cell v) throws IOException
        public void filterRowCells(List<Cell> kvs) throws IOException
        public boolean hasFilterRow()
        public boolean filterRow() throws IOException
        public Cell getNextCellHint(final Cell currentKV) throws IOException
        public boolean isFamilyEssential(byte[] name) throws IOException
        public void setReversed(boolean reversed)
        public boolean isReversed()
        public byte[] toByteArray() throws IOException
        public static Filter parseFrom(final byte[] pbBytes)
        throws DeserializationException
    }

接口提供了一個公共的枚舉類型 ReturnCode, 用於 filterKeyValue() 方法的返回值,指明執行框架下一步應進行的操作。過濾器可以選擇跳過一個值,
一個列的剩餘部分或一行的剩餘部分,而不是遍歷所有的數據。因此獲取數據的效率會大大提升。

    Possible values for the Filter.ReturnCode enumeration
    +-----------------------+--------------------------------------------------------------------------------------------------------------
    | Return code            | Description
    +-----------------------+--------------------------------------------------------------------------------------------------------------
    | INCLUDE                | Include the given Cell instance in the result
    +-----------------------+--------------------------------------------------------------------------------------------------------------
    | INCLUDE_AND_NEXT_COL    | Include current cell and move to next column, i.e. skip all further versions of the current.
    +-----------------------+--------------------------------------------------------------------------------------------------------------
    | SKIP                    | Skip the current cell and proceed to the next.
    +-----------------------+--------------------------------------------------------------------------------------------------------------
    | NEXT_COL                | Skip the remainder of the current column, proceeding to the next. This is used by the TimestampsFilter
    +-----------------------+--------------------------------------------------------------------------------------------------------------
    | NEXT_ROW                | Similar to the previous, but skips the remainder of the current row, moving to the next.
    |                        | The RowFilter makes use of this return code, for example.
    +-----------------------+--------------------------------------------------------------------------------------------------------------
    | SEEK_NEXT_USING_HINT    | Some filters want to skip a variable number of cells and use this return code to indicate that the framework
    |                        | should use the getNextCellHint() method to determine where to skip to. The ColumnPrefixFilter, for example,
    |                        | uses this feature.
    +-----------------------+--------------------------------------------------------------------------------------------------------------


示例: Implements a filter that lets certain rows pass

    public class CustomFilter extends FilterBase {
        
        private byte[] value = null;
        private boolean filterRow = true;
        
        public CustomFilter() {
            super();
        }
        
        public CustomFilter(byte[] value) {
            this.value = value;
        }
        
        @Override
        public void reset() {
            this.filterRow = true;
        }
        
        @Override
        public ReturnCode filterKeyValue(Cell cell) {
            if (CellUtil.matchingValue(cell, value)) {
                filterRow = false;
            }
            return ReturnCode.INCLUDE;
        }
        
        @Override
        public boolean filterRow() {
            return filterRow;
        }
        
        @Override
        public byte [] toByteArray() {
            FilterProtos.CustomFilter.Builder builder =
            FilterProtos.CustomFilter.newBuilder();
            if (value != null) builder.setValue(ByteStringer.wrap(value));
            return builder.build().toByteArray();
        }
        
        //@Override
        public static Filter parseFrom(final byte[] pbBytes) throws DeserializationException {
            FilterProtos.CustomFilter proto;
            
            try {
                proto = FilterProtos.CustomFilter.parseFrom(pbBytes);
                
            } catch (InvalidProtocolBufferException e) {
                throw new DeserializationException(e);
            }
            
            return new CustomFilter(proto.getValue().toByteArray());
        }
    }

    //Example using a custom filter
    List<Filter> filters = new ArrayList<Filter>();
    Filter filter1 = new CustomFilter(Bytes.toBytes("val-05.05"));
    filters.add(filter1);
    Filter filter2 = new CustomFilter(Bytes.toBytes("val-02.07"));
    filters.add(filter2);
    Filter filter3 = new CustomFilter(Bytes.toBytes("val-09.01"));
    filters.add(filter3);
    
    FilterList filterList = new FilterList(
    FilterList.Operator.MUST_PASS_ONE, filters);
    
    Scan scan = new Scan();
    scan.setFilter(filterList);
    
    ResultScanner scanner = table.getScanner(scan);
    for (Result result : scanner) {
        for (Cell cell : result.rawCells()) {
            System.out.println("Cell: " + cell + ", Value: " +
            Bytes.toString(cell.getValueArray(), cell.getValueOffset(),
            cell.getValueLength()));
        }
    }
    scanner.close();

輸出:
    Adding rows to table...
    Results of scan:
    Cell: row-02/colfam1:col-01/1/Put/vlen=9/seqid=0, Value: val-02.01
    Cell: row-02/colfam1:col-02/2/Put/vlen=9/seqid=0, Value: val-02.02
    ...
    Cell: row-02/colfam1:col-06/6/Put/vlen=9/seqid=0, Value: val-02.06
    Cell: row-02/colfam1:col-07/7/Put/vlen=9/seqid=0, Value: val-02.07
    Cell: row-02/colfam1:col-08/8/Put/vlen=9/seqid=0, Value: val-02.08
    ...
    Cell: row-05/colfam1:col-04/4/Put/vlen=9/seqid=0, Value: val-05.04
    Cell: row-05/colfam1:col-05/5/Put/vlen=9/seqid=0, Value: val-05.05
    Cell: row-05/colfam1:col-06/6/Put/vlen=9/seqid=0, Value: val-05.06
    ...
    Cell: row-05/colfam1:col-10/10/Put/vlen=9/seqid=0, Value: val-05.10
    Cell: row-09/colfam1:col-01/1/Put/vlen=9/seqid=0, Value: val-09.01
    Cell: row-09/colfam1:col-02/2/Put/vlen=9/seqid=0, Value: val-09.02
    ...
    Cell: row-09/colfam1:col-09/9/Put/vlen=9/seqid=0, Value: val-09.09
    Cell: row-09/colfam1:col-10/10/Put/vlen=9/seqid=0, Value: val-09.10


    自定義過濾器載入 (Custom Filter Loading)
    -------------------------------------------------------------------------------------------------------------------------------------
    完成過濾器的編寫之後,需要將其部署到 HBase 上。首先需要編譯好過濾器類,然後打包成 JAR, 並保證可以被 region 服務器調用。
    
    可以使用編譯系統來準備配置用的 JAR 文件,同時使用配置管理系統把文件分發到每個 region 服務器中。文件分發完成後,有兩種選擇載入 JAR 文件

    靜態配置 (Static Configuration):
        這種情況下,需要將 JAR 文件配置到 hbase-env.sh 文件中,如:
        # Extra Java CLASSPATH elements. Optional.
        # export HBASE_CLASSPATH=
        export HBASE_CLASSPATH="/hbase-book/ch04/target/hbase-bookch04-2.0.jar    
            
    動態載入 (Dynamic Loading):
        利用集羣範圍的,在 HDFS 中共享 JAR 文件目錄來載入 JAR 文件。hbase-default.xml 文件中有如下屬性設置:

        <property>
            <name>hbase.dynamic.jars.dir</name>
            <value>${hbase.rootdir}/lib</value>
        </property>

        默認指向 ${hbase.rootdir}/lib,通常在 HDFS 中解析爲 /hbase/lib/ . 完整路徑類似爲:hdfs://master.foobar.com:9000/hbase/lib , 如果該
        目錄存在,並且含有以 .jar 結尾的文件,則服務器會載入這些文件,並使其中包含的類可用。


1.7 過濾器解析工具 (Filter Parser Utility)
-----------------------------------------------------------------------------------------------------------------------------------------
客戶端過濾器包是另一個輔助類,名爲 ParseFilter. 它用於所有過濾器需要描述爲文本的地方,最終轉換爲一個 Java class。一般出現在網關服務器上(
gateway servers), 如 REST or Thrift. HBase Shell 也使用這個類允許 shell 用戶則命令行上指定一個過濾器,然後作爲後續掃描或者 get 操作的一部分
執行過濾器,例如:

    hbase(main):001:0> scan 'testtable', { FILTER => "PrefixFilter('row-2') AND QualifierFilter(<=,'binary:col-2')" }

輸出:
    ROW COLUMN+CELL
    row-20 column=colfam1:col-0, timestamp=7, value=val-46
    row-21 column=colfam1:col-0, timestamp=7, value=val-87
    row-21 column=colfam1:col-2, timestamp=5, value=val-26
    ...
    row-28 column=colfam1:col-2, timestamp=3, value=val-74
    row-29 column=colfam1:col-1, timestamp=0, value=val-86
    row-29 column=colfam1:col-2, timestamp=3, value=val-21
    10 row(s) in 0.0170 seconds

"binary:col-2" 參數。冒號分隔的第二部分是過濾器處理的值。第一部分是過濾器解析器類允許爲過濾器指定的一個基於 CompareFilter 的比較器。支持的
比較器前綴如下表:

        String representation of Comparator types
        +---------------+----------------------------
        | String        | Type
        +---------------+----------------------------
        | binary        | BinaryComparator
        +---------------+----------------------------
        | binaryprefix    | BinaryPrefixComparator
        +---------------+----------------------------
        | regexstring    | RegexStringComparator
        +---------------+----------------------------
        | substring        | SubstringComparator
        +---------------+----------------------------

由於比較過濾器也需要一個比較操作,因此有字符串格式的表示方法。例如上例子中的 "<=" 表示小於或等於。下表列出可用的字符串表示的操作符類型:

    
        String representation of compare operation
        +---------------+----------------------------
        | String        | Type
        +---------------+----------------------------
        | <                | CompareOp.LESS
        +---------------+----------------------------
        | <=            | CompareOp.LESS_OR_EQUAL
        +---------------+----------------------------
        | >                | CompareOp.GREATER
        +---------------+----------------------------
        | >=            | CompareOp.GREATER_OR_EQUAL
        +---------------+----------------------------
        | =                | CompareOp.EQUAL
        +---------------+----------------------------
        | !=            | CompareOp.NOT_EQUAL
        +---------------+----------------------------

過濾器解析器支持一些基於 token 的文本翻譯成過濾器類。可以配合過濾器使用 AND 和 OR 關鍵字,會被之後翻譯爲 FilterList 實例,或者設置爲
MUST_PASS_ALL, 或者 MUST_PASS_ONE

示例:
    hbase(main):001:0> scan 'testtable',{ FILTER => "(PrefixFilter('row-2') AND (QualifierFilter(>=, 'binary:col-2'))) AND (TimestampsFilter(1,
    5))" }

輸出:
    ROW COLUMN+CELL
    row-2 column=colfam1:col-9, timestamp=5, value=val-31
    row-21 column=colfam1:col-2, timestamp=5, value=val-26
    row-23 column=colfam1:col-5, timestamp=5, value=val-55
    row-28 column=colfam1:col-5, timestamp=1, value=val-54
    4 row(s) in 0.3190 seconds

最後,可以使用 SKIP 和 WHILE 關鍵字表示 SkipFilter 和 WhileMatchFilter.

示例:
    hbase(main):001:0> scan 'testtable',{ FILTER => "SKIP ValueFilter(>=, 'binary:val-5') " }

輸出:
    ROW COLUMN+CELL
    row-11 column=colfam1:col-0, timestamp=8, value=val-82
    row-48 column=colfam1:col-3, timestamp=6, value=val-55
    row-48 column=colfam1:col-7, timestamp=3, value=val-80
    row-48 column=colfam1:col-8, timestamp=2, value=val-65
    row-7 column=colfam1:col-9, timestamp=6, value=val-57
    3 row(s) in 0.0150 seconds


    Precedence of string keywords
    +---------------+-------------------------------------------------------------------------------------------
    | Keyword        | Description
    +---------------+-------------------------------------------------------------------------------------------
    | SKIP/WHILE    | Wrap filter into SkipFilter, or WhileMatchFilter instance.
    +---------------+-------------------------------------------------------------------------------------------
    | AND            | Add both filters left and right of keyword to FilterList instance using MUST_PASS_ALL.
    +---------------+-------------------------------------------------------------------------------------------
    | OR            | Add both filters left and right of keyword to FilterList instance using MUST_PASS_ONE.
    +---------------+-------------------------------------------------------------------------------------------

代碼中,可以調用如下方法來解析字符串爲類實例:

    Filter parseFilterString(String filterString) throws CharacterCodingException
    Filter parseFilterString (byte[] filterStringAsByteArray) throws CharacterCodingException
    Filter parseSimpleFilterExpression(byte[] filterStringAsByteArray) throws CharacterCodingException

ParseFilter 默認情況下只支持 HBase 自帶過濾器的解析,不支持 FirstKeyValueMatchingQualifiersFilter, FuzzyRowFilter, and RandomRowFilter

在自己的代碼中,可以註冊自定義過濾器,並獲取支持的過濾器列表,使用如下方法:

    static Map<String, String> getAllFilters()
    Set<String> getSupportedFilters()

    static void registerFilter(String name, String filterClass)

 

本篇完

 

參考:

    HBase 過濾器 (一)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章