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.exceptions.DeserializationException;
025import org.apache.yetus.audience.InterfaceAudience;
026
027import org.apache.hbase.thirdparty.com.google.common.base.Preconditions;
028import org.apache.hbase.thirdparty.com.google.protobuf.InvalidProtocolBufferException;
029
030import org.apache.hadoop.hbase.shaded.protobuf.generated.FilterProtos;
031
032/**
033 * Implementation of Filter interface that limits results to a specific page size. It terminates
034 * scanning once the number of filter-passed rows is > the given page size.
035 * <p>
036 * Note that this filter cannot guarantee that the number of results returned to a client are &lt;=
037 * page size. This is because the filter is applied separately on different region servers. It does
038 * however optimize the scan of individual HRegions by making sure that the page size is never
039 * exceeded locally.
040 */
041@InterfaceAudience.Public
042public class PageFilter extends FilterBase {
043  private long pageSize = Long.MAX_VALUE;
044  private int rowsAccepted = 0;
045
046  /**
047   * Constructor that takes a maximum page size.
048   * @param pageSize Maximum result size.
049   */
050  public PageFilter(final long pageSize) {
051    Preconditions.checkArgument(pageSize >= 0, "must be positive %s", pageSize);
052    this.pageSize = pageSize;
053  }
054
055  public long getPageSize() {
056    return pageSize;
057  }
058
059  @Override
060  public boolean filterRowKey(Cell cell) throws IOException {
061    // Impl in FilterBase might do unnecessary copy for Off heap backed Cells.
062    if (filterAllRemaining()) return true;
063    return false;
064  }
065
066  @Deprecated
067  @Override
068  public ReturnCode filterKeyValue(final Cell c) throws IOException {
069    return filterCell(c);
070  }
071
072  @Override
073  public ReturnCode filterCell(final Cell ignored) throws IOException {
074    return ReturnCode.INCLUDE;
075  }
076
077  @Override
078  public boolean filterAllRemaining() {
079    return this.rowsAccepted >= this.pageSize;
080  }
081
082  @Override
083  public boolean filterRow() {
084    this.rowsAccepted++;
085    return this.rowsAccepted > this.pageSize;
086  }
087
088  @Override
089  public boolean hasFilterRow() {
090    return true;
091  }
092
093  public static Filter createFilterFromArguments(ArrayList<byte[]> filterArguments) {
094    Preconditions.checkArgument(filterArguments.size() == 1, "Expected 1 but got: %s",
095      filterArguments.size());
096    long pageSize = ParseFilter.convertByteArrayToLong(filterArguments.get(0));
097    return new PageFilter(pageSize);
098  }
099
100  /** Returns The filter serialized using pb */
101  @Override
102  public byte[] toByteArray() {
103    FilterProtos.PageFilter.Builder builder = FilterProtos.PageFilter.newBuilder();
104    builder.setPageSize(this.pageSize);
105    return builder.build().toByteArray();
106  }
107
108  /**
109   * Parse a serialized representation of {@link PageFilter}
110   * @param pbBytes A pb serialized {@link PageFilter} instance
111   * @return An instance of {@link PageFilter} made from <code>bytes</code>
112   * @throws DeserializationException if an error occurred
113   * @see #toByteArray
114   */
115  public static PageFilter parseFrom(final byte[] pbBytes) throws DeserializationException {
116    FilterProtos.PageFilter proto;
117    try {
118      proto = FilterProtos.PageFilter.parseFrom(pbBytes);
119    } catch (InvalidProtocolBufferException e) {
120      throw new DeserializationException(e);
121    }
122    return new PageFilter(proto.getPageSize());
123  }
124
125  /**
126   * Returns true if and only if the fields of the filter that are serialized are equal to the
127   * corresponding fields in other. Used for testing.
128   */
129  @Override
130  boolean areSerializedFieldsEqual(Filter o) {
131    if (o == this) {
132      return true;
133    }
134    if (!(o instanceof PageFilter)) {
135      return false;
136    }
137
138    PageFilter other = (PageFilter) o;
139    return this.getPageSize() == other.getPageSize();
140  }
141
142  @Override
143  public String toString() {
144    return this.getClass().getSimpleName() + " " + this.pageSize;
145  }
146
147  @Override
148  public boolean equals(Object obj) {
149    return obj instanceof Filter && areSerializedFieldsEqual((Filter) obj);
150  }
151
152  @Override
153  public int hashCode() {
154    return Objects.hash(this.pageSize);
155  }
156}