001/* 002 * 003 * Licensed to the Apache Software Foundation (ASF) under one 004 * or more contributor license agreements. See the NOTICE file 005 * distributed with this work for additional information 006 * regarding copyright ownership. The ASF licenses this file 007 * to you under the Apache License, Version 2.0 (the 008 * "License"); you may not use this file except in compliance 009 * with the License. You may obtain a copy of the License at 010 * 011 * http://www.apache.org/licenses/LICENSE-2.0 012 * 013 * Unless required by applicable law or agreed to in writing, software 014 * distributed under the License is distributed on an "AS IS" BASIS, 015 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 016 * See the License for the specific language governing permissions and 017 * limitations under the License. 018 */ 019 020package org.apache.hadoop.hbase.filter; 021 022import java.io.IOException; 023import java.util.ArrayList; 024 025import org.apache.hadoop.hbase.ByteBufferExtendedCell; 026import org.apache.hadoop.hbase.Cell; 027import org.apache.hadoop.hbase.PrivateCellUtil; 028import org.apache.yetus.audience.InterfaceAudience; 029import org.apache.hadoop.hbase.exceptions.DeserializationException; 030import org.apache.hadoop.hbase.shaded.protobuf.generated.FilterProtos; 031import org.apache.hadoop.hbase.util.ByteBufferUtils; 032import org.apache.hadoop.hbase.util.Bytes; 033 034import org.apache.hbase.thirdparty.com.google.common.base.Preconditions; 035import org.apache.hbase.thirdparty.com.google.protobuf.InvalidProtocolBufferException; 036import org.apache.hbase.thirdparty.com.google.protobuf.UnsafeByteOperations; 037 038/** 039 * This filter is used for selecting only those keys with columns that matches 040 * a particular prefix. For example, if prefix is 'an', it will pass keys with 041 * columns like 'and', 'anti' but not keys with columns like 'ball', 'act'. 042 */ 043@InterfaceAudience.Public 044public class ColumnPrefixFilter extends FilterBase { 045 protected byte [] prefix = null; 046 047 public ColumnPrefixFilter(final byte [] prefix) { 048 this.prefix = prefix; 049 } 050 051 public byte[] getPrefix() { 052 return prefix; 053 } 054 055 @Override 056 public boolean filterRowKey(Cell cell) throws IOException { 057 // Impl in FilterBase might do unnecessary copy for Off heap backed Cells. 058 return false; 059 } 060 061 @Deprecated 062 @Override 063 public ReturnCode filterKeyValue(final Cell c) { 064 return filterCell(c); 065 } 066 067 @Override 068 public ReturnCode filterCell(final Cell cell) { 069 if (this.prefix == null) { 070 return ReturnCode.INCLUDE; 071 } else { 072 return filterColumn(cell); 073 } 074 } 075 076 public ReturnCode filterColumn(Cell cell) { 077 int qualifierLength = cell.getQualifierLength(); 078 if (qualifierLength < prefix.length) { 079 int cmp = compareQualifierPart(cell, qualifierLength, this.prefix); 080 if (cmp <= 0) { 081 return ReturnCode.SEEK_NEXT_USING_HINT; 082 } else { 083 return ReturnCode.NEXT_ROW; 084 } 085 } else { 086 int cmp = compareQualifierPart(cell, this.prefix.length, this.prefix); 087 if (cmp < 0) { 088 return ReturnCode.SEEK_NEXT_USING_HINT; 089 } else if (cmp > 0) { 090 return ReturnCode.NEXT_ROW; 091 } else { 092 return ReturnCode.INCLUDE; 093 } 094 } 095 } 096 097 private static int compareQualifierPart(Cell cell, int length, byte[] prefix) { 098 if (cell instanceof ByteBufferExtendedCell) { 099 return ByteBufferUtils.compareTo(((ByteBufferExtendedCell) cell).getQualifierByteBuffer(), 100 ((ByteBufferExtendedCell) cell).getQualifierPosition(), length, prefix, 0, length); 101 } 102 return Bytes.compareTo(cell.getQualifierArray(), cell.getQualifierOffset(), length, prefix, 0, 103 length); 104 } 105 106 public static Filter createFilterFromArguments(ArrayList<byte []> filterArguments) { 107 Preconditions.checkArgument(filterArguments.size() == 1, 108 "Expected 1 but got: %s", filterArguments.size()); 109 byte [] columnPrefix = ParseFilter.removeQuotesFromByteArray(filterArguments.get(0)); 110 return new ColumnPrefixFilter(columnPrefix); 111 } 112 113 /** 114 * @return The filter serialized using pb 115 */ 116 @Override 117 public byte [] toByteArray() { 118 FilterProtos.ColumnPrefixFilter.Builder builder = 119 FilterProtos.ColumnPrefixFilter.newBuilder(); 120 if (this.prefix != null) builder.setPrefix(UnsafeByteOperations.unsafeWrap(this.prefix)); 121 return builder.build().toByteArray(); 122 } 123 124 /** 125 * @param pbBytes A pb serialized {@link ColumnPrefixFilter} instance 126 * @return An instance of {@link ColumnPrefixFilter} made from <code>bytes</code> 127 * @throws org.apache.hadoop.hbase.exceptions.DeserializationException 128 * @see #toByteArray 129 */ 130 public static ColumnPrefixFilter parseFrom(final byte [] pbBytes) 131 throws DeserializationException { 132 FilterProtos.ColumnPrefixFilter proto; 133 try { 134 proto = FilterProtos.ColumnPrefixFilter.parseFrom(pbBytes); 135 } catch (InvalidProtocolBufferException e) { 136 throw new DeserializationException(e); 137 } 138 return new ColumnPrefixFilter(proto.getPrefix().toByteArray()); 139 } 140 141 /** 142 * @param o the other filter to compare with 143 * @return true if and only if the fields of the filter that are serialized 144 * are equal to the corresponding fields in other. Used for testing. 145 */ 146 @Override 147 boolean areSerializedFieldsEqual(Filter o) { 148 if (o == this) return true; 149 if (!(o instanceof ColumnPrefixFilter)) return false; 150 151 ColumnPrefixFilter other = (ColumnPrefixFilter)o; 152 return Bytes.equals(this.getPrefix(), other.getPrefix()); 153 } 154 155 @Override 156 public Cell getNextCellHint(Cell cell) { 157 return PrivateCellUtil.createFirstOnRowCol(cell, prefix, 0, prefix.length); 158 } 159 160 @Override 161 public String toString() { 162 return this.getClass().getSimpleName() + " " + Bytes.toStringBinary(this.prefix); 163 } 164 165 @Override 166 public boolean equals(Object obj) { 167 return obj instanceof Filter && areSerializedFieldsEqual((Filter) obj); 168 } 169 170 @Override 171 public int hashCode() { 172 return Bytes.hashCode(this.getPrefix()); 173 } 174}