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 org.apache.hadoop.hbase.KeyValue;
21 import org.apache.hadoop.hbase.util.Bytes;
22
23 import java.io.DataOutput;
24 import java.io.IOException;
25 import java.io.DataInput;
26 import java.util.Arrays;
27 import java.util.Comparator;
28 import java.util.TreeSet;
29 import java.util.ArrayList;
30
31
32
33
34
35
36 public class MultipleColumnPrefixFilter extends FilterBase {
37 protected byte [] hint = null;
38 protected TreeSet<byte []> sortedPrefixes = createTreeSet();
39 private final static int MAX_LOG_PREFIXES = 5;
40
41 public MultipleColumnPrefixFilter() {
42 super();
43 }
44
45 public MultipleColumnPrefixFilter(final byte [][] prefixes) {
46 if (prefixes != null) {
47 for (int i = 0; i < prefixes.length; i++) {
48 if (!sortedPrefixes.add(prefixes[i]))
49 throw new IllegalArgumentException ("prefixes must be distinct");
50 }
51 }
52 }
53
54 public byte [][] getPrefix() {
55 int count = 0;
56 byte [][] temp = new byte [sortedPrefixes.size()][];
57 for (byte [] prefixes : sortedPrefixes) {
58 temp [count++] = prefixes;
59 }
60 return temp;
61 }
62
63 @Override
64 public ReturnCode filterKeyValue(KeyValue kv) {
65 if (sortedPrefixes.size() == 0 || kv.getBuffer() == null) {
66 return ReturnCode.INCLUDE;
67 } else {
68 return filterColumn(kv.getBuffer(), kv.getQualifierOffset(), kv.getQualifierLength());
69 }
70 }
71
72 public ReturnCode filterColumn(byte[] buffer, int qualifierOffset, int qualifierLength) {
73 byte [] qualifier = Arrays.copyOfRange(buffer, qualifierOffset,
74 qualifierLength + qualifierOffset);
75 TreeSet<byte []> lesserOrEqualPrefixes =
76 (TreeSet<byte []>) sortedPrefixes.headSet(qualifier, true);
77
78 if (lesserOrEqualPrefixes.size() != 0) {
79 byte [] largestPrefixSmallerThanQualifier = lesserOrEqualPrefixes.last();
80
81 if (Bytes.startsWith(qualifier, largestPrefixSmallerThanQualifier)) {
82 return ReturnCode.INCLUDE;
83 }
84
85 if (lesserOrEqualPrefixes.size() == sortedPrefixes.size()) {
86 return ReturnCode.NEXT_ROW;
87 } else {
88 hint = sortedPrefixes.higher(largestPrefixSmallerThanQualifier);
89 return ReturnCode.SEEK_NEXT_USING_HINT;
90 }
91 } else {
92 hint = sortedPrefixes.first();
93 return ReturnCode.SEEK_NEXT_USING_HINT;
94 }
95 }
96
97 public static Filter createFilterFromArguments(ArrayList<byte []> filterArguments) {
98 byte [][] prefixes = new byte [filterArguments.size()][];
99 for (int i = 0 ; i < filterArguments.size(); i++) {
100 byte [] columnPrefix = ParseFilter.removeQuotesFromByteArray(filterArguments.get(i));
101 prefixes[i] = columnPrefix;
102 }
103 return new MultipleColumnPrefixFilter(prefixes);
104 }
105
106 public void write(DataOutput out) throws IOException {
107 out.writeInt(sortedPrefixes.size());
108 for (byte [] element : sortedPrefixes) {
109 Bytes.writeByteArray(out, element);
110 }
111 }
112
113 public void readFields(DataInput in) throws IOException {
114 int x = in.readInt();
115 this.sortedPrefixes = createTreeSet();
116 for (int j = 0; j < x; j++) {
117 sortedPrefixes.add(Bytes.readByteArray(in));
118 }
119 }
120
121 public KeyValue getNextKeyHint(KeyValue kv) {
122 return KeyValue.createFirstOnRow(
123 kv.getBuffer(), kv.getRowOffset(), kv.getRowLength(), kv.getBuffer(),
124 kv.getFamilyOffset(), kv.getFamilyLength(), hint, 0, hint.length);
125 }
126
127 public TreeSet<byte []> createTreeSet() {
128 return new TreeSet<byte []>(new Comparator<Object>() {
129 @Override
130 public int compare (Object o1, Object o2) {
131 if (o1 == null || o2 == null)
132 throw new IllegalArgumentException ("prefixes can't be null");
133
134 byte [] b1 = (byte []) o1;
135 byte [] b2 = (byte []) o2;
136 return Bytes.compareTo (b1, 0, b1.length, b2, 0, b2.length);
137 }
138 });
139 }
140
141 @Override
142 public String toString() {
143 return toString(MAX_LOG_PREFIXES);
144 }
145
146 protected String toString(int maxPrefixes) {
147 StringBuilder prefixes = new StringBuilder();
148
149 int count = 0;
150 for (byte[] ba : this.sortedPrefixes) {
151 if (count >= maxPrefixes) {
152 break;
153 }
154 ++count;
155 prefixes.append(Bytes.toStringBinary(ba));
156 if (count < this.sortedPrefixes.size() && count < maxPrefixes) {
157 prefixes.append(", ");
158 }
159 }
160
161 return String.format("%s (%d/%d): [%s]", this.getClass().getSimpleName(),
162 count, this.sortedPrefixes.size(), prefixes.toString());
163 }
164 }