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