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  @Deprecated
071  @Override
072  public ReturnCode filterKeyValue(final Cell c) throws IOException {
073    return filterCell(c);
074  }
075
076  @Override
077  public ReturnCode filterCell(final Cell ignored) throws IOException {
078    return ReturnCode.INCLUDE;
079  }
080
081  @Override
082  public boolean filterAllRemaining() {
083    return this.rowsAccepted >= this.pageSize;
084  }
085
086  @Override
087  public boolean filterRow() {
088    this.rowsAccepted++;
089    return this.rowsAccepted > this.pageSize;
090  }
091  
092  @Override
093  public boolean hasFilterRow() {
094    return true;
095  }
096
097  public static Filter createFilterFromArguments(ArrayList<byte []> filterArguments) {
098    Preconditions.checkArgument(filterArguments.size() == 1,
099                                "Expected 1 but got: %s", filterArguments.size());
100    long pageSize = ParseFilter.convertByteArrayToLong(filterArguments.get(0));
101    return new PageFilter(pageSize);
102  }
103
104  /**
105   * @return The filter serialized using pb
106   */
107  @Override
108  public byte [] toByteArray() {
109    FilterProtos.PageFilter.Builder builder =
110      FilterProtos.PageFilter.newBuilder();
111    builder.setPageSize(this.pageSize);
112    return builder.build().toByteArray();
113  }
114
115  /**
116   * @param pbBytes A pb serialized {@link PageFilter} instance
117   * @return An instance of {@link PageFilter} made from <code>bytes</code>
118   * @throws DeserializationException
119   * @see #toByteArray
120   */
121  public static PageFilter parseFrom(final byte [] pbBytes)
122  throws DeserializationException {
123    FilterProtos.PageFilter proto;
124    try {
125      proto = FilterProtos.PageFilter.parseFrom(pbBytes);
126    } catch (InvalidProtocolBufferException e) {
127      throw new DeserializationException(e);
128    }
129    return new PageFilter(proto.getPageSize());
130  }
131
132  /**
133   * @param o other Filter to compare with
134   * @return true if and only if the fields of the filter that are serialized are equal to the
135   *         corresponding fields in other.  Used for testing.
136   */
137  @Override
138  boolean areSerializedFieldsEqual(Filter o) {
139    if (o == this) {
140      return true;
141    }
142    if (!(o instanceof PageFilter)) {
143      return false;
144    }
145
146    PageFilter other = (PageFilter)o;
147    return this.getPageSize() == other.getPageSize();
148  }
149
150  @Override
151  public String toString() {
152    return this.getClass().getSimpleName() + " " + this.pageSize;
153  }
154
155  @Override
156  public boolean equals(Object obj) {
157    return obj instanceof Filter && areSerializedFieldsEqual((Filter) obj);
158  }
159
160  @Override
161  public int hashCode() {
162    return Objects.hash(this.pageSize);
163  }
164}