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