1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.apache.hadoop.hbase.filter;
21
22 import com.google.common.base.Preconditions;
23 import com.google.protobuf.ByteString;
24 import com.google.protobuf.InvalidProtocolBufferException;
25 import org.apache.commons.logging.Log;
26 import org.apache.commons.logging.LogFactory;
27 import org.apache.hadoop.classification.InterfaceAudience;
28 import org.apache.hadoop.classification.InterfaceStability;
29 import org.apache.hadoop.hbase.KeyValue;
30 import org.apache.hadoop.hbase.client.Scan;
31 import org.apache.hadoop.hbase.exceptions.DeserializationException;
32 import org.apache.hadoop.hbase.filter.CompareFilter.CompareOp;
33 import org.apache.hadoop.hbase.protobuf.ProtobufUtil;
34 import org.apache.hadoop.hbase.protobuf.generated.FilterProtos;
35 import org.apache.hadoop.hbase.protobuf.generated.HBaseProtos;
36 import org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.CompareType;
37 import org.apache.hadoop.hbase.util.Bytes;
38
39 import java.io.IOException;
40 import java.util.ArrayList;
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69 @InterfaceAudience.Public
70 @InterfaceStability.Stable
71 public class SingleColumnValueFilter extends FilterBase {
72 static final Log LOG = LogFactory.getLog(SingleColumnValueFilter.class);
73
74 protected byte [] columnFamily;
75 protected byte [] columnQualifier;
76 protected CompareOp compareOp;
77 protected ByteArrayComparable comparator;
78 protected boolean foundColumn = false;
79 protected boolean matchedColumn = false;
80 protected boolean filterIfMissing = false;
81 protected boolean latestVersionOnly = true;
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97 public SingleColumnValueFilter(final byte [] family, final byte [] qualifier,
98 final CompareOp compareOp, final byte[] value) {
99 this(family, qualifier, compareOp, new BinaryComparator(value));
100 }
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116 public SingleColumnValueFilter(final byte [] family, final byte [] qualifier,
117 final CompareOp compareOp, final ByteArrayComparable comparator) {
118 this.columnFamily = family;
119 this.columnQualifier = qualifier;
120 this.compareOp = compareOp;
121 this.comparator = comparator;
122 }
123
124
125
126
127
128
129
130
131
132
133 protected SingleColumnValueFilter(final byte[] family, final byte[] qualifier,
134 final CompareOp compareOp, ByteArrayComparable comparator, final boolean filterIfMissing,
135 final boolean latestVersionOnly) {
136 this(family, qualifier, compareOp, comparator);
137 this.filterIfMissing = filterIfMissing;
138 this.latestVersionOnly = latestVersionOnly;
139 }
140
141
142
143
144 public CompareOp getOperator() {
145 return compareOp;
146 }
147
148
149
150
151 public ByteArrayComparable getComparator() {
152 return comparator;
153 }
154
155
156
157
158 public byte[] getFamily() {
159 return columnFamily;
160 }
161
162
163
164
165 public byte[] getQualifier() {
166 return columnQualifier;
167 }
168
169 public ReturnCode filterKeyValue(KeyValue keyValue) {
170
171 if (this.matchedColumn) {
172
173 return ReturnCode.INCLUDE;
174 } else if (this.latestVersionOnly && this.foundColumn) {
175
176 return ReturnCode.NEXT_ROW;
177 }
178 if (!keyValue.matchingColumn(this.columnFamily, this.columnQualifier)) {
179 return ReturnCode.INCLUDE;
180 }
181 foundColumn = true;
182 if (filterColumnValue(keyValue.getBuffer(),
183 keyValue.getValueOffset(), keyValue.getValueLength())) {
184 return this.latestVersionOnly? ReturnCode.NEXT_ROW: ReturnCode.INCLUDE;
185 }
186 this.matchedColumn = true;
187 return ReturnCode.INCLUDE;
188 }
189
190 private boolean filterColumnValue(final byte [] data, final int offset,
191 final int length) {
192 int compareResult = this.comparator.compareTo(data, offset, length);
193 switch (this.compareOp) {
194 case LESS:
195 return compareResult <= 0;
196 case LESS_OR_EQUAL:
197 return compareResult < 0;
198 case EQUAL:
199 return compareResult != 0;
200 case NOT_EQUAL:
201 return compareResult == 0;
202 case GREATER_OR_EQUAL:
203 return compareResult > 0;
204 case GREATER:
205 return compareResult >= 0;
206 default:
207 throw new RuntimeException("Unknown Compare op " + compareOp.name());
208 }
209 }
210
211 public boolean filterRow() {
212
213
214 return this.foundColumn? !this.matchedColumn: this.filterIfMissing;
215 }
216
217 public boolean hasFilterRow() {
218 return true;
219 }
220
221 public void reset() {
222 foundColumn = false;
223 matchedColumn = false;
224 }
225
226
227
228
229
230
231 public boolean getFilterIfMissing() {
232 return filterIfMissing;
233 }
234
235
236
237
238
239
240
241
242
243 public void setFilterIfMissing(boolean filterIfMissing) {
244 this.filterIfMissing = filterIfMissing;
245 }
246
247
248
249
250
251
252
253
254 public boolean getLatestVersionOnly() {
255 return latestVersionOnly;
256 }
257
258
259
260
261
262
263
264
265 public void setLatestVersionOnly(boolean latestVersionOnly) {
266 this.latestVersionOnly = latestVersionOnly;
267 }
268
269 public static Filter createFilterFromArguments(ArrayList<byte []> filterArguments) {
270 Preconditions.checkArgument(filterArguments.size() == 4 || filterArguments.size() == 6,
271 "Expected 4 or 6 but got: %s", filterArguments.size());
272 byte [] family = ParseFilter.removeQuotesFromByteArray(filterArguments.get(0));
273 byte [] qualifier = ParseFilter.removeQuotesFromByteArray(filterArguments.get(1));
274 CompareOp compareOp = ParseFilter.createCompareOp(filterArguments.get(2));
275 ByteArrayComparable comparator = ParseFilter.createComparator(
276 ParseFilter.removeQuotesFromByteArray(filterArguments.get(3)));
277
278 if (comparator instanceof RegexStringComparator ||
279 comparator instanceof SubstringComparator) {
280 if (compareOp != CompareOp.EQUAL &&
281 compareOp != CompareOp.NOT_EQUAL) {
282 throw new IllegalArgumentException ("A regexstring comparator and substring comparator " +
283 "can only be used with EQUAL and NOT_EQUAL");
284 }
285 }
286
287 SingleColumnValueFilter filter = new SingleColumnValueFilter(family, qualifier,
288 compareOp, comparator);
289
290 if (filterArguments.size() == 6) {
291 boolean filterIfMissing = ParseFilter.convertByteArrayToBoolean(filterArguments.get(4));
292 boolean latestVersionOnly = ParseFilter.convertByteArrayToBoolean(filterArguments.get(5));
293 filter.setFilterIfMissing(filterIfMissing);
294 filter.setLatestVersionOnly(latestVersionOnly);
295 }
296 return filter;
297 }
298
299 FilterProtos.SingleColumnValueFilter convert() {
300 FilterProtos.SingleColumnValueFilter.Builder builder =
301 FilterProtos.SingleColumnValueFilter.newBuilder();
302 if (this.columnFamily != null) {
303 builder.setColumnFamily(ByteString.copyFrom(this.columnFamily));
304 }
305 if (this.columnQualifier != null) {
306 builder.setColumnQualifier(ByteString.copyFrom(this.columnQualifier));
307 }
308 HBaseProtos.CompareType compareOp = CompareType.valueOf(this.compareOp.name());
309 builder.setCompareOp(compareOp);
310 builder.setComparator(ProtobufUtil.toComparator(this.comparator));
311 builder.setFilterIfMissing(this.filterIfMissing);
312 builder.setLatestVersionOnly(this.latestVersionOnly);
313
314 return builder.build();
315 }
316
317
318
319
320 public byte [] toByteArray() {
321 return convert().toByteArray();
322 }
323
324
325
326
327
328
329
330 public static SingleColumnValueFilter parseFrom(final byte [] pbBytes)
331 throws DeserializationException {
332 FilterProtos.SingleColumnValueFilter proto;
333 try {
334 proto = FilterProtos.SingleColumnValueFilter.parseFrom(pbBytes);
335 } catch (InvalidProtocolBufferException e) {
336 throw new DeserializationException(e);
337 }
338
339 final CompareOp compareOp =
340 CompareOp.valueOf(proto.getCompareOp().name());
341 final ByteArrayComparable comparator;
342 try {
343 comparator = ProtobufUtil.toComparator(proto.getComparator());
344 } catch (IOException ioe) {
345 throw new DeserializationException(ioe);
346 }
347
348 return new SingleColumnValueFilter(proto.hasColumnFamily() ? proto.getColumnFamily()
349 .toByteArray() : null, proto.hasColumnQualifier() ? proto.getColumnQualifier()
350 .toByteArray() : null, compareOp, comparator, proto.getFilterIfMissing(), proto
351 .getLatestVersionOnly());
352 }
353
354
355
356
357
358
359 boolean areSerializedFieldsEqual(Filter o) {
360 if (o == this) return true;
361 if (!(o instanceof SingleColumnValueFilter)) return false;
362
363 SingleColumnValueFilter other = (SingleColumnValueFilter)o;
364 return Bytes.equals(this.getFamily(), other.getFamily())
365 && Bytes.equals(this.getQualifier(), other.getQualifier())
366 && this.compareOp.equals(other.compareOp)
367 && this.getComparator().areSerializedFieldsEqual(other.getComparator())
368 && this.getFilterIfMissing() == other.getFilterIfMissing()
369 && this.getLatestVersionOnly() == other.getLatestVersionOnly();
370 }
371
372
373
374
375
376
377 public boolean isFamilyEssential(byte[] name) {
378 return !this.filterIfMissing || Bytes.equals(name, this.columnFamily);
379 }
380
381 @Override
382 public String toString() {
383 return String.format("%s (%s, %s, %s, %s)",
384 this.getClass().getSimpleName(), Bytes.toStringBinary(this.columnFamily),
385 Bytes.toStringBinary(this.columnQualifier), this.compareOp.name(),
386 Bytes.toStringBinary(this.comparator.getValue()));
387 }
388 }