HBASE-19950 Introduce a ColumnValueFilter

Signed-off-by: Chia-Ping Tsai <chia7712@gmail.com>
This commit is contained in:
Reid Chan 2018-02-13 16:01:28 +08:00 committed by Chia-Ping Tsai
parent a458d7c400
commit 4ef6319af0
8 changed files with 440 additions and 6 deletions

View File

@ -0,0 +1,241 @@
/**
*
* 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 org.apache.hadoop.hbase.filter;
import java.io.IOException;
import java.util.ArrayList;
import org.apache.hadoop.hbase.Cell;
import org.apache.hadoop.hbase.CellUtil;
import org.apache.hadoop.hbase.CompareOperator;
import org.apache.hadoop.hbase.PrivateCellUtil;
import org.apache.hadoop.hbase.exceptions.DeserializationException;
import org.apache.hadoop.hbase.util.Bytes;
import org.apache.yetus.audience.InterfaceAudience;
import org.apache.hbase.thirdparty.com.google.common.base.Preconditions;
import org.apache.hbase.thirdparty.com.google.protobuf.InvalidProtocolBufferException;
import org.apache.hbase.thirdparty.com.google.protobuf.UnsafeByteOperations;
import org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil;
import org.apache.hadoop.hbase.shaded.protobuf.generated.FilterProtos;
import org.apache.hadoop.hbase.shaded.protobuf.generated.HBaseProtos;
/**
* Different from {@link SingleColumnValueFilter} which returns an <b>entire</b> row
* when specified condition is matched, {@link ColumnValueFilter} return the matched cell only.
* <p>
* This filter is used to filter cells based on column and value.
* It takes a {@link org.apache.hadoop.hbase.CompareOperator} operator (<, <=, =, !=, >, >=), and
* and a {@link ByteArrayComparable} comparator.
*/
@InterfaceAudience.Public
public class ColumnValueFilter extends FilterBase {
private final byte[] family;
private final byte[] qualifier;
private final CompareOperator op;
private final ByteArrayComparable comparator;
// This flag is used to speed up seeking cells when matched column is found, such that following
// columns in the same row can be skipped faster by NEXT_ROW instead of NEXT_COL.
private boolean columnFound = false;
public ColumnValueFilter(final byte[] family, final byte[] qualifier,
final CompareOperator op, final byte[] value) {
this(family, qualifier, op, new BinaryComparator(value));
}
public ColumnValueFilter(final byte[] family, final byte[] qualifier,
final CompareOperator op,
final ByteArrayComparable comparator) {
this.family = Preconditions.checkNotNull(family, "family should not be null.");
this.qualifier = qualifier == null ? new byte[0] : qualifier;
this.op = Preconditions.checkNotNull(op, "CompareOperator should not be null");
this.comparator = Preconditions.checkNotNull(comparator, "Comparator should not be null");
}
/**
* @return operator
*/
public CompareOperator getCompareOperator() {
return op;
}
/**
* @return the comparator
*/
public ByteArrayComparable getComparator() {
return comparator;
}
/**
* @return the column family
*/
public byte[] getFamily() {
return family;
}
/**
* @return the qualifier
*/
public byte[] getQualifier() {
return qualifier;
}
@Override
public void reset() throws IOException {
columnFound = false;
}
@Override
public boolean filterRowKey(Cell cell) throws IOException {
return false;
}
@Override
public ReturnCode filterCell(Cell c) throws IOException {
// 1. Check column match
if (!CellUtil.matchingColumn(c, this.family, this.qualifier)) {
return columnFound ? ReturnCode.NEXT_ROW : ReturnCode.NEXT_COL;
}
// Column found
columnFound = true;
// 2. Check value match:
// True means filter out, just skip this cell, else include it.
return compareValue(getCompareOperator(), getComparator(), c) ?
ReturnCode.SKIP : ReturnCode.INCLUDE;
}
/**
* This method is used to determine a cell should be included or filtered out.
* @param op one of operators {@link CompareOperator}
* @param comparator comparator used to compare cells.
* @param cell cell to be compared.
* @return true means cell should be filtered out, included otherwise.
*/
private boolean compareValue(final CompareOperator op, final ByteArrayComparable comparator,
final Cell cell) {
if (op == CompareOperator.NO_OP) {
return true;
}
int compareResult = PrivateCellUtil.compareValue(cell, comparator);
return CompareFilter.compare(op, compareResult);
}
/**
* Creating this filter by reflection, it is used by {@link ParseFilter},
* @param filterArguments arguments for creating a ColumnValueFilter
* @return a ColumnValueFilter
*/
public static Filter createFilterFromArguments(ArrayList<byte[]> filterArguments) {
Preconditions.checkArgument(filterArguments.size() == 4,
"Expect 4 arguments: %s", filterArguments.size());
byte[] family = ParseFilter.removeQuotesFromByteArray(filterArguments.get(0));
byte[] qualifier = ParseFilter.removeQuotesFromByteArray(filterArguments.get(1));
CompareOperator operator = ParseFilter.createCompareOperator(filterArguments.get(2));
ByteArrayComparable comparator =
ParseFilter.createComparator(ParseFilter.removeQuotesFromByteArray(filterArguments.get(3)));
if (comparator instanceof RegexStringComparator ||
comparator instanceof SubstringComparator) {
if (operator != CompareOperator.EQUAL &&
operator != CompareOperator.NOT_EQUAL) {
throw new IllegalArgumentException("A regexstring comparator and substring comparator " +
"can only be used with EQUAL and NOT_EQUAL");
}
}
return new ColumnValueFilter(family, qualifier, operator, comparator);
}
/**
* @return A pb instance to represent this instance.
*/
FilterProtos.ColumnValueFilter convert() {
FilterProtos.ColumnValueFilter.Builder builder =
FilterProtos.ColumnValueFilter.newBuilder();
builder.setFamily(UnsafeByteOperations.unsafeWrap(this.family));
builder.setQualifier(UnsafeByteOperations.unsafeWrap(this.qualifier));
builder.setCompareOp(HBaseProtos.CompareType.valueOf(this.op.name()));
builder.setComparator(ProtobufUtil.toComparator(this.comparator));
return builder.build();
}
/**
* Parse protobuf bytes to a ColumnValueFilter
* @param pbBytes pbBytes
* @return a ColumnValueFilter
* @throws DeserializationException deserialization exception
*/
public static ColumnValueFilter parseFrom(final byte[] pbBytes) throws DeserializationException {
FilterProtos.ColumnValueFilter proto;
try {
proto = FilterProtos.ColumnValueFilter.parseFrom(pbBytes);
} catch (InvalidProtocolBufferException e) {
throw new DeserializationException(e);
}
final CompareOperator compareOp = CompareOperator.valueOf(proto.getCompareOp().name());
final ByteArrayComparable comparator;
try {
comparator = ProtobufUtil.toComparator(proto.getComparator());
} catch (IOException ioe) {
throw new DeserializationException(ioe);
}
return new ColumnValueFilter(proto.getFamily().toByteArray(),
proto.getQualifier().toByteArray(), compareOp, comparator);
}
@Override
public byte[] toByteArray() throws IOException {
return convert().toByteArray();
}
@Override
boolean areSerializedFieldsEqual(Filter o) {
if (o == this) {
return true;
} else if (!(o instanceof ColumnValueFilter)) {
return false;
}
ColumnValueFilter other = (ColumnValueFilter) o;
return Bytes.equals(this.getFamily(), other.getFamily()) &&
Bytes.equals(this.getQualifier(), other.getQualifier()) &&
this.getCompareOperator().equals(other.getCompareOperator()) &&
this.getComparator().areSerializedFieldsEqual(other.getComparator());
}
@Override
public boolean isFamilyEssential(byte[] name) throws IOException {
return Bytes.equals(name, this.family);
}
@Override
public String toString() {
return String.format("%s (%s, %s, %s, %s)",
getClass().getSimpleName(), Bytes.toStringBinary(this.family),
Bytes.toStringBinary(this.qualifier), this.op.name(),
Bytes.toStringBinary(this.comparator.getValue()));
}
}

View File

@ -25,23 +25,27 @@ import java.util.ArrayList;
import org.apache.hadoop.hbase.Cell; import org.apache.hadoop.hbase.Cell;
import org.apache.hadoop.hbase.CompareOperator; import org.apache.hadoop.hbase.CompareOperator;
import org.apache.hadoop.hbase.PrivateCellUtil; import org.apache.hadoop.hbase.PrivateCellUtil;
import org.apache.hadoop.hbase.util.Bytes;
import org.apache.yetus.audience.InterfaceAudience; import org.apache.yetus.audience.InterfaceAudience;
import org.apache.hbase.thirdparty.com.google.common.base.Preconditions;
import org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil; import org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil;
import org.apache.hadoop.hbase.shaded.protobuf.generated.FilterProtos; import org.apache.hadoop.hbase.shaded.protobuf.generated.FilterProtos;
import org.apache.hadoop.hbase.shaded.protobuf.generated.HBaseProtos; import org.apache.hadoop.hbase.shaded.protobuf.generated.HBaseProtos;
import org.apache.hadoop.hbase.shaded.protobuf.generated.HBaseProtos.CompareType; import org.apache.hadoop.hbase.shaded.protobuf.generated.HBaseProtos.CompareType;
import org.apache.hadoop.hbase.util.Bytes;
import org.apache.hbase.thirdparty.com.google.common.base.Preconditions;
/** /**
* This is a generic filter to be used to filter by comparison. It takes an * This is a generic filter to be used to filter by comparison. It takes an
* operator (equal, greater, not equal, etc) and a byte [] comparator. * operator (equal, greater, not equal, etc) and a byte [] comparator.
* <p> * <p>
* To filter by row key, use {@link RowFilter}. * To filter by row key, use {@link RowFilter}.
* <p> * <p>
* To filter by column family, use {@link FamilyFilter}.
* <p>
* To filter by column qualifier, use {@link QualifierFilter}. * To filter by column qualifier, use {@link QualifierFilter}.
* <p> * <p>
* To filter by value, use {@link SingleColumnValueFilter}. * To filter by value, use {@link ValueFilter}.
* <p> * <p>
* These filters can be wrapped with {@link SkipFilter} and {@link WhileMatchFilter} * These filters can be wrapped with {@link SkipFilter} and {@link WhileMatchFilter}
* to add more control. * to add more control.

View File

@ -93,6 +93,8 @@ public class ParseFilter {
"SingleColumnValueExcludeFilter"); "SingleColumnValueExcludeFilter");
filterHashMap.put("DependentColumnFilter", ParseConstants.FILTER_PACKAGE + "." + filterHashMap.put("DependentColumnFilter", ParseConstants.FILTER_PACKAGE + "." +
"DependentColumnFilter"); "DependentColumnFilter");
filterHashMap.put("ColumnValueFilter", ParseConstants.FILTER_PACKAGE + "." +
"ColumnValueFilter");
// Creates the operatorPrecedenceHashMap // Creates the operatorPrecedenceHashMap
operatorPrecedenceHashMap = new HashMap<>(); operatorPrecedenceHashMap = new HashMap<>();
@ -769,8 +771,6 @@ public class ParseFilter {
/** /**
* Takes a compareOperator symbol as a byte array and returns the corresponding CompareOperator * Takes a compareOperator symbol as a byte array and returns the corresponding CompareOperator
* @deprecated Since 2.0
* <p>
* @param compareOpAsByteArray the comparatorOperator symbol as a byte array * @param compareOpAsByteArray the comparatorOperator symbol as a byte array
* @return the Compare Operator * @return the Compare Operator
*/ */

View File

@ -170,3 +170,10 @@ message RowRange {
message MultiRowRangeFilter { message MultiRowRangeFilter {
repeated RowRange row_range_list = 1; repeated RowRange row_range_list = 1;
} }
message ColumnValueFilter {
required bytes family = 1;
required bytes qualifier = 2;
required CompareType compare_op = 3;
required Comparator comparator = 4;
}

View File

@ -169,3 +169,10 @@ message RowRange {
message MultiRowRangeFilter { message MultiRowRangeFilter {
repeated RowRange row_range_list = 1; repeated RowRange row_range_list = 1;
} }
message ColumnValueFilter {
required bytes family = 1;
required bytes qualifier = 2;
required CompareType compare_op = 3;
required Comparator comparator = 4;
}

View File

@ -132,6 +132,10 @@ public class TestFilter {
Bytes.toBytes("testQualifierFour-2"), Bytes.toBytes("testQualifierFour-3") Bytes.toBytes("testQualifierFour-2"), Bytes.toBytes("testQualifierFour-3")
}; };
private static final byte [][] QUALIFIERS_FIVE = {
Bytes.toBytes("testQualifierFive-0"), Bytes.toBytes("testQualifierFive-1")
};
private static final byte [][] VALUES = { private static final byte [][] VALUES = {
Bytes.toBytes("testValueOne"), Bytes.toBytes("testValueTwo") Bytes.toBytes("testValueOne"), Bytes.toBytes("testValueTwo")
}; };
@ -1663,6 +1667,157 @@ public class TestFilter {
} }
@Test
public void testColumnValueFilter() throws Exception {
// Prepare test rows:
for (int i = 0; i < 2; i++) {
for (int j = 0; j < ROWS_ONE.length; j++) {
Put p1 = new Put(ROWS_ONE[j]).setDurability(Durability.SKIP_WAL);
Put p2 = new Put(ROWS_TWO[j]).setDurability(Durability.SKIP_WAL);
for (byte[] q5 : QUALIFIERS_FIVE) {
p1.addColumn(FAMILIES[0], q5, VALUES[0 + i]).addColumn(FAMILIES[1], q5, VALUES[0 + i]);
p2.addColumn(FAMILIES[0], q5, VALUES[1 - i]).addColumn(FAMILIES[1], q5, VALUES[1 - i]);
}
this.region.put(p1);
this.region.put(p2);
}
this.region.flush(true);
}
// 1. Test = f[0]:q5[0]:v[1]
Scan scan = new Scan().setFilter(
new ColumnValueFilter(FAMILIES[0], QUALIFIERS_FIVE[0], CompareOperator.EQUAL, VALUES[1]));
KeyValue[] expectedEquals =
{ new KeyValue(ROWS_ONE[0], FAMILIES[0], QUALIFIERS_FIVE[0], VALUES[1]),
new KeyValue(ROWS_ONE[1], FAMILIES[0], QUALIFIERS_FIVE[0], VALUES[1]),
new KeyValue(ROWS_ONE[2], FAMILIES[0], QUALIFIERS_FIVE[0], VALUES[1]),
new KeyValue(ROWS_ONE[3], FAMILIES[0], QUALIFIERS_FIVE[0], VALUES[1]),
new KeyValue(ROWS_TWO[0], FAMILIES[0], QUALIFIERS_FIVE[0], VALUES[1]),
new KeyValue(ROWS_TWO[1], FAMILIES[0], QUALIFIERS_FIVE[0], VALUES[1]),
new KeyValue(ROWS_TWO[2], FAMILIES[0], QUALIFIERS_FIVE[0], VALUES[1]),
new KeyValue(ROWS_TWO[3], FAMILIES[0], QUALIFIERS_FIVE[0], VALUES[1]) };
verifyScanFull(scan, expectedEquals);
// 2. Test > f[0]:q5[0]:v[0]
scan.setFilter(
new ColumnValueFilter(FAMILIES[0], QUALIFIERS_FIVE[0], CompareOperator.GREATER, VALUES[0]));
KeyValue[] expectedGreater =
{ new KeyValue(ROWS_ONE[0], FAMILIES[0], QUALIFIERS_FIVE[0], VALUES[1]),
new KeyValue(ROWS_ONE[1], FAMILIES[0], QUALIFIERS_FIVE[0], VALUES[1]),
new KeyValue(ROWS_ONE[2], FAMILIES[0], QUALIFIERS_FIVE[0], VALUES[1]),
new KeyValue(ROWS_ONE[3], FAMILIES[0], QUALIFIERS_FIVE[0], VALUES[1]),
new KeyValue(ROWS_TWO[0], FAMILIES[0], QUALIFIERS_FIVE[0], VALUES[1]),
new KeyValue(ROWS_TWO[1], FAMILIES[0], QUALIFIERS_FIVE[0], VALUES[1]),
new KeyValue(ROWS_TWO[2], FAMILIES[0], QUALIFIERS_FIVE[0], VALUES[1]),
new KeyValue(ROWS_TWO[3], FAMILIES[0], QUALIFIERS_FIVE[0], VALUES[1]) };
verifyScanFull(scan, expectedGreater);
// 3. Test >= f[0]:q5[0]:v[0]
// also test readAllVersions(), since FAMILIES[0] allow multiple versions.
scan.readAllVersions().setFilter(new ColumnValueFilter(FAMILIES[0], QUALIFIERS_FIVE[0],
CompareOperator.GREATER_OR_EQUAL, VALUES[0]));
KeyValue[] expectedGreaterOrEqual =
{ new KeyValue(ROWS_ONE[0], FAMILIES[0], QUALIFIERS_FIVE[0], VALUES[1]),
new KeyValue(ROWS_ONE[0], FAMILIES[0], QUALIFIERS_FIVE[0], VALUES[0]),
new KeyValue(ROWS_ONE[1], FAMILIES[0], QUALIFIERS_FIVE[0], VALUES[1]),
new KeyValue(ROWS_ONE[1], FAMILIES[0], QUALIFIERS_FIVE[0], VALUES[0]),
new KeyValue(ROWS_ONE[2], FAMILIES[0], QUALIFIERS_FIVE[0], VALUES[1]),
new KeyValue(ROWS_ONE[2], FAMILIES[0], QUALIFIERS_FIVE[0], VALUES[0]),
new KeyValue(ROWS_ONE[3], FAMILIES[0], QUALIFIERS_FIVE[0], VALUES[1]),
new KeyValue(ROWS_ONE[3], FAMILIES[0], QUALIFIERS_FIVE[0], VALUES[0]),
new KeyValue(ROWS_TWO[0], FAMILIES[0], QUALIFIERS_FIVE[0], VALUES[0]),
new KeyValue(ROWS_TWO[0], FAMILIES[0], QUALIFIERS_FIVE[0], VALUES[1]),
new KeyValue(ROWS_TWO[1], FAMILIES[0], QUALIFIERS_FIVE[0], VALUES[0]),
new KeyValue(ROWS_TWO[1], FAMILIES[0], QUALIFIERS_FIVE[0], VALUES[1]),
new KeyValue(ROWS_TWO[2], FAMILIES[0], QUALIFIERS_FIVE[0], VALUES[0]),
new KeyValue(ROWS_TWO[2], FAMILIES[0], QUALIFIERS_FIVE[0], VALUES[1]),
new KeyValue(ROWS_TWO[3], FAMILIES[0], QUALIFIERS_FIVE[0], VALUES[0]),
new KeyValue(ROWS_TWO[3], FAMILIES[0], QUALIFIERS_FIVE[0], VALUES[1]) };
verifyScanFull(scan, expectedGreaterOrEqual);
// 4. Test < f[1]:q5[1]:v[1], FAMILIES[1] doesn't support multiple versions
scan.readVersions(1).setFilter(new ColumnValueFilter(FAMILIES[1], QUALIFIERS_FIVE[1],
CompareOperator.LESS, VALUES[1]));
KeyValue[] expectedLess =
{ new KeyValue(ROWS_TWO[0], FAMILIES[1], QUALIFIERS_FIVE[1], VALUES[0]),
new KeyValue(ROWS_TWO[1], FAMILIES[1], QUALIFIERS_FIVE[1], VALUES[0]),
new KeyValue(ROWS_TWO[2], FAMILIES[1], QUALIFIERS_FIVE[1], VALUES[0]),
new KeyValue(ROWS_TWO[3], FAMILIES[1], QUALIFIERS_FIVE[1], VALUES[0]) };
verifyScanFull(scan, expectedLess);
// 5. Test <= f[1]:q5[0]:v[1]
scan.setFilter(new ColumnValueFilter(FAMILIES[1], QUALIFIERS_FIVE[1],
CompareOperator.LESS_OR_EQUAL, VALUES[1]));
KeyValue[] expectedLessOrEqual =
{ new KeyValue(ROWS_ONE[0], FAMILIES[1], QUALIFIERS_FIVE[1], VALUES[1]),
new KeyValue(ROWS_ONE[1], FAMILIES[1], QUALIFIERS_FIVE[1], VALUES[1]),
new KeyValue(ROWS_ONE[2], FAMILIES[1], QUALIFIERS_FIVE[1], VALUES[1]),
new KeyValue(ROWS_ONE[3], FAMILIES[1], QUALIFIERS_FIVE[1], VALUES[1]),
new KeyValue(ROWS_TWO[0], FAMILIES[1], QUALIFIERS_FIVE[1], VALUES[0]),
new KeyValue(ROWS_TWO[1], FAMILIES[1], QUALIFIERS_FIVE[1], VALUES[0]),
new KeyValue(ROWS_TWO[2], FAMILIES[1], QUALIFIERS_FIVE[1], VALUES[0]),
new KeyValue(ROWS_TWO[3], FAMILIES[1], QUALIFIERS_FIVE[1], VALUES[0]) };
verifyScanFull(scan, expectedLessOrEqual);
// 6. Test != f[1]:q5[1]:v[1]
scan.setFilter(
new ColumnValueFilter(FAMILIES[1], QUALIFIERS_FIVE[1], CompareOperator.NOT_EQUAL, VALUES[1]));
KeyValue[] expectedNotEqual =
{ new KeyValue(ROWS_TWO[0], FAMILIES[1], QUALIFIERS_FIVE[1], VALUES[0]),
new KeyValue(ROWS_TWO[1], FAMILIES[1], QUALIFIERS_FIVE[1], VALUES[0]),
new KeyValue(ROWS_TWO[2], FAMILIES[1], QUALIFIERS_FIVE[1], VALUES[0]),
new KeyValue(ROWS_TWO[3], FAMILIES[1], QUALIFIERS_FIVE[1], VALUES[0]) };
verifyScanFull(scan, expectedNotEqual);
// 7. Test FilterList(MUST_PASS_ONE) combining ColumnValueFilter and QualifierFilter
// (ColumnValueFilter, != f[1]:q5[1]:v[1]) || (QualifierFilter, = q5[0])
List<Filter> orFilters = new ArrayList<>(2);
orFilters.add(
new ColumnValueFilter(FAMILIES[1], QUALIFIERS_FIVE[1], CompareOperator.NOT_EQUAL, VALUES[1]));
orFilters.add(
new QualifierFilter(CompareOperator.EQUAL, new BinaryComparator(QUALIFIERS_FIVE[0])));
scan.setFilter(new FilterList(Operator.MUST_PASS_ONE, orFilters));
KeyValue[] expectedMustPassOne =
{ new KeyValue(ROWS_ONE[0], FAMILIES[0], QUALIFIERS_FIVE[0], VALUES[1]),
new KeyValue(ROWS_ONE[0], FAMILIES[1], QUALIFIERS_FIVE[0], VALUES[1]),
new KeyValue(ROWS_ONE[1], FAMILIES[0], QUALIFIERS_FIVE[0], VALUES[1]),
new KeyValue(ROWS_ONE[1], FAMILIES[1], QUALIFIERS_FIVE[0], VALUES[1]),
new KeyValue(ROWS_ONE[2], FAMILIES[0], QUALIFIERS_FIVE[0], VALUES[1]),
new KeyValue(ROWS_ONE[2], FAMILIES[1], QUALIFIERS_FIVE[0], VALUES[1]),
new KeyValue(ROWS_ONE[3], FAMILIES[0], QUALIFIERS_FIVE[0], VALUES[1]),
new KeyValue(ROWS_ONE[3], FAMILIES[1], QUALIFIERS_FIVE[0], VALUES[1]),
new KeyValue(ROWS_TWO[0], FAMILIES[0], QUALIFIERS_FIVE[0], VALUES[0]),
new KeyValue(ROWS_TWO[0], FAMILIES[1], QUALIFIERS_FIVE[0], VALUES[0]),
new KeyValue(ROWS_TWO[0], FAMILIES[1], QUALIFIERS_FIVE[1], VALUES[0]), // this pass scvf
new KeyValue(ROWS_TWO[1], FAMILIES[0], QUALIFIERS_FIVE[0], VALUES[0]),
new KeyValue(ROWS_TWO[1], FAMILIES[1], QUALIFIERS_FIVE[0], VALUES[0]),
new KeyValue(ROWS_TWO[1], FAMILIES[1], QUALIFIERS_FIVE[1], VALUES[0]), // this pass scvf
new KeyValue(ROWS_TWO[2], FAMILIES[0], QUALIFIERS_FIVE[0], VALUES[0]),
new KeyValue(ROWS_TWO[2], FAMILIES[1], QUALIFIERS_FIVE[0], VALUES[0]),
new KeyValue(ROWS_TWO[2], FAMILIES[1], QUALIFIERS_FIVE[1], VALUES[0]), // this pass scvf
new KeyValue(ROWS_TWO[3], FAMILIES[0], QUALIFIERS_FIVE[0], VALUES[0]),
new KeyValue(ROWS_TWO[3], FAMILIES[1], QUALIFIERS_FIVE[0], VALUES[0]),
new KeyValue(ROWS_TWO[3], FAMILIES[1], QUALIFIERS_FIVE[1], VALUES[0]) }; // this pass scvf
verifyScanFull(scan, expectedMustPassOne);
// 8. Test FilterList(MUST_PASS_ALL) combining ColumnValueFilter and RowFilter
// (ColumnValueFilter, != f[1]:q5[1]:v[1]) && (RowFilter, = prefix:"testRow")
List<Filter> andFilters = new ArrayList<>(2);
andFilters.add(
new ColumnValueFilter(FAMILIES[1], QUALIFIERS_FIVE[1], CompareOperator.NOT_EQUAL, VALUES[1]));
andFilters.add(new RowFilter(CompareOperator.EQUAL,
new BinaryPrefixComparator(Bytes.toBytes("testRow"))));
scan.setFilter(new FilterList(Operator.MUST_PASS_ALL, andFilters));
KeyValue[] expectedMustPassAll =
{ new KeyValue(ROWS_TWO[0], FAMILIES[1], QUALIFIERS_FIVE[1], VALUES[0]),
new KeyValue(ROWS_TWO[1], FAMILIES[1], QUALIFIERS_FIVE[1], VALUES[0]),
new KeyValue(ROWS_TWO[2], FAMILIES[1], QUALIFIERS_FIVE[1], VALUES[0]),
new KeyValue(ROWS_TWO[3], FAMILIES[1], QUALIFIERS_FIVE[1], VALUES[0]) };
verifyScanFull(scan, expectedMustPassAll);
// 9. Test specified columns with FilterList(MUST_PASS_ONE) which sused in case 7.
// Result is different from case 7, because column is strongly constrained by specified columns
Scan anotherScan = new Scan().addColumn(FAMILIES[1], QUALIFIERS_FIVE[1])
.setFilter(new FilterList(Operator.MUST_PASS_ONE, orFilters));
KeyValue[] expectedValues =
{ new KeyValue(ROWS_TWO[0], FAMILIES[1], QUALIFIERS_FIVE[1], VALUES[0]),
new KeyValue(ROWS_TWO[1], FAMILIES[1], QUALIFIERS_FIVE[1], VALUES[0]),
new KeyValue(ROWS_TWO[2], FAMILIES[1], QUALIFIERS_FIVE[1], VALUES[0]),
new KeyValue(ROWS_TWO[3], FAMILIES[1], QUALIFIERS_FIVE[1], VALUES[0]) };
verifyScanFull(anotherScan, expectedValues);
}
private void verifyScan(Scan s, long expectedRows, long expectedKeys) private void verifyScan(Scan s, long expectedRows, long expectedKeys)
throws IOException { throws IOException {
InternalScanner scanner = this.region.getScanner(s); InternalScanner scanner = this.region.getScanner(s);

View File

@ -341,4 +341,13 @@ public class TestFilterSerialization {
assertTrue(multiRowRangeFilter.areSerializedFieldsEqual( assertTrue(multiRowRangeFilter.areSerializedFieldsEqual(
ProtobufUtil.toFilter(ProtobufUtil.toFilter(multiRowRangeFilter)))); ProtobufUtil.toFilter(ProtobufUtil.toFilter(multiRowRangeFilter))));
} }
@Test
public void testColumnValueFilter() throws Exception {
ColumnValueFilter columnValueFilter =
new ColumnValueFilter(Bytes.toBytes("family"), Bytes.toBytes("qualifier"),
CompareOperator.EQUAL, Bytes.toBytes("value"));
assertTrue(columnValueFilter.areSerializedFieldsEqual(
ProtobufUtil.toFilter(ProtobufUtil.toFilter(columnValueFilter))));
}
} }

View File

@ -685,4 +685,15 @@ public class TestParseFilter {
assertEquals(clazz, filter.getClass()); assertEquals(clazz, filter.getClass());
return clazz.cast(filter); return clazz.cast(filter);
} }
@Test
public void testColumnValueFilter() throws IOException {
String filterString = "ColumnValueFilter ('family', 'qualifier', <, 'binaryprefix:value')";
ColumnValueFilter cvf = doTestFilter(filterString, ColumnValueFilter.class);
assertEquals("family", new String(cvf.getFamily(), StandardCharsets.UTF_8));
assertEquals("qualifier", new String(cvf.getQualifier(), StandardCharsets.UTF_8));
assertEquals(CompareOperator.LESS, cvf.getCompareOperator());
assertTrue(cvf.getComparator() instanceof BinaryPrefixComparator);
assertEquals("value", new String(cvf.getComparator().getValue(), StandardCharsets.UTF_8));
}
} }