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 @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 * @param pbBytes A pb serialized {@link PageFilter} instance 110 * @return An instance of {@link PageFilter} made from <code>bytes</code> 111 * @see #toByteArray 112 */ 113 public static PageFilter parseFrom(final byte[] pbBytes) throws DeserializationException { 114 FilterProtos.PageFilter proto; 115 try { 116 proto = FilterProtos.PageFilter.parseFrom(pbBytes); 117 } catch (InvalidProtocolBufferException e) { 118 throw new DeserializationException(e); 119 } 120 return new PageFilter(proto.getPageSize()); 121 } 122 123 /** 124 * @param o other Filter to compare with 125 * @return true if and only if the fields of the filter that are serialized are equal to the 126 * corresponding fields in other. Used for testing. 127 */ 128 @Override 129 boolean areSerializedFieldsEqual(Filter o) { 130 if (o == this) { 131 return true; 132 } 133 if (!(o instanceof PageFilter)) { 134 return false; 135 } 136 137 PageFilter other = (PageFilter) o; 138 return this.getPageSize() == other.getPageSize(); 139 } 140 141 @Override 142 public String toString() { 143 return this.getClass().getSimpleName() + " " + this.pageSize; 144 } 145 146 @Override 147 public boolean equals(Object obj) { 148 return obj instanceof Filter && areSerializedFieldsEqual((Filter) obj); 149 } 150 151 @Override 152 public int hashCode() { 153 return Objects.hash(this.pageSize); 154 } 155}