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 @Deprecated 060 @Override 061 public ReturnCode filterKeyValue(final Cell c) { 062 return filterCell(c); 063 } 064 065 @Override 066 public ReturnCode filterCell(final Cell cell) { 067 if (this.prefix == null) { 068 return ReturnCode.INCLUDE; 069 } else { 070 return filterColumn(cell); 071 } 072 } 073 074 public ReturnCode filterColumn(Cell cell) { 075 int qualifierLength = cell.getQualifierLength(); 076 if (qualifierLength < prefix.length) { 077 int cmp = compareQualifierPart(cell, qualifierLength, this.prefix); 078 if (cmp <= 0) { 079 return ReturnCode.SEEK_NEXT_USING_HINT; 080 } else { 081 return ReturnCode.NEXT_ROW; 082 } 083 } else { 084 int cmp = compareQualifierPart(cell, this.prefix.length, this.prefix); 085 if (cmp < 0) { 086 return ReturnCode.SEEK_NEXT_USING_HINT; 087 } else if (cmp > 0) { 088 return ReturnCode.NEXT_ROW; 089 } else { 090 return ReturnCode.INCLUDE; 091 } 092 } 093 } 094 095 private static int compareQualifierPart(Cell cell, int length, byte[] prefix) { 096 if (cell instanceof ByteBufferExtendedCell) { 097 return ByteBufferUtils.compareTo(((ByteBufferExtendedCell) cell).getQualifierByteBuffer(), 098 ((ByteBufferExtendedCell) cell).getQualifierPosition(), length, prefix, 0, length); 099 } 100 return Bytes.compareTo(cell.getQualifierArray(), cell.getQualifierOffset(), length, prefix, 0, 101 length); 102 } 103 104 public static Filter createFilterFromArguments(ArrayList<byte[]> filterArguments) { 105 Preconditions.checkArgument(filterArguments.size() == 1, "Expected 1 but got: %s", 106 filterArguments.size()); 107 byte[] columnPrefix = ParseFilter.removeQuotesFromByteArray(filterArguments.get(0)); 108 return new ColumnPrefixFilter(columnPrefix); 109 } 110 111 /** Returns The filter serialized using pb */ 112 @Override 113 public byte[] toByteArray() { 114 FilterProtos.ColumnPrefixFilter.Builder builder = FilterProtos.ColumnPrefixFilter.newBuilder(); 115 if (this.prefix != null) builder.setPrefix(UnsafeByteOperations.unsafeWrap(this.prefix)); 116 return builder.build().toByteArray(); 117 } 118 119 /** 120 * Parses a serialized representation of the {@link ColumnPrefixFilter} 121 * @param pbBytes A pb serialized {@link ColumnPrefixFilter} instance 122 * @return An instance of {@link ColumnPrefixFilter} made from <code>bytes</code> 123 * @throws DeserializationException if an error occurred 124 * @see #toByteArray 125 */ 126 public static ColumnPrefixFilter parseFrom(final byte[] pbBytes) throws DeserializationException { 127 FilterProtos.ColumnPrefixFilter proto; 128 try { 129 proto = FilterProtos.ColumnPrefixFilter.parseFrom(pbBytes); 130 } catch (InvalidProtocolBufferException e) { 131 throw new DeserializationException(e); 132 } 133 return new ColumnPrefixFilter(proto.getPrefix().toByteArray()); 134 } 135 136 /** 137 * Returns true if and only if the fields of the filter that are serialized are equal to the 138 * corresponding fields in other. Used for testing. 139 */ 140 @Override 141 boolean areSerializedFieldsEqual(Filter o) { 142 if (o == this) { 143 return true; 144 } 145 if (!(o instanceof ColumnPrefixFilter)) { 146 return false; 147 } 148 ColumnPrefixFilter other = (ColumnPrefixFilter) o; 149 return Bytes.equals(this.getPrefix(), other.getPrefix()); 150 } 151 152 @Override 153 public Cell getNextCellHint(Cell cell) { 154 return PrivateCellUtil.createFirstOnRowCol(cell, prefix, 0, prefix.length); 155 } 156 157 @Override 158 public String toString() { 159 return this.getClass().getSimpleName() + " " + Bytes.toStringBinary(this.prefix); 160 } 161 162 @Override 163 public boolean equals(Object obj) { 164 return obj instanceof Filter && areSerializedFieldsEqual((Filter) obj); 165 } 166 167 @Override 168 public int hashCode() { 169 return Bytes.hashCode(this.getPrefix()); 170 } 171}