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.Objects; 022import org.apache.hadoop.hbase.Cell; 023import org.apache.hadoop.hbase.exceptions.DeserializationException; 024import org.apache.yetus.audience.InterfaceAudience; 025 026import org.apache.hbase.thirdparty.com.google.protobuf.InvalidProtocolBufferException; 027 028import org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil; 029import org.apache.hadoop.hbase.shaded.protobuf.generated.FilterProtos; 030 031/** 032 * A wrapper filter that filters an entire row if any of the Cell checks do not pass. 033 * <p> 034 * For example, if all columns in a row represent weights of different things, with the values being 035 * the actual weights, and we want to filter out the entire row if any of its weights are zero. In 036 * this case, we want to prevent rows from being emitted if a single key is filtered. Combine this 037 * filter with a {@link ValueFilter}: 038 * </p> 039 * <p> 040 * <code> 041 * scan.setFilter(new SkipFilter(new ValueFilter(CompareOp.NOT_EQUAL, 042 * new BinaryComparator(Bytes.toBytes(0)))); 043 * </code> Any row which contained a column whose value was 0 will be filtered out (since 044 * ValueFilter will not pass that Cell). Without this filter, the other non-zero valued columns in 045 * the row would still be emitted. 046 * </p> 047 */ 048@InterfaceAudience.Public 049public class SkipFilter extends FilterBase { 050 private boolean filterRow = false; 051 private Filter filter; 052 053 public SkipFilter(Filter filter) { 054 this.filter = filter; 055 } 056 057 public Filter getFilter() { 058 return filter; 059 } 060 061 @Override 062 public void reset() throws IOException { 063 filter.reset(); 064 filterRow = false; 065 } 066 067 private void changeFR(boolean value) { 068 filterRow = filterRow || value; 069 } 070 071 @Override 072 public boolean filterRowKey(Cell cell) throws IOException { 073 // Impl in FilterBase might do unnecessary copy for Off heap backed Cells. 074 return false; 075 } 076 077 @Override 078 public ReturnCode filterCell(final Cell c) throws IOException { 079 ReturnCode rc = filter.filterCell(c); 080 changeFR(rc != ReturnCode.INCLUDE); 081 return rc; 082 } 083 084 @Override 085 public Cell transformCell(Cell v) throws IOException { 086 return filter.transformCell(v); 087 } 088 089 @Override 090 public boolean filterRow() { 091 return filterRow; 092 } 093 094 @Override 095 public boolean hasFilterRow() { 096 return true; 097 } 098 099 /** Returns The filter serialized using pb */ 100 @Override 101 public byte[] toByteArray() throws IOException { 102 FilterProtos.SkipFilter.Builder builder = FilterProtos.SkipFilter.newBuilder(); 103 builder.setFilter(ProtobufUtil.toFilter(this.filter)); 104 return builder.build().toByteArray(); 105 } 106 107 /** 108 * Parse a serialized representation of {@link SkipFilter} 109 * @param pbBytes A pb serialized {@link SkipFilter} instance 110 * @return An instance of {@link SkipFilter} made from <code>bytes</code> 111 * @throws DeserializationException if an error occurred 112 * @see #toByteArray 113 */ 114 public static SkipFilter parseFrom(final byte[] pbBytes) throws DeserializationException { 115 FilterProtos.SkipFilter proto; 116 try { 117 proto = FilterProtos.SkipFilter.parseFrom(pbBytes); 118 } catch (InvalidProtocolBufferException e) { 119 throw new DeserializationException(e); 120 } 121 try { 122 return new SkipFilter(ProtobufUtil.toFilter(proto.getFilter())); 123 } catch (IOException ioe) { 124 throw new DeserializationException(ioe); 125 } 126 } 127 128 /** 129 * Returns true if and only if the fields of the filter that are serialized are equal to the 130 * corresponding fields in other. Used for testing. 131 */ 132 @Override 133 boolean areSerializedFieldsEqual(Filter o) { 134 if (o == this) { 135 return true; 136 } 137 if (!(o instanceof SkipFilter)) { 138 return false; 139 } 140 SkipFilter other = (SkipFilter) o; 141 return getFilter().areSerializedFieldsEqual(other.getFilter()); 142 } 143 144 @Override 145 public boolean isFamilyEssential(byte[] name) throws IOException { 146 return filter.isFamilyEssential(name); 147 } 148 149 @Override 150 public String toString() { 151 return this.getClass().getSimpleName() + " " + this.filter.toString(); 152 } 153 154 @Override 155 public boolean equals(Object obj) { 156 return obj instanceof Filter && areSerializedFieldsEqual((Filter) obj); 157 } 158 159 @Override 160 public int hashCode() { 161 return Objects.hash(this.filter); 162 } 163}