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 */
019package org.apache.hadoop.hbase.filter;
020
021import java.io.IOException;
022import java.util.ArrayList;
023import java.util.Objects;
024
025import org.apache.hadoop.hbase.Cell;
026import org.apache.hadoop.hbase.CompareOperator;
027import org.apache.hadoop.hbase.PrivateCellUtil;
028import org.apache.hadoop.hbase.util.Bytes;
029import org.apache.yetus.audience.InterfaceAudience;
030
031import org.apache.hbase.thirdparty.com.google.common.base.Preconditions;
032
033import org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil;
034import org.apache.hadoop.hbase.shaded.protobuf.generated.FilterProtos;
035import org.apache.hadoop.hbase.shaded.protobuf.generated.HBaseProtos;
036import org.apache.hadoop.hbase.shaded.protobuf.generated.HBaseProtos.CompareType;
037
038/**
039 * This is a generic filter to be used to filter by comparison.  It takes an
040 * operator (equal, greater, not equal, etc) and a byte [] comparator.
041 * <p>
042 * To filter by row key, use {@link RowFilter}.
043 * <p>
044 * To filter by column family, use {@link FamilyFilter}.
045 * <p>
046 * To filter by column qualifier, use {@link QualifierFilter}.
047 * <p>
048 * To filter by value, use {@link ValueFilter}.
049 * <p>
050 * These filters can be wrapped with {@link SkipFilter} and {@link WhileMatchFilter}
051 * to add more control.
052 * <p>
053 * Multiple filters can be combined using {@link FilterList}.
054 */
055@InterfaceAudience.Public
056public abstract class CompareFilter extends FilterBase {
057  protected CompareOperator op;
058  protected ByteArrayComparable comparator;
059
060  /**
061   * Constructor.
062   * @param op the compare op for row matching
063   * @param comparator the comparator for row matching
064   */
065  public CompareFilter(final CompareOperator op,
066                       final ByteArrayComparable comparator) {
067    this.op = op;
068    this.comparator = comparator;
069  }
070
071  public CompareOperator getCompareOperator() {
072    return op;
073  }
074
075  /**
076   * @return the comparator
077   */
078  public ByteArrayComparable getComparator() {
079    return comparator;
080  }
081
082  @Override
083  public boolean filterRowKey(Cell cell) throws IOException {
084    // Impl in FilterBase might do unnecessary copy for Off heap backed Cells.
085    return false;
086  }
087
088  protected boolean compareRow(final CompareOperator op, final ByteArrayComparable comparator,
089                               final Cell cell) {
090    if (op == CompareOperator.NO_OP) {
091      return true;
092    }
093    int compareResult = PrivateCellUtil.compareRow(cell, comparator);
094    return compare(op, compareResult);
095  }
096
097  protected boolean compareFamily(final CompareOperator op, final ByteArrayComparable comparator,
098                                  final Cell cell) {
099    if (op == CompareOperator.NO_OP) {
100      return true;
101    }
102    int compareResult = PrivateCellUtil.compareFamily(cell, comparator);
103    return compare(op, compareResult);
104  }
105
106  protected boolean compareQualifier(final CompareOperator op,
107                                     final ByteArrayComparable comparator, final Cell cell) {
108    // We do not call through to the non-deprecated method for perf reasons.
109    if (op == CompareOperator.NO_OP) {
110      return true;
111    }
112    int compareResult = PrivateCellUtil.compareQualifier(cell, comparator);
113    return compare(op, compareResult);
114  }
115
116  protected boolean compareValue(final CompareOperator op, final ByteArrayComparable comparator,
117                                 final Cell cell) {
118    if (op == CompareOperator.NO_OP) {
119      return true;
120    }
121    int compareResult = PrivateCellUtil.compareValue(cell, comparator);
122    return compare(op, compareResult);
123  }
124
125  static boolean compare(final CompareOperator op, int compareResult) {
126    switch (op) {
127      case LESS:
128        return compareResult <= 0;
129      case LESS_OR_EQUAL:
130        return compareResult < 0;
131      case EQUAL:
132        return compareResult != 0;
133      case NOT_EQUAL:
134        return compareResult == 0;
135      case GREATER_OR_EQUAL:
136        return compareResult > 0;
137      case GREATER:
138        return compareResult >= 0;
139      default:
140        throw new RuntimeException("Unknown Compare op " + op.name());
141    }
142  }
143
144  // returns an array of heterogeneous objects
145  public static ArrayList<Object> extractArguments(ArrayList<byte []> filterArguments) {
146    Preconditions.checkArgument(filterArguments.size() == 2,
147                                "Expected 2 but got: %s", filterArguments.size());
148    CompareOperator op = ParseFilter.createCompareOperator(filterArguments.get(0));
149    ByteArrayComparable comparator = ParseFilter.createComparator(
150      ParseFilter.removeQuotesFromByteArray(filterArguments.get(1)));
151
152    if (comparator instanceof RegexStringComparator ||
153        comparator instanceof SubstringComparator) {
154      if (op != CompareOperator.EQUAL &&
155          op != CompareOperator.NOT_EQUAL) {
156        throw new IllegalArgumentException ("A regexstring comparator and substring comparator" +
157                                            " can only be used with EQUAL and NOT_EQUAL");
158      }
159    }
160    ArrayList<Object> arguments = new ArrayList<>(2);
161    arguments.add(op);
162    arguments.add(comparator);
163    return arguments;
164  }
165
166  /**
167   * @return A pb instance to represent this instance.
168   */
169  FilterProtos.CompareFilter convert() {
170    FilterProtos.CompareFilter.Builder builder =
171      FilterProtos.CompareFilter.newBuilder();
172    HBaseProtos.CompareType compareOp = CompareType.valueOf(this.op.name());
173    builder.setCompareOp(compareOp);
174    if (this.comparator != null) builder.setComparator(ProtobufUtil.toComparator(this.comparator));
175    return builder.build();
176  }
177
178  /**
179   *
180   * @param o
181   * @return true if and only if the fields of the filter that are serialized
182   * are equal to the corresponding fields in other.  Used for testing.
183   */
184  @Override
185  boolean areSerializedFieldsEqual(Filter o) {
186    if (o == this) return true;
187    if (!(o instanceof CompareFilter)) return false;
188    CompareFilter other = (CompareFilter)o;
189    return this.getCompareOperator().equals(other.getCompareOperator()) &&
190      (this.getComparator() == other.getComparator()
191        || this.getComparator().areSerializedFieldsEqual(other.getComparator()));
192  }
193
194  @Override
195  public String toString() {
196    return String.format("%s (%s, %s)",
197        this.getClass().getSimpleName(),
198        this.op.name(),
199        Bytes.toStringBinary(this.comparator.getValue()));
200  }
201
202  @Override
203  public boolean equals(Object obj) {
204    return obj instanceof Filter && areSerializedFieldsEqual((Filter) obj);
205  }
206
207  @Override
208  public int hashCode() {
209    return Objects.hash(this.getComparator(), this.getCompareOperator());
210  }
211}