HBaseのJava APIのFilter系クラスは、ScanやGetにおける条件を指定するクラス。
Filter自身はインターフェースで、色々な具象クラスが存在する。
|
Filterのインスタンスを作り、条件を指定する。
そしてScanにセットしてHTable#getScanner()でデータ取得を実行する。
後はScanの使用方法と同様。
import org.apache.hadoop.hbase.KeyValue; 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.Filter;
Filter filter = new Filterの具象クラス(〜);
Scan scan = new Scan();
scan.setFilter(filter);
// byte[] startRow = HConstants.EMPTY_START_ROW; //なるべく検索範囲に近い値を指定する方が良い
// Scan scan = new Scan(startRow, filter);
ResultScanner rs = table.getScanner(scan);
try {
〜
} finally {
rs.close();
}
Getに対してFilterをセットすることも出来る。が、Get自体をサーバー側ではScanに置き換えて使っているっぽい。
Get get = new Get(row); get.setFilter(filter); Result r = table.get(get);
↓HRegionクラス内
private List<KeyValue> get(final Get get) throws IOException {
Scan scan = new Scan(get);
〜
値を条件とするフィルター。
| フィルター条件 | コーディング例 | 備考 |
|---|---|---|
| 値指定 |
BinaryComparator comparator = new BinaryComparator(Bytes.toBytes("value1")); Filter filter = new ValueFilter(CompareOp.EQUAL, comparator); |
値が条件に合致するカラムを返す。 |
| カラムの値を指定 |
byte[] f = Bytes.toBytes("ColumnFamilyA");
byte[] q = Bytes.toBytes("Column1");
byte[] v = Bytes.toBytes("value1");
SingleColumnValueFilter filter = new SingleColumnValueFilter(
f, q, CompareOp.EQUAL, v
);
filter.setFilterIfMissing(true);
byte[] f = Bytes.toBytes("ColumnFamilyA");
byte[] q = Bytes.toBytes("Column1");
BinaryComparator comparator =
new BinaryComparator(Bytes.toBytes("value1"));
SingleColumnValueFilter filter = new SingleColumnValueFilter(
f, q, CompareOp.EQUAL, comparator
);
filter.setFilterIfMissing(true);
|
指定したカラムの値が指定した条件に合致している場合に、その行(ROW)の全カラムを返す。 filterIfMissingがfalse(デフォルト)だと、指定カラムが存在しない行(ROW)も返る。 SQLの「 select * from テーブル where "ColumnFamilyA:Column1" = 'value1'」に相当。 |
| カラムの値を指定 (指定したカラムは除外) |
byte[] f = Bytes.toBytes("ColumnFamilyA");
byte[] q = Bytes.toBytes("Column1");
byte[] v = Bytes.toBytes("value1");
SingleColumnValueExcludeFilter filter =
new SingleColumnValueExcludeFilter(
f, q, CompareOp.EQUAL, v
);
filter.setFilterIfMissing(true);
|
条件の判定方法はSingleColumnValueFilterと全く同じだが、 判定に使ったカラム(コンストラクターで指定したカラム)は返されない点だけが異なる。 HBase0.89以降 |
行キーや行数を条件とするフィルター。
| フィルター条件 | コーディング例 | 備考 |
|---|---|---|
| 行キー指定 |
BinaryComparator comparator = new BinaryComparator(Bytes.toBytes("KEY00002")); Filter filter = new RowFilter(CompareOp.EQUAL, comparator); |
行キー(ROW)が条件に合致するカラムを返す。 (キーの範囲指定であれば、Scanで指定する方がいいのではないかと思う) SQLの「 select * from テーブル where ROW='KEY00002'」に相当。 |
| 行キー接頭辞指定 |
byte[] prefix = Bytes.toBytes("KEY");
Filter filter = new PrefixFilter(prefix);
|
行キーの先頭部分が一致する行(ROW)の全カラムを返す。 SQLの「 select * from テーブル where ROW like'KEY%'」に相当。 |
| 最終行指定 |
byte[] stop = Bytes.toBytes("KEY00003");
Filter filter = new InclusiveStopFilter(stop);
|
指定された行キー以前の行(ROW)の全カラムを返す。 Scan#setStopRow()との違いは、 setStopRow()は指定キーは含まないのに対し、 InclusiveStopFilterは指定キーも含むこと。 SQLの「 select * from テーブル where ROW<='KEY00003'」に相当。 |
| 取得行数指定 |
long pageSize = 10; //N Filter filter = new PageFilter(pageSize); |
最大N行分の全カラムを返す。 (ColumnPaginationFilterは行毎のカラム数を指定するものであり、 当フィルターは行数を指定するところが異なる) SQLの「 select * from テーブル limit 10」に相当。 |
カラム名やカラム数を条件とするフィルター。
| フィルター条件 | コーディング例 | 備考 |
|---|---|---|
| qualifier指定 |
BinaryComparator comparator = new BinaryComparator(Bytes.toBytes("Column2")); Filter filter = new QualifierFilter(CompareOp.EQUAL, comparator); |
qualifier名(カラム名からファミリー名を除いた部分)が条件に合致するカラムを返す。 qualiferが無いカラムも含まれる。 (カラム名そのものを指定したい場合はScan#addColumn()を使う。 ファミリー名を指定したい場合はScan#addFamily()を使う) |
| 行毎の先頭カラム取得 |
Filter filter = new FirstKeyOnlyFilter(); |
各行(ROW)において、先頭のカラムを返す。 (1行につき1個) |
| 行毎のスキップ・取得カラム数指定 |
int limit = 2; //N int offset = 1; //M Filter filter = new ColumnPaginationFilter(limit, offset); |
各行(ROW)において、先頭M個を除いた最大N個のカラムを返す。 HBase0.89以降 |
| 行内の取得カラム数指定 |
int limit = 3; //N Filter filter = new ColumnCountGetFilter(limit); |
一行(ROW)だけを指定する前提で、その行の先頭N個のカラムを返す。 つまりGet#setFilter()で使用する想定であり、Scan#setFilter()で使うのは不適切。 (Scanの場合はColumnPaginationFilterを使う) |
| タイムスタンプ依存 |
byte[] f = Bytes.toBytes("ColumnFamilyA");
byte[] q = Bytes.toBytes("Column1");
Filter filter = new DependentColumnFilter(f, q);
|
各行(ROW)において、指定したカラムとタイムスタンプが一致するカラムを返す。 HBase0.89以降 左記の例の場合、同じ行(ROW)内で「 ColumnFamilyA:Column1」とタイムスタンプが一致するカラムだけを返す。 |
byte[] f = Bytes.toBytes("ColumnFamilyA");
byte[] q = Bytes.toBytes("Column1");
Filter filter = new DependentColumnFilter(f, q, true);
|
dropDependentColumnにtrueを指定すると、 指定したカラム自身は除外される(返されない)。 デフォルトはfalse。 |
|
byte[] f = Bytes.toBytes("ColumnFamilyA");
byte[] q = Bytes.toBytes("Column1");
BinaryComparator comparator =
new BinaryComparator(Bytes.toBytes("value1"));
Filter filter = new DependentColumnFilter(
f, q, false, CompareOp.EQUAL, comparator
);
|
指定したカラムが指定した値の条件に合致する場合だけ判定される。 左記の例の場合、「 ColumnFamilyA:Column1」の値が「value1」の行(ROW)において、そのタイムスタンプと一致するカラムだけを返す。 |
|
| タイムスタンプ指定 |
List<Long> list = new ArrayList<Long>(); list.add(1280582757890L); list.add(1280586240375L); list.add(1280586245515L); Filter filter = new TimestampsFilter(list); |
指定されたタイムスタンプと一致するデータを返す。 ひとつのカラムで複数の履歴が一致する場合、その中の最新データのみを返すようだ。 HBase0.89.20100726以降 |
他のフィルターをラップして条件を変更するフィルター。
| フィルター条件 | コーディング例 | 備考 |
|---|---|---|
| FilterList | 複数のフィルター条件を組み合わせたひとつのフィルターを形成する。 | |
FilterList filter = new FilterList(FilterList.Operator.MUST_PASS_ALL); filter.addFilter( new ValueFilter( CompareOp.GREATER_OR_EQUAL, new BinaryComparator(Bytes.toBytes("a")) ) ); filter.addFilter( new ValueFilter( CompareOp.LESS_OR_EQUAL, new BinaryComparator(Bytes.toBytes("z")) ) ); |
全てのフィルターを満たす場合にカラムを返す。 いわばAND条件。 SQLの「 select * from テーブル where 'a'<=値 AND 値<='z'」に相当。 |
|
FilterList filter = new FilterList(FilterList.Operator.MUST_PASS_ONE); filter.addFilter( new ValueFilter( CompareOp.LESS, new BinaryComparator(Bytes.toBytes("a")) ) ); filter.addFilter( new ValueFilter( CompareOp.GREATER, new BinaryComparator(Bytes.toBytes("zzz")) ) ); |
ひとつでも条件を満たす場合にカラムを返す。 いわばOR条件。 SQLの「 select * from テーブル where 値<'a' OR 値>'zzz'」に相当。 |
|
| SkipFilter |
Filter f = new ValueFilter( CompareOp.EQUAL, new BinaryComparator(Bytes.toBytes("zzz")) ); Filter filter = new SkipFilter(f); Scan scan = new Scan(); scan.setFilter(filter); scan.addColumn(Bytes.toBytes("ColumnFamilyA"), Bytes.toBytes("Column1")); scan.addColumn(Bytes.toBytes("ColumnFamilyA"), Bytes.toBytes("Column2")); scan.addColumn(Bytes.toBytes("ColumnFamilyA"), Bytes.toBytes("Column3")); |
行(ROW)内の(指定された)カラムが別フィルター条件を満たす場合にそれらのカラムを返す。 ひとつでも条件を満たさないカラムがあったら、その行をスキップする。 たぶん、Scan#addFamily()やaddColumn()と組み合わせて、 それらのカラム全てが初期値であるようなレコードを探す為に使うのだろう。 左記の例の場合、指定された全てのカラムの値が「zzz」の場合にそれらのカラムを返す。 SQLの「 select * from テーブル where "ColumnFamilyA:Column1"='zzz' AND "ColumnFamilyA:Column2"='zzz' AND "ColumnFamilyA:Column3"='zzz'」に相当。ただし、カラムそのものが存在しない場合は、そのカラムの値は'zzz'に等しいものとして扱われる。 |
| WhileMatchFilter |
Filter f = new ValueFilter( CompareOp.NOT_EQUAL, new BinaryComparator(Bytes.toBytes("zzz")) ); Filter filter = new WhileMatchFilter(f); |
別フィルターの条件を満たす間、それらのカラムを返す。 一度でも条件外になったら、それ以降の全カラムをスキップする。 左記の例の場合、'zzz'以外の値である間は出力される。 逆に言うと、'zzz'が来るまで出力される。 「'zzz'と等しい」というのが終了条件と言える。 |
CompareFilter系クラス(ValueFilterやRowFilter等)やSingleColumnValueFilter等では、値の比較条件を指定することが出来る。
それらのフィルターでは、
comparator(WritableByteArrayComparableの具象クラス)で大小関係の算出方法と比較する固定値を指定し、
CompareFilter$CompareOp(比較演算子・コンペアオペレーター)で比較方法を指定する。
comparatorの種類によっては、指定できないCompareOpもある。
例えばSubstringComparatorは条件に合致したかしないかの二種類しか返さない(compareTo()が0・1しか返さず、-1を返す事が無い)ので、EQUAL・NOT_EQUAL以外を指定した場合の動作は不定。
| CompareOp | 備考 |
|---|---|
| LESS | 値<指定条件 |
| LESS_OR_EQUAL | 値≦指定条件 |
| EQUAL | 値=指定条件 |
| NOT_EQUAL | 値≠指定条件 |
| GREATER_OR_EQUAL | 値≧指定条件 |
| GREATER | 値>指定条件 |
| NO_OP | HBase0.89以降。何らかの演算をするわけではなく、 「何もしない」という値を(デフォルト値として)指定する為に使用する。 |
| comparator | 備考 | 例 | |
|---|---|---|---|
| バイナリー比較 | バイナリー(バイト列)で値全体を比較する。 |
new ValueFilter( CompareOp.LESS, new BinaryComparator(Bytes.toBytes("a")) ); |
「a」より小さい値 のカラムを返す。 |
| 接頭辞比較 | 値の接頭辞(先頭)部分をバイナリー(バイト列)で比較する。 |
new QualifierFilter( CompareOp.NOT_EQUAL, new BinaryPrefixComparator(Bytes.toBytes("Column")) ); |
qualifierの先頭が「Column」 でないカラムを返す。 |
| 文字列包含 | 値をString(英小文字)に変換して、文字列が含まれるかどうかを判定する。 大小比較不可。 |
new RowFilter( CompareOp.EQUAL, new SubstringComparator("KEY1") ); |
行キーに「KEY1」が含まれている カラムを返す。 |
| 正規表現 | 値をStringに変換して、パターン(正規表現)が含まれるかどうかを判定する。 大小比較不可。 Stringに変換する際のエンコードは別途指定可能。デフォルトではUTF8。 |
new RowFilter( CompareOp.EQUAL, new RegexStringComparator("^KEY[12]") ); |
行キーが「KEY1」または「KEY2」で始まっている カラムを返す。 |
スキャンによる検索は、サーバー側のRegionScanner#nextInternal()でデータを取得し、Filterの各メソッドを呼び出してフィルタリングしている。
Fitler#filterXXX()メソッドは、trueを返すとフィルターする。つまり該当データを除去する。
resultsはRegionScanner内のList<KeyValue>型の変数であり、フィルターされずに残ったデータを保持する。
| RegionScannerの処理内容 | Filterのメソッド | Filterメソッドの返り値 | Filterメソッドの目的 | ||
|---|---|---|---|---|---|
| 行初期処理 | 1 | (次レコードの)行キーを取得する。 | |||
| 2 | Scanのストップキー条件の判定を行う。 ストップする場合、行終了処理を行う。 |
||||
| 3 | 行キーを条件としてフィルターするかどうか判定する。 | filterRowKey(行キー) | trueを返すと、次の行へスキップする。 | そのキー(ROW)を処理するかどうかを判定する。 | |
| 4 | (カラムのデータ(KeyValue)を取得する前に) スキャンを終了するかどうか判定する。 |
filterAllRemaining() | trueを返すと、スキャンを終了する。 | 条件に応じてスキャン自体を終了させる。 | |
| KeyValue処理 | 5 | カラムのデータ(KeyValue)を取得し、 KeyValueを条件としてフィルターするかどうか判定する。 有効なKeyValueはresultsに追加される。 |
filterKeyValue(kv) | INCLUDEを返すとresultsにKeyValueが追加される。 SKIPを返すとそのデータはスキップされる。 NEXT_ROWを返すと、次の行へスキップする。 NEXT_COLを返すと次のカラムにスキップする。[2010-08-15] (HBase0.89.20100726以降) |
データ(KeyValue)をフィルターするかどうか判定する。 |
| 6 | resultsの個数がlimit(Scan#setBatch()でセットした値)に達するか その行のデータがなくなるまで繰り返す。 |
||||
| 行終了処理 | 7 | (一行分のデータを取得した後の処理) | hasFilterRow() HBase0.89以降 |
trueを返すと、filterRow(results)が呼ばれる。 | filterRow(results)を呼び出すかどうかを指定する。 実行時の状態に関わらず、“当クラスはfilterRow(results)を使うかどうか”でtrue/falseを返す必要がある。 (このメソッドがtrueを返す場合、Scan#setBatch()との併用は不可となる) |
| filterRow(results) HBase0.89以降 |
条件・状態に応じて、不要なデータ(KeyValue)をresultsから削除する。 | ||||
| 8 | resultsにデータが入っている場合に、それらのデータをフィルターするかどうか判定する。 | filterRow() | trueを返すとresultsをクリアーする。 | 行全体(resultsに入っているデータ)が有効かどうかを最終的に判定する。 | |
| 9 | (resultsが空でない場合、)クライアントへresultsを返す。 | ||||
| 行スキップ処理 | a | (次の行へスキップする際の処理) resultsをクリアーする。 |
|||
| b | フィルターのリセットを行う。 | reset() | 行(ROW)毎に、フィルター内の状態の初期化を行う。 | ||
| c | 行初期処理へ戻る。 | ||||
検索を終了する(スキップする)判定は随所にある(スキャン自体を終了させる方法もある)が、それまでの間は、データを先頭から全て調査していくことになる。
したがって、検索条件に合致するデータがなるべく近くなるように、検索範囲を絞っておく(検索開始行(startRow)をScanで指定しておく)のが良さそう。
ScanのFilterを指定するコンストラクターでstartRowを一緒に指定するようになっている(Filterのみを指定するコンストラクターが無い)のは、そういう意図がこめられているのではないかと思う。
Filterインターフェースを実装(あるいはFilterBase(HBase0.89以降)を継承/CompareFilterを拡張)して、自作フィルターを作れる。
が、フィルター処理はHBaseのサーバー側で行われる点に留意する必要がある。
つまり、自作したクラス(の入ったjarファイル)を、HBaseがインストールされている全ノードにも配置しなければならない。
HBaseで提供されているフィルター等のクラスについてはHbaseObjectWritableクラスの中で型をキャッシュしていたりするのだが、
Scanクラスのシリアライゼーション(write()・readFields())でフィルターのクラス名も受け渡しているので、
自作クラスではHbaseObjectWritableを気にする必要は無い。