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