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