Skip to content

Commit 349f632

Browse files
committed
HBASE-28717 Support FuzzyRowFilter in REST interface (#6061)
Signed-off-by: Ankit Singhal <ankit@apache.org>
1 parent 28a4009 commit 349f632

File tree

3 files changed

+128
-2
lines changed

3 files changed

+128
-2
lines changed

hbase-client/src/main/java/org/apache/hadoop/hbase/filter/FuzzyRowFilter.java

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,33 @@ private boolean isPreprocessedMask(byte[] mask) {
182182
return true;
183183
}
184184

185+
/**
186+
* Returns the Fuzzy keys in the format expected by the constructor.
187+
* @return the Fuzzy keys in the format expected by the constructor
188+
*/
189+
public List<Pair<byte[], byte[]>> getFuzzyKeys() {
190+
List<Pair<byte[], byte[]>> returnList = new ArrayList<>(fuzzyKeysData.size());
191+
for (Pair<byte[], byte[]> fuzzyKey : fuzzyKeysData) {
192+
Pair<byte[], byte[]> returnKey = new Pair<>();
193+
// This won't revert the original key's don't care values, but we don't care.
194+
returnKey.setFirst(Arrays.copyOf(fuzzyKey.getFirst(), fuzzyKey.getFirst().length));
195+
byte[] returnMask = Arrays.copyOf(fuzzyKey.getSecond(), fuzzyKey.getSecond().length);
196+
if (UNSAFE_UNALIGNED && isPreprocessedMask(returnMask)) {
197+
// Revert the preprocessing.
198+
for (int i = 0; i < returnMask.length; i++) {
199+
if (returnMask[i] == -1) {
200+
returnMask[i] = 0; // -1 >> 0
201+
} else if (returnMask[i] == processedWildcardMask) {
202+
returnMask[i] = 1; // 0 or 2 >> 1 depending on mask version
203+
}
204+
}
205+
}
206+
returnKey.setSecond(returnMask);
207+
returnList.add(returnKey);
208+
}
209+
return returnList;
210+
}
211+
185212
@Deprecated
186213
@Override
187214
public ReturnCode filterKeyValue(final Cell c) {

hbase-rest/src/main/java/org/apache/hadoop/hbase/rest/model/ScannerModel.java

Lines changed: 77 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
import org.apache.hadoop.hbase.filter.Filter;
5151
import org.apache.hadoop.hbase.filter.FilterList;
5252
import org.apache.hadoop.hbase.filter.FirstKeyOnlyFilter;
53+
import org.apache.hadoop.hbase.filter.FuzzyRowFilter;
5354
import org.apache.hadoop.hbase.filter.InclusiveStopFilter;
5455
import org.apache.hadoop.hbase.filter.KeyOnlyFilter;
5556
import org.apache.hadoop.hbase.filter.MultiRowRangeFilter;
@@ -75,6 +76,7 @@
7576
import org.apache.hadoop.hbase.security.visibility.Authorizations;
7677
import org.apache.hadoop.hbase.util.ByteStringer;
7778
import org.apache.hadoop.hbase.util.Bytes;
79+
import org.apache.hadoop.hbase.util.Pair;
7880
import org.apache.yetus.audience.InterfaceAudience;
7981

8082
import org.apache.hbase.thirdparty.com.fasterxml.jackson.jaxrs.json.JacksonJaxbJsonProvider;
@@ -277,6 +279,63 @@ public boolean equals(Object obj) {
277279

278280
}
279281

282+
static class FuzzyKeyModel {
283+
284+
protected byte[] key;
285+
286+
protected byte[] mask;
287+
288+
public FuzzyKeyModel() {
289+
}
290+
291+
public FuzzyKeyModel(Pair<byte[], byte[]> keyWithMask) {
292+
this.key = keyWithMask.getFirst();
293+
this.mask = keyWithMask.getSecond();
294+
}
295+
296+
public Pair<byte[], byte[]> build() {
297+
return new Pair<>(key, mask);
298+
}
299+
300+
public byte[] getKey() {
301+
return key;
302+
}
303+
304+
public void setKey(byte[] key) {
305+
this.key = key;
306+
}
307+
308+
public byte[] getMask() {
309+
return mask;
310+
}
311+
312+
public void setMask(byte[] mask) {
313+
this.mask = mask;
314+
}
315+
316+
@Override
317+
public int hashCode() {
318+
final int prime = 31;
319+
int result = 1;
320+
result = prime * result + Arrays.hashCode(key);
321+
result = prime * result + Arrays.hashCode(mask);
322+
return result;
323+
}
324+
325+
@Override
326+
public boolean equals(Object obj) {
327+
if (this == obj) {
328+
return true;
329+
}
330+
if (!(obj instanceof FuzzyKeyModel)) {
331+
return false;
332+
}
333+
FuzzyKeyModel other = (FuzzyKeyModel) obj;
334+
return Arrays.equals(key, other.key) && Arrays.equals(mask, other.mask);
335+
}
336+
337+
}
338+
280339
// A grab bag of fields, would have been a union if this were C.
281340
// These are null by default and will only be serialized if set (non null).
282341
@XmlAttribute
@@ -319,6 +378,8 @@ public boolean equals(Object obj) {
319378
private List<RowRangeModel> ranges;
320379
@XmlElement
321380
public List<Long> timestamps;
381+
@XmlElement
382+
private List<FuzzyKeyModel> fuzzyKeys;
322383

323384
static enum FilterType {
324385
ColumnCountGetFilter,
@@ -343,7 +404,8 @@ static enum FilterType {
343404
SkipFilter,
344405
TimestampsFilter,
345406
ValueFilter,
346-
WhileMatchFilter
407+
WhileMatchFilter,
408+
FuzzyRowFilter
347409
}
348410

349411
public FilterModel() {
@@ -456,6 +518,12 @@ public FilterModel(Filter filter) {
456518
this.filters = new ArrayList<>();
457519
this.filters.add(new FilterModel(((WhileMatchFilter) filter).getFilter()));
458520
break;
521+
case FuzzyRowFilter:
522+
this.fuzzyKeys = new ArrayList<>(((FuzzyRowFilter) filter).getFuzzyKeys().size());
523+
for (Pair<byte[], byte[]> keyWithMask : ((FuzzyRowFilter) filter).getFuzzyKeys()) {
524+
this.fuzzyKeys.add(new FuzzyKeyModel(keyWithMask));
525+
}
526+
break;
459527
default:
460528
throw new RuntimeException("unhandled filter type " + type);
461529
}
@@ -567,6 +635,14 @@ public Filter build() {
567635
case WhileMatchFilter:
568636
filter = new WhileMatchFilter(filters.get(0).build());
569637
break;
638+
case FuzzyRowFilter: {
639+
ArrayList<Pair<byte[], byte[]>> fuzzyKeyArgs = new ArrayList<>(fuzzyKeys.size());
640+
for (FuzzyKeyModel keyModel : fuzzyKeys) {
641+
fuzzyKeyArgs.add(keyModel.build());
642+
}
643+
filter = new FuzzyRowFilter(fuzzyKeyArgs);
644+
}
645+
break;
570646
default:
571647
throw new RuntimeException("unhandled filter type: " + type);
572648
}

hbase-rest/src/test/java/org/apache/hadoop/hbase/rest/TestScannersWithFilters.java

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
import org.apache.hadoop.hbase.filter.FilterList;
5151
import org.apache.hadoop.hbase.filter.FilterList.Operator;
5252
import org.apache.hadoop.hbase.filter.FirstKeyOnlyFilter;
53+
import org.apache.hadoop.hbase.filter.FuzzyRowFilter;
5354
import org.apache.hadoop.hbase.filter.InclusiveStopFilter;
5455
import org.apache.hadoop.hbase.filter.MultiRowRangeFilter;
5556
import org.apache.hadoop.hbase.filter.PageFilter;
@@ -70,6 +71,7 @@
7071
import org.apache.hadoop.hbase.testclassification.MediumTests;
7172
import org.apache.hadoop.hbase.testclassification.RestTests;
7273
import org.apache.hadoop.hbase.util.Bytes;
74+
import org.apache.hadoop.hbase.util.Pair;
7375
import org.junit.AfterClass;
7476
import org.junit.BeforeClass;
7577
import org.junit.ClassRule;
@@ -229,7 +231,7 @@ private static void verifyScan(Scan s, long expectedRows, long expectedKeys) thr
229231

230232
int rows = cells.getRows().size();
231233
assertEquals(
232-
"Scanned too many rows! Only expected " + expectedRows + " total but scanned " + rows,
234+
"Unexpected number of rows! Expected " + expectedRows + " total but scanned " + rows,
233235
expectedRows, rows);
234236
for (RowModel row : cells.getRows()) {
235237
int count = row.getCells().size();
@@ -972,4 +974,25 @@ public void testMultiRowRangeFilter() throws Exception {
972974
s.setFilter(new MultiRowRangeFilter(ranges));
973975
verifyScan(s, expectedRows, expectedKeys);
974976
}
977+
978+
@Test
979+
public void testFuzzyRowFilter() throws Exception {
980+
long expectedRows = 4;
981+
long expectedKeys = colsPerRow;
982+
List<Pair<byte[], byte[]>> fuzzyKeys = new ArrayList<>();
983+
984+
// Exact match for ROWS_ONE[0] (one row)
985+
byte[] rowOneMask = new byte[ROWS_ONE[0].length];
986+
Arrays.fill(rowOneMask, (byte) 0);
987+
fuzzyKeys.add(new Pair<>(ROWS_ONE[0], rowOneMask));
988+
// All ROW_TWO keys (three rows)
989+
byte[] rowTwoMask = new byte[ROWS_TWO[0].length];
990+
Arrays.fill(rowTwoMask, (byte) 0);
991+
rowTwoMask[rowTwoMask.length - 1] = (byte) 1;
992+
fuzzyKeys.add(new Pair<>(ROWS_TWO[2], rowTwoMask));
993+
994+
Scan s = new Scan();
995+
s.setFilter(new FuzzyRowFilter(fuzzyKeys));
996+
verifyScan(s, expectedRows, expectedKeys);
997+
}
975998
}

0 commit comments

Comments
 (0)