/**
* Copyright 2008 The Apache Software Foundation
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package meetup.beeno.filter;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import org.apache.hadoop.hbase.KeyValue;
import org.apache.hadoop.hbase.util.Bytes;
import org.apache.hadoop.hbase.filter.Filter;
/**
* This filter is used to filter based on the value of a given column. It takes
* an operator (equal, greater, not equal, etc) and either a byte [] value or a
* byte [] comparator. If we have a byte [] value then we just do a
* lexicographic compare. If this is not sufficient (eg you want to deserialize
* a long and then compare it to a fixed long value, then you can pass in your
* own comparator instead.
*/
public class ColumnMatchFilter implements Filter {
/** Comparison operators. */
public enum CompareOp {
/** less than */
LESS,
/** less than or equal to */
LESS_OR_EQUAL,
/** equals */
EQUAL,
/** not equal */
NOT_EQUAL,
/** greater than or equal to */
GREATER_OR_EQUAL,
/** greater than */
GREATER;
}
private byte[] columnName;
private CompareOp compareOp;
private byte[] value;
private boolean filterIfColumnMissing;
private boolean columnSeen = false;
private boolean columnFiltered = false;
public ColumnMatchFilter() {
// for Writable
}
/**
* Constructor.
*
* @param columnName
* name of column
* @param compareOp
* operator
* @param value
* value to compare column values against
*/
public ColumnMatchFilter( final byte[] columnName, final CompareOp compareOp,
final byte[] value ) {
this(columnName, compareOp, value, true);
}
/**
* Constructor.
*
* @param columnName
* name of column
* @param compareOp
* operator
* @param value
* value to compare column values against
* @param filterIfColumnMissing
* if true then we will filter rows that don't have the column.
*/
public ColumnMatchFilter( final byte[] columnName, final CompareOp compareOp,
final byte[] value, boolean filterIfColumnMissing ) {
this.columnName = columnName;
this.compareOp = compareOp;
this.value = value;
this.filterIfColumnMissing = filterIfColumnMissing;
}
/**
* Constructor.
*
* @param columnName
* name of column
* @param compareOp
* operator
* @param comparator
* Comparator to use.
*/
public ColumnMatchFilter( final byte[] columnName, final CompareOp compareOp ) {
this(columnName, compareOp, true);
}
/**
* Constructor.
*
* @param columnName
* name of column
* @param compareOp
* operator
* @param comparator
* Comparator to use.
* @param filterIfColumnMissing
* if true then we will filter rows that don't have the column.
*/
public ColumnMatchFilter( final byte[] columnName, final CompareOp compareOp,
boolean filterIfColumnMissing ) {
this.columnName = columnName;
this.compareOp = compareOp;
this.filterIfColumnMissing = filterIfColumnMissing;
}
public boolean filterRowKey( final byte[] rowKey, int offset, int length ) {
return false;
}
public Filter.ReturnCode filterKeyValue( KeyValue v ) {
if (v.matchingColumn(this.columnName)) {
byte[] val = v.getValue();
if (val != null && val.length > 0)
this.columnSeen = true;
if (filterColumnValue(v.getValue())) {
this.columnFiltered = true;
return Filter.ReturnCode.NEXT_ROW;
}
}
return Filter.ReturnCode.INCLUDE;
}
private boolean filterColumnValue( final byte[] data ) {
int compareResult;
compareResult = compare(value, data);
switch (compareOp) {
case LESS:
return compareResult <= 0;
case LESS_OR_EQUAL:
return compareResult < 0;
case EQUAL:
return compareResult != 0;
case NOT_EQUAL:
return compareResult == 0;
case GREATER_OR_EQUAL:
return compareResult > 0;
case GREATER:
return compareResult >= 0;
default:
throw new RuntimeException("Unknown Compare op " + compareOp.name());
}
}
public boolean filterAllRemaining() {
return false;
}
public boolean filterRow() {
if (columnFiltered)
return true;
if (filterIfColumnMissing && !columnSeen)
return true;
return false;
}
private int compare( final byte[] b1, final byte[] b2 ) {
int len = Math.min(b1.length, b2.length);
for (int i = 0; i < len; i++) {
if (b1[i] != b2[i]) {
return b1[i] - b2[i];
}
}
return b1.length - b2.length;
}
public void reset() {
this.columnSeen = false;
this.columnFiltered = false;
}
public void readFields( final DataInput in ) throws IOException {
int valueLen = in.readInt();
if (valueLen > 0) {
value = new byte[valueLen];
in.readFully(value);
}
columnName = Bytes.readByteArray(in);
compareOp = CompareOp.valueOf(in.readUTF());
filterIfColumnMissing = in.readBoolean();
}
public void write( final DataOutput out ) throws IOException {
if (value == null) {
out.writeInt(0);
}
else {
out.writeInt(value.length);
out.write(value);
}
Bytes.writeByteArray(out, columnName);
out.writeUTF(compareOp.name());
out.writeBoolean(filterIfColumnMissing);
}
}