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