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