1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.hadoop.hbase.filter;
19
20 import com.google.protobuf.InvalidProtocolBufferException;
21
22 import java.util.ArrayList;
23 import java.util.Arrays;
24 import java.util.Comparator;
25 import java.util.Objects;
26 import java.util.TreeSet;
27
28 import org.apache.hadoop.hbase.Cell;
29 import org.apache.hadoop.hbase.KeyValueUtil;
30 import org.apache.hadoop.hbase.classification.InterfaceAudience;
31 import org.apache.hadoop.hbase.classification.InterfaceStability;
32 import org.apache.hadoop.hbase.exceptions.DeserializationException;
33 import org.apache.hadoop.hbase.protobuf.generated.FilterProtos;
34 import org.apache.hadoop.hbase.util.ByteStringer;
35 import org.apache.hadoop.hbase.util.Bytes;
36 import org.slf4j.Logger;
37 import org.slf4j.LoggerFactory;
38
39
40
41
42
43
44 @InterfaceAudience.Public
45 @InterfaceStability.Stable
46 public class MultipleColumnPrefixFilter extends FilterBase {
47 private static final Logger LOG = LoggerFactory.getLogger(MultipleColumnPrefixFilter.class);
48 protected byte [] hint = null;
49 protected TreeSet<byte []> sortedPrefixes = createTreeSet();
50 private final static int MAX_LOG_PREFIXES = 5;
51
52 public MultipleColumnPrefixFilter(final byte [][] prefixes) {
53 if (prefixes != null) {
54 for (byte[] prefix : prefixes) {
55 if (!sortedPrefixes.add(prefix)) {
56 LOG.error("prefix {} is repeated", Bytes.toString(prefix));
57 throw new IllegalArgumentException("prefixes must be distinct");
58 }
59 }
60 }
61 }
62
63 public byte [][] getPrefix() {
64 int count = 0;
65 byte [][] temp = new byte [sortedPrefixes.size()][];
66 for (byte [] prefixes : sortedPrefixes) {
67 temp [count++] = prefixes;
68 }
69 return temp;
70 }
71
72 @Override
73 public ReturnCode filterKeyValue(Cell kv) {
74 if (sortedPrefixes.size() == 0 || kv.getQualifierArray() == null) {
75 return ReturnCode.INCLUDE;
76 } else {
77 return filterColumn(kv.getQualifierArray(), kv.getQualifierOffset(), kv.getQualifierLength());
78 }
79 }
80
81
82
83 @Override
84 public Cell transformCell(Cell v) {
85 return v;
86 }
87
88 public ReturnCode filterColumn(byte[] buffer, int qualifierOffset, int qualifierLength) {
89 byte [] qualifier = Arrays.copyOfRange(buffer, qualifierOffset,
90 qualifierLength + qualifierOffset);
91 TreeSet<byte []> lesserOrEqualPrefixes =
92 (TreeSet<byte []>) sortedPrefixes.headSet(qualifier, true);
93
94 if (lesserOrEqualPrefixes.size() != 0) {
95 byte [] largestPrefixSmallerThanQualifier = lesserOrEqualPrefixes.last();
96
97 if (Bytes.startsWith(qualifier, largestPrefixSmallerThanQualifier)) {
98 return ReturnCode.INCLUDE;
99 }
100
101 if (lesserOrEqualPrefixes.size() == sortedPrefixes.size()) {
102 return ReturnCode.NEXT_ROW;
103 } else {
104 hint = sortedPrefixes.higher(largestPrefixSmallerThanQualifier);
105 return ReturnCode.SEEK_NEXT_USING_HINT;
106 }
107 } else {
108 hint = sortedPrefixes.first();
109 return ReturnCode.SEEK_NEXT_USING_HINT;
110 }
111 }
112
113 public static Filter createFilterFromArguments(ArrayList<byte []> filterArguments) {
114 byte [][] prefixes = new byte [filterArguments.size()][];
115 for (int i = 0 ; i < filterArguments.size(); i++) {
116 byte [] columnPrefix = ParseFilter.removeQuotesFromByteArray(filterArguments.get(i));
117 prefixes[i] = columnPrefix;
118 }
119 return new MultipleColumnPrefixFilter(prefixes);
120 }
121
122
123
124
125 @Override
126 public byte [] toByteArray() {
127 FilterProtos.MultipleColumnPrefixFilter.Builder builder =
128 FilterProtos.MultipleColumnPrefixFilter.newBuilder();
129 for (byte [] element : sortedPrefixes) {
130 if (element != null) builder.addSortedPrefixes(ByteStringer.wrap(element));
131 }
132 return builder.build().toByteArray();
133 }
134
135
136
137
138
139
140
141 public static MultipleColumnPrefixFilter parseFrom(final byte [] pbBytes)
142 throws DeserializationException {
143 FilterProtos.MultipleColumnPrefixFilter proto;
144 try {
145 proto = FilterProtos.MultipleColumnPrefixFilter.parseFrom(pbBytes);
146 } catch (InvalidProtocolBufferException e) {
147 throw new DeserializationException(e);
148 }
149 int numPrefixes = proto.getSortedPrefixesCount();
150 byte [][] prefixes = new byte[numPrefixes][];
151 for (int i = 0; i < numPrefixes; ++i) {
152 prefixes[i] = proto.getSortedPrefixes(i).toByteArray();
153 }
154
155 return new MultipleColumnPrefixFilter(prefixes);
156 }
157
158
159
160
161
162
163 @Override
164 boolean areSerializedFieldsEqual(Filter o) {
165 if (o == this) return true;
166 if (!(o instanceof MultipleColumnPrefixFilter)) return false;
167
168 MultipleColumnPrefixFilter other = (MultipleColumnPrefixFilter)o;
169 return this.sortedPrefixes.equals(other.sortedPrefixes);
170 }
171
172 @Override
173 public Cell getNextCellHint(Cell kv) {
174 return KeyValueUtil.createFirstOnRow(
175 kv.getRowArray(), kv.getRowOffset(), kv.getRowLength(), kv.getFamilyArray(),
176 kv.getFamilyOffset(), kv.getFamilyLength(), hint, 0, hint.length);
177 }
178
179 public TreeSet<byte []> createTreeSet() {
180 return new TreeSet<byte []>(new Comparator<Object>() {
181 @Override
182 public int compare (Object o1, Object o2) {
183 if (o1 == null || o2 == null)
184 throw new IllegalArgumentException ("prefixes can't be null");
185
186 byte [] b1 = (byte []) o1;
187 byte [] b2 = (byte []) o2;
188 return Bytes.compareTo (b1, 0, b1.length, b2, 0, b2.length);
189 }
190 });
191 }
192
193 @Override
194 public String toString() {
195 return toString(MAX_LOG_PREFIXES);
196 }
197
198 protected String toString(int maxPrefixes) {
199 StringBuilder prefixes = new StringBuilder();
200
201 int count = 0;
202 for (byte[] ba : this.sortedPrefixes) {
203 if (count >= maxPrefixes) {
204 break;
205 }
206 ++count;
207 prefixes.append(Bytes.toStringBinary(ba));
208 if (count < this.sortedPrefixes.size() && count < maxPrefixes) {
209 prefixes.append(", ");
210 }
211 }
212
213 return String.format("%s (%d/%d): [%s]", this.getClass().getSimpleName(),
214 count, this.sortedPrefixes.size(), prefixes.toString());
215 }
216
217 @Override
218 public boolean equals(Object obj) {
219 return obj instanceof Filter && areSerializedFieldsEqual((Filter) obj);
220 }
221
222 @Override
223 public int hashCode() {
224 return Objects.hash(this.sortedPrefixes);
225 }
226 }