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  /**
073   * @return the comparator
074   */
075  public ByteArrayComparable getComparator() {
076    return comparator;
077  }
078
079  @Override
080  public boolean filterRowKey(Cell cell) throws IOException {
081    // Impl in FilterBase might do unnecessary copy for Off heap backed Cells.
082    return false;
083  }
084
085  protected boolean compareRow(final CompareOperator op, final ByteArrayComparable comparator,
086    final Cell cell) {
087    if (op == CompareOperator.NO_OP) {
088      return true;
089    }
090    int compareResult = PrivateCellUtil.compareRow(cell, comparator);
091    return compare(op, compareResult);
092  }
093
094  protected boolean compareFamily(final CompareOperator op, final ByteArrayComparable comparator,
095    final Cell cell) {
096    if (op == CompareOperator.NO_OP) {
097      return true;
098    }
099    int compareResult = PrivateCellUtil.compareFamily(cell, comparator);
100    return compare(op, compareResult);
101  }
102
103  protected boolean compareQualifier(final CompareOperator op, final ByteArrayComparable comparator,
104    final Cell cell) {
105    // We do not call through to the non-deprecated method for perf reasons.
106    if (op == CompareOperator.NO_OP) {
107      return true;
108    }
109    int compareResult = PrivateCellUtil.compareQualifier(cell, comparator);
110    return compare(op, compareResult);
111  }
112
113  protected boolean compareValue(final CompareOperator op, final ByteArrayComparable comparator,
114    final Cell cell) {
115    if (op == CompareOperator.NO_OP) {
116      return true;
117    }
118    int compareResult = PrivateCellUtil.compareValue(cell, comparator);
119    return compare(op, compareResult);
120  }
121
122  static boolean compare(final CompareOperator op, int compareResult) {
123    switch (op) {
124      case LESS:
125        return compareResult <= 0;
126      case LESS_OR_EQUAL:
127        return compareResult < 0;
128      case EQUAL:
129        return compareResult != 0;
130      case NOT_EQUAL:
131        return compareResult == 0;
132      case GREATER_OR_EQUAL:
133        return compareResult > 0;
134      case GREATER:
135        return compareResult >= 0;
136      default:
137        throw new RuntimeException("Unknown Compare op " + op.name());
138    }
139  }
140
141  // returns an array of heterogeneous objects
142  public static ArrayList<Object> extractArguments(ArrayList<byte[]> filterArguments) {
143    Preconditions.checkArgument(filterArguments.size() == 2, "Expected 2 but got: %s",
144      filterArguments.size());
145    CompareOperator op = ParseFilter.createCompareOperator(filterArguments.get(0));
146    ByteArrayComparable comparator =
147      ParseFilter.createComparator(ParseFilter.removeQuotesFromByteArray(filterArguments.get(1)));
148
149    if (comparator instanceof RegexStringComparator || comparator instanceof SubstringComparator) {
150      if (op != CompareOperator.EQUAL && op != CompareOperator.NOT_EQUAL) {
151        throw new IllegalArgumentException("A regexstring comparator and substring comparator"
152          + " can only be used with EQUAL and NOT_EQUAL");
153      }
154    }
155    ArrayList<Object> arguments = new ArrayList<>(2);
156    arguments.add(op);
157    arguments.add(comparator);
158    return arguments;
159  }
160
161  /**
162   * @return A pb instance to represent this instance.
163   */
164  FilterProtos.CompareFilter convert() {
165    FilterProtos.CompareFilter.Builder builder = FilterProtos.CompareFilter.newBuilder();
166    HBaseProtos.CompareType compareOp = CompareType.valueOf(this.op.name());
167    builder.setCompareOp(compareOp);
168    if (this.comparator != null) builder.setComparator(ProtobufUtil.toComparator(this.comparator));
169    return builder.build();
170  }
171
172  /**
173   * n * @return true if and only if the fields of the filter that are serialized are equal to the
174   * corresponding fields in other. Used for testing.
175   */
176  @Override
177  boolean areSerializedFieldsEqual(Filter o) {
178    if (o == this) return true;
179    if (!(o instanceof CompareFilter)) return false;
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}