001/** 002 * 003 * Licensed to the Apache Software Foundation (ASF) under one 004 * or more contributor license agreements. See the NOTICE file 005 * distributed with this work for additional information 006 * regarding copyright ownership. The ASF licenses this file 007 * to you under the Apache License, Version 2.0 (the 008 * "License"); you may not use this file except in compliance 009 * with the License. You may obtain a copy of the License at 010 * 011 * http://www.apache.org/licenses/LICENSE-2.0 012 * 013 * Unless required by applicable law or agreed to in writing, software 014 * distributed under the License is distributed on an "AS IS" BASIS, 015 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 016 * See the License for the specific language governing permissions and 017 * limitations under the License. 018 */ 019 020package org.apache.hadoop.hbase.filter; 021 022import java.io.IOException; 023import java.util.ArrayList; 024 025import org.apache.hadoop.hbase.Cell; 026import org.apache.hadoop.hbase.CellUtil; 027import org.apache.hadoop.hbase.CompareOperator; 028import org.apache.hadoop.hbase.PrivateCellUtil; 029import org.apache.hadoop.hbase.exceptions.DeserializationException; 030import org.apache.hadoop.hbase.util.Bytes; 031import org.apache.yetus.audience.InterfaceAudience; 032 033import org.apache.hbase.thirdparty.com.google.common.base.Preconditions; 034import org.apache.hbase.thirdparty.com.google.protobuf.InvalidProtocolBufferException; 035import org.apache.hbase.thirdparty.com.google.protobuf.UnsafeByteOperations; 036 037import org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil; 038import org.apache.hadoop.hbase.shaded.protobuf.generated.FilterProtos; 039import org.apache.hadoop.hbase.shaded.protobuf.generated.HBaseProtos; 040 041/** 042 * Different from {@link SingleColumnValueFilter} which returns an <b>entire</b> row 043 * when specified condition is matched, {@link ColumnValueFilter} return the matched cell only. 044 * <p> 045 * This filter is used to filter cells based on column and value. 046 * It takes a {@link org.apache.hadoop.hbase.CompareOperator} operator (<, <=, =, !=, >, >=), and 047 * and a {@link ByteArrayComparable} comparator. 048 */ 049@InterfaceAudience.Public 050public class ColumnValueFilter extends FilterBase { 051 private final byte[] family; 052 private final byte[] qualifier; 053 private final CompareOperator op; 054 private final ByteArrayComparable comparator; 055 056 // This flag is used to speed up seeking cells when matched column is found, such that following 057 // columns in the same row can be skipped faster by NEXT_ROW instead of NEXT_COL. 058 private boolean columnFound = false; 059 060 public ColumnValueFilter(final byte[] family, final byte[] qualifier, 061 final CompareOperator op, final byte[] value) { 062 this(family, qualifier, op, new BinaryComparator(value)); 063 } 064 065 public ColumnValueFilter(final byte[] family, final byte[] qualifier, 066 final CompareOperator op, 067 final ByteArrayComparable comparator) { 068 this.family = Preconditions.checkNotNull(family, "family should not be null."); 069 this.qualifier = qualifier == null ? new byte[0] : qualifier; 070 this.op = Preconditions.checkNotNull(op, "CompareOperator should not be null"); 071 this.comparator = Preconditions.checkNotNull(comparator, "Comparator should not be null"); 072 } 073 074 /** 075 * @return operator 076 */ 077 public CompareOperator getCompareOperator() { 078 return op; 079 } 080 081 /** 082 * @return the comparator 083 */ 084 public ByteArrayComparable getComparator() { 085 return comparator; 086 } 087 088 /** 089 * @return the column family 090 */ 091 public byte[] getFamily() { 092 return family; 093 } 094 095 /** 096 * @return the qualifier 097 */ 098 public byte[] getQualifier() { 099 return qualifier; 100 } 101 102 @Override 103 public void reset() throws IOException { 104 columnFound = false; 105 } 106 107 @Override 108 public boolean filterRowKey(Cell cell) throws IOException { 109 return false; 110 } 111 112 @Override 113 public ReturnCode filterCell(Cell c) throws IOException { 114 // 1. Check column match 115 if (!CellUtil.matchingColumn(c, this.family, this.qualifier)) { 116 return columnFound ? ReturnCode.NEXT_ROW : ReturnCode.NEXT_COL; 117 } 118 // Column found 119 columnFound = true; 120 // 2. Check value match: 121 // True means filter out, just skip this cell, else include it. 122 return compareValue(getCompareOperator(), getComparator(), c) ? 123 ReturnCode.SKIP : ReturnCode.INCLUDE; 124 } 125 126 /** 127 * This method is used to determine a cell should be included or filtered out. 128 * @param op one of operators {@link CompareOperator} 129 * @param comparator comparator used to compare cells. 130 * @param cell cell to be compared. 131 * @return true means cell should be filtered out, included otherwise. 132 */ 133 private boolean compareValue(final CompareOperator op, final ByteArrayComparable comparator, 134 final Cell cell) { 135 if (op == CompareOperator.NO_OP) { 136 return true; 137 } 138 int compareResult = PrivateCellUtil.compareValue(cell, comparator); 139 return CompareFilter.compare(op, compareResult); 140 } 141 142 /** 143 * Creating this filter by reflection, it is used by {@link ParseFilter}, 144 * @param filterArguments arguments for creating a ColumnValueFilter 145 * @return a ColumnValueFilter 146 */ 147 public static Filter createFilterFromArguments(ArrayList<byte[]> filterArguments) { 148 Preconditions.checkArgument(filterArguments.size() == 4, 149 "Expect 4 arguments: %s", filterArguments.size()); 150 byte[] family = ParseFilter.removeQuotesFromByteArray(filterArguments.get(0)); 151 byte[] qualifier = ParseFilter.removeQuotesFromByteArray(filterArguments.get(1)); 152 CompareOperator operator = ParseFilter.createCompareOperator(filterArguments.get(2)); 153 ByteArrayComparable comparator = 154 ParseFilter.createComparator(ParseFilter.removeQuotesFromByteArray(filterArguments.get(3))); 155 156 if (comparator instanceof RegexStringComparator || 157 comparator instanceof SubstringComparator) { 158 if (operator != CompareOperator.EQUAL && 159 operator != CompareOperator.NOT_EQUAL) { 160 throw new IllegalArgumentException("A regexstring comparator and substring comparator " + 161 "can only be used with EQUAL and NOT_EQUAL"); 162 } 163 } 164 165 return new ColumnValueFilter(family, qualifier, operator, comparator); 166 } 167 168 /** 169 * @return A pb instance to represent this instance. 170 */ 171 FilterProtos.ColumnValueFilter convert() { 172 FilterProtos.ColumnValueFilter.Builder builder = 173 FilterProtos.ColumnValueFilter.newBuilder(); 174 175 builder.setFamily(UnsafeByteOperations.unsafeWrap(this.family)); 176 builder.setQualifier(UnsafeByteOperations.unsafeWrap(this.qualifier)); 177 builder.setCompareOp(HBaseProtos.CompareType.valueOf(this.op.name())); 178 builder.setComparator(ProtobufUtil.toComparator(this.comparator)); 179 180 return builder.build(); 181 } 182 183 /** 184 * Parse protobuf bytes to a ColumnValueFilter 185 * @param pbBytes pbBytes 186 * @return a ColumnValueFilter 187 * @throws DeserializationException deserialization exception 188 */ 189 public static ColumnValueFilter parseFrom(final byte[] pbBytes) throws DeserializationException { 190 FilterProtos.ColumnValueFilter proto; 191 try { 192 proto = FilterProtos.ColumnValueFilter.parseFrom(pbBytes); 193 } catch (InvalidProtocolBufferException e) { 194 throw new DeserializationException(e); 195 } 196 197 final CompareOperator compareOp = CompareOperator.valueOf(proto.getCompareOp().name()); 198 final ByteArrayComparable comparator; 199 try { 200 comparator = ProtobufUtil.toComparator(proto.getComparator()); 201 } catch (IOException ioe) { 202 throw new DeserializationException(ioe); 203 } 204 205 return new ColumnValueFilter(proto.getFamily().toByteArray(), 206 proto.getQualifier().toByteArray(), compareOp, comparator); 207 } 208 209 @Override 210 public byte[] toByteArray() throws IOException { 211 return convert().toByteArray(); 212 } 213 214 @Override 215 boolean areSerializedFieldsEqual(Filter o) { 216 if (o == this) { 217 return true; 218 } else if (!(o instanceof ColumnValueFilter)) { 219 return false; 220 } 221 222 ColumnValueFilter other = (ColumnValueFilter) o; 223 return Bytes.equals(this.getFamily(), other.getFamily()) && 224 Bytes.equals(this.getQualifier(), other.getQualifier()) && 225 this.getCompareOperator().equals(other.getCompareOperator()) && 226 this.getComparator().areSerializedFieldsEqual(other.getComparator()); 227 } 228 229 @Override 230 public boolean isFamilyEssential(byte[] name) throws IOException { 231 return Bytes.equals(name, this.family); 232 } 233 234 @Override 235 public String toString() { 236 return String.format("%s (%s, %s, %s, %s)", 237 getClass().getSimpleName(), Bytes.toStringBinary(this.family), 238 Bytes.toStringBinary(this.qualifier), this.op.name(), 239 Bytes.toStringBinary(this.comparator.getValue())); 240 } 241}