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 org.apache.hadoop.hbase.ByteBufferExtendedCell; 023import org.apache.hadoop.hbase.Cell; 024import org.apache.hadoop.hbase.PrivateCellUtil; 025import org.apache.hadoop.hbase.exceptions.DeserializationException; 026import org.apache.hadoop.hbase.util.ByteBufferUtils; 027import org.apache.hadoop.hbase.util.Bytes; 028import org.apache.yetus.audience.InterfaceAudience; 029 030import org.apache.hbase.thirdparty.com.google.common.base.Preconditions; 031import org.apache.hbase.thirdparty.com.google.protobuf.InvalidProtocolBufferException; 032import org.apache.hbase.thirdparty.com.google.protobuf.UnsafeByteOperations; 033 034import org.apache.hadoop.hbase.shaded.protobuf.generated.FilterProtos; 035 036/** 037 * This filter is used for selecting only those keys with columns that matches a particular prefix. 038 * For example, if prefix is 'an', it will pass keys with columns like 'and', 'anti' but not keys 039 * with columns like 'ball', 'act'. 040 */ 041@InterfaceAudience.Public 042public class ColumnPrefixFilter extends FilterBase { 043 protected byte[] prefix = null; 044 045 public ColumnPrefixFilter(final byte[] prefix) { 046 this.prefix = prefix; 047 } 048 049 public byte[] getPrefix() { 050 return prefix; 051 } 052 053 @Override 054 public boolean filterRowKey(Cell cell) throws IOException { 055 // Impl in FilterBase might do unnecessary copy for Off heap backed Cells. 056 return false; 057 } 058 059 @Override 060 public ReturnCode filterCell(final Cell cell) { 061 if (this.prefix == null) { 062 return ReturnCode.INCLUDE; 063 } else { 064 return filterColumn(cell); 065 } 066 } 067 068 public ReturnCode filterColumn(Cell cell) { 069 int qualifierLength = cell.getQualifierLength(); 070 if (qualifierLength < prefix.length) { 071 int cmp = compareQualifierPart(cell, qualifierLength, this.prefix); 072 if (cmp <= 0) { 073 return ReturnCode.SEEK_NEXT_USING_HINT; 074 } else { 075 return ReturnCode.NEXT_ROW; 076 } 077 } else { 078 int cmp = compareQualifierPart(cell, this.prefix.length, this.prefix); 079 if (cmp < 0) { 080 return ReturnCode.SEEK_NEXT_USING_HINT; 081 } else if (cmp > 0) { 082 return ReturnCode.NEXT_ROW; 083 } else { 084 return ReturnCode.INCLUDE; 085 } 086 } 087 } 088 089 private static int compareQualifierPart(Cell cell, int length, byte[] prefix) { 090 if (cell instanceof ByteBufferExtendedCell) { 091 return ByteBufferUtils.compareTo(((ByteBufferExtendedCell) cell).getQualifierByteBuffer(), 092 ((ByteBufferExtendedCell) cell).getQualifierPosition(), length, prefix, 0, length); 093 } 094 return Bytes.compareTo(cell.getQualifierArray(), cell.getQualifierOffset(), length, prefix, 0, 095 length); 096 } 097 098 public static Filter createFilterFromArguments(ArrayList<byte[]> filterArguments) { 099 Preconditions.checkArgument(filterArguments.size() == 1, "Expected 1 but got: %s", 100 filterArguments.size()); 101 byte[] columnPrefix = ParseFilter.removeQuotesFromByteArray(filterArguments.get(0)); 102 return new ColumnPrefixFilter(columnPrefix); 103 } 104 105 /** Returns The filter serialized using pb */ 106 @Override 107 public byte[] toByteArray() { 108 FilterProtos.ColumnPrefixFilter.Builder builder = FilterProtos.ColumnPrefixFilter.newBuilder(); 109 if (this.prefix != null) builder.setPrefix(UnsafeByteOperations.unsafeWrap(this.prefix)); 110 return builder.build().toByteArray(); 111 } 112 113 /** 114 * Parses a serialized representation of the {@link ColumnPrefixFilter} 115 * @param pbBytes A pb serialized {@link ColumnPrefixFilter} instance 116 * @return An instance of {@link ColumnPrefixFilter} made from <code>bytes</code> 117 * @throws DeserializationException if an error occurred 118 * @see #toByteArray 119 */ 120 public static ColumnPrefixFilter parseFrom(final byte[] pbBytes) throws DeserializationException { 121 FilterProtos.ColumnPrefixFilter proto; 122 try { 123 proto = FilterProtos.ColumnPrefixFilter.parseFrom(pbBytes); 124 } catch (InvalidProtocolBufferException e) { 125 throw new DeserializationException(e); 126 } 127 return new ColumnPrefixFilter(proto.getPrefix().toByteArray()); 128 } 129 130 /** 131 * Returns true if and only if the fields of the filter that are serialized are equal to the 132 * corresponding fields in other. Used for testing. 133 */ 134 @Override 135 boolean areSerializedFieldsEqual(Filter o) { 136 if (o == this) { 137 return true; 138 } 139 if (!(o instanceof ColumnPrefixFilter)) { 140 return false; 141 } 142 ColumnPrefixFilter other = (ColumnPrefixFilter) o; 143 return Bytes.equals(this.getPrefix(), other.getPrefix()); 144 } 145 146 @Override 147 public Cell getNextCellHint(Cell cell) { 148 return PrivateCellUtil.createFirstOnRowCol(cell, prefix, 0, prefix.length); 149 } 150 151 @Override 152 public String toString() { 153 return this.getClass().getSimpleName() + " " + Bytes.toStringBinary(this.prefix); 154 } 155 156 @Override 157 public boolean equals(Object obj) { 158 return obj instanceof Filter && areSerializedFieldsEqual((Filter) obj); 159 } 160 161 @Override 162 public int hashCode() { 163 return Bytes.hashCode(this.getPrefix()); 164 } 165}