001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018package org.apache.hadoop.hbase.filter; 019 020import static org.junit.jupiter.api.Assertions.assertEquals; 021import static org.junit.jupiter.api.Assertions.assertFalse; 022import static org.junit.jupiter.api.Assertions.assertNull; 023import static org.junit.jupiter.api.Assertions.assertTrue; 024import static org.junit.jupiter.api.Assertions.fail; 025 026import java.io.IOException; 027import java.util.ArrayList; 028import java.util.Arrays; 029import java.util.List; 030import java.util.Objects; 031import org.apache.hadoop.hbase.Cell; 032import org.apache.hadoop.hbase.CellComparator; 033import org.apache.hadoop.hbase.CompareOperator; 034import org.apache.hadoop.hbase.ExtendedCell; 035import org.apache.hadoop.hbase.KeyValue; 036import org.apache.hadoop.hbase.KeyValueUtil; 037import org.apache.hadoop.hbase.exceptions.DeserializationException; 038import org.apache.hadoop.hbase.filter.Filter.ReturnCode; 039import org.apache.hadoop.hbase.filter.FilterList.Operator; 040import org.apache.hadoop.hbase.testclassification.FilterTests; 041import org.apache.hadoop.hbase.testclassification.MediumTests; 042import org.apache.hadoop.hbase.util.Bytes; 043import org.junit.jupiter.api.Tag; 044import org.junit.jupiter.api.Test; 045import org.mockito.Mockito; 046 047import org.apache.hbase.thirdparty.com.google.common.collect.Lists; 048 049import org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil; 050 051@Tag(FilterTests.TAG) 052@Tag(MediumTests.TAG) 053public class TestFilterList { 054 055 static final int MAX_PAGES = 2; 056 057 @Test 058 public void testAddFilter() throws Exception { 059 Filter filter1 = new FirstKeyOnlyFilter(); 060 Filter filter2 = new FirstKeyOnlyFilter(); 061 062 FilterList filterList = new FilterList(filter1, filter2); 063 filterList.addFilter(new FirstKeyOnlyFilter()); 064 065 filterList = new FilterList(Arrays.asList(filter1, filter2)); 066 filterList.addFilter(new FirstKeyOnlyFilter()); 067 068 filterList = new FilterList(Operator.MUST_PASS_ALL, filter1, filter2); 069 filterList.addFilter(new FirstKeyOnlyFilter()); 070 071 filterList = new FilterList(Operator.MUST_PASS_ALL, Arrays.asList(filter1, filter2)); 072 filterList.addFilter(new FirstKeyOnlyFilter()); 073 074 filterList.setReversed(false); 075 FirstKeyOnlyFilter f = new FirstKeyOnlyFilter(); 076 f.setReversed(true); 077 try { 078 filterList.addFilter(f); 079 fail("The IllegalArgumentException should be thrown because the added filter is reversed"); 080 } catch (IllegalArgumentException e) { 081 } 082 083 } 084 085 @Test 086 public void testConstruction() { 087 FirstKeyOnlyFilter f1 = new FirstKeyOnlyFilter(); 088 FirstKeyOnlyFilter f2 = new FirstKeyOnlyFilter(); 089 f1.setReversed(true); 090 f2.setReversed(false); 091 092 try { 093 FilterList ff = new FilterList(f1, f2); 094 fail("The IllegalArgumentException should be thrown"); 095 } catch (IllegalArgumentException e) { 096 } 097 098 try { 099 FilterList ff = new FilterList(Arrays.asList(f1, f2)); 100 fail("The IllegalArgumentException should be thrown because the added filter is reversed"); 101 } catch (IllegalArgumentException e) { 102 } 103 104 try { 105 FilterList ff = new FilterList(FilterList.Operator.MUST_PASS_ALL, Arrays.asList(f1, f2)); 106 fail("The IllegalArgumentException should be thrown because the added filter is reversed"); 107 } catch (IllegalArgumentException e) { 108 } 109 110 try { 111 FilterList ff = new FilterList(FilterList.Operator.MUST_PASS_ALL, f1, f2); 112 fail("The IllegalArgumentException should be thrown because the added filter is reversed"); 113 } catch (IllegalArgumentException e) { 114 } 115 } 116 117 /** 118 * Test "must pass one" 119 */ 120 @Test 121 public void testMPONE() throws Exception { 122 mpOneTest(getFilterMPONE()); 123 } 124 125 private Filter getFilterMPONE() { 126 List<Filter> filters = new ArrayList<>(); 127 filters.add(new PageFilter(MAX_PAGES)); 128 filters.add(new WhileMatchFilter(new PrefixFilter(Bytes.toBytes("yyy")))); 129 Filter filterMPONE = new FilterList(FilterList.Operator.MUST_PASS_ONE, filters); 130 return filterMPONE; 131 } 132 133 private void mpOneTest(Filter filterMPONE) throws Exception { 134 /* 135 * Filter must do all below steps: <ul> <li>{@link #reset()}</li> <li>{@link 136 * #filterAllRemaining()} -> true indicates scan is over, false, keep going on.</li> <li>{@link 137 * #filterRowKey(byte[],int,int)} -> true to drop this row, if false, we will also call</li> 138 * <li>{@link #filterCell(org.apache.hadoop.hbase.KeyValue)} -> true to drop this cell</li> 139 * <li>{@link #filterRow()} -> last chance to drop entire row based on the sequence of 140 * filterValue() calls. Eg: filter a row if it doesn't contain a specified column. </li> </ul> 141 */ 142 filterMPONE.reset(); 143 assertFalse(filterMPONE.filterAllRemaining()); 144 145 /* Will pass both */ 146 byte[] rowkey = Bytes.toBytes("yyyyyyyyy"); 147 for (int i = 0; i < MAX_PAGES - 1; i++) { 148 assertFalse(filterMPONE.filterRowKey(KeyValueUtil.createFirstOnRow(rowkey))); 149 KeyValue kv = new KeyValue(rowkey, rowkey, Bytes.toBytes(i), Bytes.toBytes(i)); 150 assertTrue(Filter.ReturnCode.INCLUDE == filterMPONE.filterCell(kv)); 151 assertFalse(filterMPONE.filterRow()); 152 } 153 154 /* Only pass PageFilter */ 155 rowkey = Bytes.toBytes("z"); 156 assertFalse(filterMPONE.filterRowKey(KeyValueUtil.createFirstOnRow(rowkey))); 157 KeyValue kv = new KeyValue(rowkey, rowkey, Bytes.toBytes(0), Bytes.toBytes(0)); 158 assertTrue(Filter.ReturnCode.INCLUDE == filterMPONE.filterCell(kv)); 159 assertFalse(filterMPONE.filterRow()); 160 161 /* reach MAX_PAGES already, should filter any rows */ 162 rowkey = Bytes.toBytes("yyy"); 163 assertTrue(filterMPONE.filterRowKey(KeyValueUtil.createFirstOnRow(rowkey))); 164 kv = new KeyValue(rowkey, rowkey, Bytes.toBytes(0), Bytes.toBytes(0)); 165 assertFalse(Filter.ReturnCode.INCLUDE == filterMPONE.filterCell(kv)); 166 // FilterList.filterRow() returns true because previously "z" was filtered out (return true) by 167 // PrefixFilter.filterRowKey() 168 assertTrue(filterMPONE.filterRow()); 169 170 /* We should filter any row */ 171 rowkey = Bytes.toBytes("z"); 172 assertTrue(filterMPONE.filterRowKey(KeyValueUtil.createFirstOnRow(rowkey))); 173 assertTrue(filterMPONE.filterAllRemaining()); 174 } 175 176 /** 177 * Test "must pass all" 178 */ 179 @Test 180 public void testMPALL() throws Exception { 181 mpAllTest(getMPALLFilter()); 182 } 183 184 private Filter getMPALLFilter() { 185 List<Filter> filters = new ArrayList<>(); 186 filters.add(new PageFilter(MAX_PAGES)); 187 filters.add(new WhileMatchFilter(new PrefixFilter(Bytes.toBytes("yyy")))); 188 Filter filterMPALL = new FilterList(FilterList.Operator.MUST_PASS_ALL, filters); 189 return filterMPALL; 190 } 191 192 private void mpAllTest(Filter filterMPALL) throws Exception { 193 /* 194 * Filter must do all below steps: <ul> <li>{@link #reset()}</li> <li>{@link 195 * #filterAllRemaining()} -> true indicates scan is over, false, keep going on.</li> <li>{@link 196 * #filterRowKey(byte[],int,int)} -> true to drop this row, if false, we will also call</li> 197 * <li>{@link #filterCell(org.apache.hadoop.hbase.KeyValue)} -> true to drop this cell</li> 198 * <li>{@link #filterRow()} -> last chance to drop entire row based on the sequence of 199 * filterValue() calls. Eg: filter a row if it doesn't contain a specified column. </li> </ul> 200 */ 201 filterMPALL.reset(); 202 assertFalse(filterMPALL.filterAllRemaining()); 203 byte[] rowkey = Bytes.toBytes("yyyyyyyyy"); 204 for (int i = 0; i < MAX_PAGES - 1; i++) { 205 assertFalse(filterMPALL.filterRowKey(KeyValueUtil.createFirstOnRow(rowkey))); 206 KeyValue kv = new KeyValue(rowkey, rowkey, Bytes.toBytes(i), Bytes.toBytes(i)); 207 assertTrue(Filter.ReturnCode.INCLUDE == filterMPALL.filterCell(kv)); 208 } 209 filterMPALL.reset(); 210 rowkey = Bytes.toBytes("z"); 211 assertTrue(filterMPALL.filterRowKey(KeyValueUtil.createFirstOnRow(rowkey))); 212 // Should fail here; row should be filtered out. 213 KeyValue kv = new KeyValue(rowkey, rowkey, rowkey, rowkey); 214 assertTrue(Filter.ReturnCode.NEXT_ROW == filterMPALL.filterCell(kv)); 215 } 216 217 /** 218 * Test list ordering 219 */ 220 @Test 221 public void testOrdering() throws Exception { 222 orderingTest(getOrderingFilter()); 223 } 224 225 public Filter getOrderingFilter() { 226 List<Filter> filters = new ArrayList<>(); 227 filters.add(new PrefixFilter(Bytes.toBytes("yyy"))); 228 filters.add(new PageFilter(MAX_PAGES)); 229 Filter filterMPONE = new FilterList(FilterList.Operator.MUST_PASS_ONE, filters); 230 return filterMPONE; 231 } 232 233 public void orderingTest(Filter filterMPONE) throws Exception { 234 /* 235 * Filter must do all below steps: <ul> <li>{@link #reset()}</li> <li>{@link 236 * #filterAllRemaining()} -> true indicates scan is over, false, keep going on.</li> <li>{@link 237 * #filterRowKey(byte[],int,int)} -> true to drop this row, if false, we will also call</li> 238 * <li>{@link #filterCell(org.apache.hadoop.hbase.KeyValue)} -> true to drop this key/value</li> 239 * <li>{@link #filterRow()} -> last chance to drop entire row based on the sequence of 240 * filterValue() calls. Eg: filter a row if it doesn't contain a specified column. </li> </ul> 241 */ 242 filterMPONE.reset(); 243 assertFalse(filterMPONE.filterAllRemaining()); 244 245 /* We should be able to fill MAX_PAGES without incrementing page counter */ 246 byte[] rowkey = Bytes.toBytes("yyyyyyyy"); 247 for (int i = 0; i < MAX_PAGES; i++) { 248 assertFalse(filterMPONE.filterRowKey(KeyValueUtil.createFirstOnRow(rowkey))); 249 KeyValue kv = new KeyValue(rowkey, rowkey, Bytes.toBytes(i), Bytes.toBytes(i)); 250 assertTrue(Filter.ReturnCode.INCLUDE == filterMPONE.filterCell(kv)); 251 assertFalse(filterMPONE.filterRow()); 252 } 253 254 /* Now let's fill the page filter */ 255 rowkey = Bytes.toBytes("xxxxxxx"); 256 for (int i = 0; i < MAX_PAGES; i++) { 257 assertFalse(filterMPONE.filterRowKey(KeyValueUtil.createFirstOnRow(rowkey))); 258 KeyValue kv = new KeyValue(rowkey, rowkey, Bytes.toBytes(i), Bytes.toBytes(i)); 259 assertTrue(Filter.ReturnCode.INCLUDE == filterMPONE.filterCell(kv)); 260 assertFalse(filterMPONE.filterRow()); 261 } 262 263 /* We should still be able to include even though page filter is at max */ 264 rowkey = Bytes.toBytes("yyy"); 265 for (int i = 0; i < MAX_PAGES; i++) { 266 assertFalse(filterMPONE.filterRowKey(KeyValueUtil.createFirstOnRow(rowkey))); 267 KeyValue kv = new KeyValue(rowkey, rowkey, Bytes.toBytes(i), Bytes.toBytes(i)); 268 assertTrue(Filter.ReturnCode.INCLUDE == filterMPONE.filterCell(kv)); 269 assertFalse(filterMPONE.filterRow()); 270 } 271 } 272 273 /** 274 * When we do a "MUST_PASS_ONE" (a logical 'OR') of the above two filters we expect to get the 275 * same result as the 'prefix' only result. 276 */ 277 @Test 278 public void testFilterListTwoFiltersMustPassOne() throws Exception { 279 byte[] r1 = Bytes.toBytes("Row1"); 280 byte[] r11 = Bytes.toBytes("Row11"); 281 byte[] r2 = Bytes.toBytes("Row2"); 282 283 FilterList flist = new FilterList(FilterList.Operator.MUST_PASS_ONE); 284 flist.addFilter(new PrefixFilter(r1)); 285 flist.filterRowKey(KeyValueUtil.createFirstOnRow(r1)); 286 assertEquals(ReturnCode.INCLUDE, flist.filterCell(new KeyValue(r1, r1, r1))); 287 assertEquals(ReturnCode.INCLUDE, flist.filterCell(new KeyValue(r11, r11, r11))); 288 289 flist.reset(); 290 flist.filterRowKey(KeyValueUtil.createFirstOnRow(r2)); 291 assertEquals(ReturnCode.SKIP, flist.filterCell(new KeyValue(r2, r2, r2))); 292 293 flist = new FilterList(FilterList.Operator.MUST_PASS_ONE); 294 flist.addFilter(new AlwaysNextColFilter()); 295 flist.addFilter(new PrefixFilter(r1)); 296 flist.filterRowKey(KeyValueUtil.createFirstOnRow(r1)); 297 assertEquals(ReturnCode.INCLUDE, flist.filterCell(new KeyValue(r1, r1, r1))); 298 assertEquals(ReturnCode.INCLUDE, flist.filterCell(new KeyValue(r11, r11, r11))); 299 300 flist.reset(); 301 flist.filterRowKey(KeyValueUtil.createFirstOnRow(r2)); 302 assertEquals(ReturnCode.NEXT_COL, flist.filterCell(new KeyValue(r2, r2, r2))); 303 } 304 305 /** 306 * When we do a "MUST_PASS_ONE" (a logical 'OR') of the two filters we expect to get the same 307 * result as the inclusive stop result. 308 */ 309 @Test 310 public void testFilterListWithInclusiveStopFilterMustPassOne() throws Exception { 311 byte[] r1 = Bytes.toBytes("Row1"); 312 byte[] r11 = Bytes.toBytes("Row11"); 313 byte[] r2 = Bytes.toBytes("Row2"); 314 315 FilterList flist = new FilterList(FilterList.Operator.MUST_PASS_ONE); 316 flist.addFilter(new AlwaysNextColFilter()); 317 flist.addFilter(new InclusiveStopFilter(r1)); 318 flist.filterRowKey(KeyValueUtil.createFirstOnRow(r1)); 319 assertEquals(ReturnCode.INCLUDE, flist.filterCell(new KeyValue(r1, r1, r1))); 320 assertEquals(ReturnCode.INCLUDE, flist.filterCell(new KeyValue(r11, r11, r11))); 321 322 flist.reset(); 323 flist.filterRowKey(KeyValueUtil.createFirstOnRow(r2)); 324 assertEquals(ReturnCode.NEXT_COL, flist.filterCell(new KeyValue(r2, r2, r2))); 325 } 326 327 public static class AlwaysNextColFilter extends FilterBase { 328 public AlwaysNextColFilter() { 329 super(); 330 } 331 332 @Override 333 public ReturnCode filterCell(final Cell v) { 334 return ReturnCode.NEXT_COL; 335 } 336 337 public static AlwaysNextColFilter parseFrom(final byte[] pbBytes) 338 throws DeserializationException { 339 return new AlwaysNextColFilter(); 340 } 341 } 342 343 /** 344 * Test serialization 345 */ 346 @Test 347 public void testSerialization() throws Exception { 348 List<Filter> filters = new ArrayList<>(); 349 filters.add(new PageFilter(MAX_PAGES)); 350 filters.add(new WhileMatchFilter(new PrefixFilter(Bytes.toBytes("yyy")))); 351 Filter filterMPALL = new FilterList(FilterList.Operator.MUST_PASS_ALL, filters); 352 353 // Decompose filterMPALL to bytes. 354 byte[] buffer = filterMPALL.toByteArray(); 355 356 // Recompose filterMPALL. 357 FilterList newFilter = FilterList.parseFrom(buffer); 358 359 // Run tests 360 mpOneTest(ProtobufUtil.toFilter(ProtobufUtil.toFilter(getFilterMPONE()))); 361 mpAllTest(ProtobufUtil.toFilter(ProtobufUtil.toFilter(getMPALLFilter()))); 362 orderingTest(ProtobufUtil.toFilter(ProtobufUtil.toFilter(getOrderingFilter()))); 363 } 364 365 /** 366 * Test filterCell logic. 367 */ 368 @Test 369 public void testFilterCell() throws Exception { 370 Filter includeFilter = new FilterBase() { 371 @Override 372 public Filter.ReturnCode filterCell(final Cell v) { 373 return Filter.ReturnCode.INCLUDE; 374 } 375 }; 376 377 Filter alternateFilter = new FilterBase() { 378 boolean returnInclude = true; 379 380 @Override 381 public Filter.ReturnCode filterCell(final Cell v) { 382 Filter.ReturnCode returnCode = 383 returnInclude ? Filter.ReturnCode.INCLUDE : Filter.ReturnCode.SKIP; 384 returnInclude = !returnInclude; 385 return returnCode; 386 } 387 }; 388 389 Filter alternateIncludeFilter = new FilterBase() { 390 boolean returnIncludeOnly = false; 391 392 @Override 393 public Filter.ReturnCode filterCell(final Cell v) { 394 Filter.ReturnCode returnCode = 395 returnIncludeOnly ? Filter.ReturnCode.INCLUDE : Filter.ReturnCode.INCLUDE_AND_NEXT_COL; 396 returnIncludeOnly = !returnIncludeOnly; 397 return returnCode; 398 } 399 }; 400 401 // Check must pass one filter. 402 FilterList mpOnefilterList = new FilterList(Operator.MUST_PASS_ONE, 403 Arrays.asList(new Filter[] { includeFilter, alternateIncludeFilter, alternateFilter })); 404 // INCLUDE, INCLUDE, INCLUDE_AND_NEXT_COL. 405 assertEquals(ReturnCode.INCLUDE, mpOnefilterList.filterCell(null)); 406 // INCLUDE, SKIP, INCLUDE. 407 assertEquals(Filter.ReturnCode.INCLUDE, mpOnefilterList.filterCell(null)); 408 409 // Check must pass all filter. 410 FilterList mpAllfilterList = new FilterList(Operator.MUST_PASS_ALL, 411 Arrays.asList(new Filter[] { includeFilter, alternateIncludeFilter, alternateFilter })); 412 // INCLUDE, INCLUDE, INCLUDE_AND_NEXT_COL. 413 assertEquals(Filter.ReturnCode.INCLUDE_AND_NEXT_COL, mpAllfilterList.filterCell(null)); 414 // INCLUDE, SKIP, INCLUDE. 415 assertEquals(Filter.ReturnCode.SKIP, mpAllfilterList.filterCell(null)); 416 } 417 418 /** 419 * Test pass-thru of hints. 420 */ 421 @Test 422 public void testHintPassThru() throws Exception { 423 424 final KeyValue minKeyValue = new KeyValue(Bytes.toBytes(0L), null, null); 425 final KeyValue maxKeyValue = new KeyValue(Bytes.toBytes(Long.MAX_VALUE), null, null); 426 427 Filter filterNoHint = new FilterBase() { 428 @Override 429 public byte[] toByteArray() { 430 return null; 431 } 432 433 @Override 434 public ReturnCode filterCell(final Cell ignored) throws IOException { 435 return ReturnCode.INCLUDE; 436 } 437 }; 438 439 Filter filterMinHint = new FilterBase() { 440 @Override 441 public ReturnCode filterCell(final Cell ignored) { 442 return ReturnCode.SEEK_NEXT_USING_HINT; 443 } 444 445 @Override 446 public Cell getNextCellHint(Cell currentKV) { 447 return minKeyValue; 448 } 449 450 @Override 451 public byte[] toByteArray() { 452 return null; 453 } 454 }; 455 456 Filter filterMaxHint = new FilterBase() { 457 @Override 458 public ReturnCode filterCell(final Cell ignored) { 459 return ReturnCode.SEEK_NEXT_USING_HINT; 460 } 461 462 @Override 463 public Cell getNextCellHint(Cell cell) { 464 return new KeyValue(Bytes.toBytes(Long.MAX_VALUE), null, null); 465 } 466 467 @Override 468 public byte[] toByteArray() { 469 return null; 470 } 471 }; 472 473 CellComparator comparator = CellComparator.getInstance(); 474 // MUST PASS ONE 475 476 // Should take the min if given two hints 477 FilterList filterList = new FilterList(Operator.MUST_PASS_ONE, 478 Arrays.asList(new Filter[] { filterMinHint, filterMaxHint })); 479 assertEquals(0, comparator.compare(filterList.getNextCellHint(null), minKeyValue)); 480 481 // Should have no hint if any filter has no hint 482 filterList = new FilterList(Operator.MUST_PASS_ONE, 483 Arrays.asList(new Filter[] { filterMinHint, filterMaxHint, filterNoHint })); 484 assertNull(filterList.getNextCellHint(null)); 485 filterList = new FilterList(Operator.MUST_PASS_ONE, 486 Arrays.asList(new Filter[] { filterNoHint, filterMaxHint })); 487 assertNull(filterList.getNextCellHint(null)); 488 489 // Should give max hint if its the only one 490 filterList = new FilterList(Operator.MUST_PASS_ONE, 491 Arrays.asList(new Filter[] { filterMaxHint, filterMaxHint })); 492 assertEquals(0, comparator.compare(filterList.getNextCellHint(null), maxKeyValue)); 493 494 // MUST PASS ALL 495 496 // Should take the first hint 497 filterList = new FilterList(Operator.MUST_PASS_ALL, 498 Arrays.asList(new Filter[] { filterMinHint, filterMaxHint })); 499 filterList.filterCell(null); 500 assertEquals(0, comparator.compare(filterList.getNextCellHint(null), minKeyValue)); 501 502 filterList = new FilterList(Operator.MUST_PASS_ALL, 503 Arrays.asList(new Filter[] { filterMaxHint, filterMinHint })); 504 filterList.filterCell(null); 505 assertEquals(0, comparator.compare(filterList.getNextCellHint(null), maxKeyValue)); 506 507 // Should have first hint even if a filter has no hint 508 filterList = new FilterList(Operator.MUST_PASS_ALL, 509 Arrays.asList(new Filter[] { filterNoHint, filterMinHint, filterMaxHint })); 510 filterList.filterCell(null); 511 assertEquals(0, comparator.compare(filterList.getNextCellHint(null), minKeyValue)); 512 filterList = new FilterList(Operator.MUST_PASS_ALL, 513 Arrays.asList(new Filter[] { filterNoHint, filterMaxHint })); 514 filterList.filterCell(null); 515 assertEquals(0, comparator.compare(filterList.getNextCellHint(null), maxKeyValue)); 516 filterList = new FilterList(Operator.MUST_PASS_ALL, 517 Arrays.asList(new Filter[] { filterNoHint, filterMinHint })); 518 filterList.filterCell(null); 519 assertEquals(0, comparator.compare(filterList.getNextCellHint(null), minKeyValue)); 520 } 521 522 /** 523 * Tests the behavior of transform() in a hierarchical filter. transform() only applies after a 524 * filterCell() whose return-code includes the KeyValue. Lazy evaluation of AND 525 */ 526 @Test 527 public void testTransformMPO() throws Exception { 528 // Apply the following filter: 529 // (family=fam AND qualifier=qual1 AND KeyOnlyFilter) 530 // OR (family=fam AND qualifier=qual2) 531 final FilterList flist = new FilterList(Operator.MUST_PASS_ONE, 532 Lists.<Filter> newArrayList( 533 new FilterList(Operator.MUST_PASS_ALL, 534 Lists.<Filter> newArrayList( 535 new FamilyFilter(CompareOperator.EQUAL, new BinaryComparator(Bytes.toBytes("fam"))), 536 new QualifierFilter(CompareOperator.EQUAL, 537 new BinaryComparator(Bytes.toBytes("qual1"))), 538 new KeyOnlyFilter())), 539 new FilterList(Operator.MUST_PASS_ALL, 540 Lists.<Filter> newArrayList( 541 new FamilyFilter(CompareOperator.EQUAL, new BinaryComparator(Bytes.toBytes("fam"))), 542 new QualifierFilter(CompareOperator.EQUAL, 543 new BinaryComparator(Bytes.toBytes("qual2"))))))); 544 545 final KeyValue kvQual1 = new KeyValue(Bytes.toBytes("row"), Bytes.toBytes("fam"), 546 Bytes.toBytes("qual1"), Bytes.toBytes("value")); 547 final KeyValue kvQual2 = new KeyValue(Bytes.toBytes("row"), Bytes.toBytes("fam"), 548 Bytes.toBytes("qual2"), Bytes.toBytes("value")); 549 final KeyValue kvQual3 = new KeyValue(Bytes.toBytes("row"), Bytes.toBytes("fam"), 550 Bytes.toBytes("qual3"), Bytes.toBytes("value")); 551 552 // Value for fam:qual1 should be stripped: 553 assertEquals(Filter.ReturnCode.INCLUDE, flist.filterCell(kvQual1)); 554 final KeyValue transformedQual1 = 555 KeyValueUtil.ensureKeyValue((ExtendedCell) flist.transformCell(kvQual1)); 556 assertEquals(0, transformedQual1.getValueLength()); 557 558 // Value for fam:qual2 should not be stripped: 559 assertEquals(Filter.ReturnCode.INCLUDE, flist.filterCell(kvQual2)); 560 final KeyValue transformedQual2 = 561 KeyValueUtil.ensureKeyValue((ExtendedCell) flist.transformCell(kvQual2)); 562 assertEquals("value", Bytes.toString(transformedQual2.getValueArray(), 563 transformedQual2.getValueOffset(), transformedQual2.getValueLength())); 564 565 // Other keys should be skipped: 566 assertEquals(Filter.ReturnCode.SKIP, flist.filterCell(kvQual3)); 567 } 568 569 @Test 570 public void testWithMultiVersionsInSameRow() throws Exception { 571 FilterList filterList01 = 572 new FilterList(Operator.MUST_PASS_ONE, new ColumnPaginationFilter(1, 0)); 573 574 KeyValue kv1 = new KeyValue(Bytes.toBytes("row"), Bytes.toBytes("fam"), Bytes.toBytes("qual"), 575 1, Bytes.toBytes("value")); 576 KeyValue kv2 = new KeyValue(Bytes.toBytes("row"), Bytes.toBytes("fam"), Bytes.toBytes("qual"), 577 2, Bytes.toBytes("value")); 578 KeyValue kv3 = new KeyValue(Bytes.toBytes("row"), Bytes.toBytes("fam"), Bytes.toBytes("qual"), 579 3, Bytes.toBytes("value")); 580 581 assertEquals(ReturnCode.INCLUDE_AND_NEXT_COL, filterList01.filterCell(kv1)); 582 assertEquals(ReturnCode.NEXT_COL, filterList01.filterCell(kv2)); 583 assertEquals(ReturnCode.NEXT_COL, filterList01.filterCell(kv3)); 584 585 FilterList filterList11 = 586 new FilterList(Operator.MUST_PASS_ONE, new ColumnPaginationFilter(1, 1)); 587 588 assertEquals(ReturnCode.NEXT_COL, filterList11.filterCell(kv1)); 589 assertEquals(ReturnCode.NEXT_COL, filterList11.filterCell(kv2)); 590 assertEquals(ReturnCode.NEXT_COL, filterList11.filterCell(kv3)); 591 } 592 593 @Test 594 public void testMPONEWithSeekNextUsingHint() throws Exception { 595 byte[] col = Bytes.toBytes("c"); 596 FilterList filterList = 597 new FilterList(Operator.MUST_PASS_ONE, new ColumnPaginationFilter(1, col)); 598 599 KeyValue kv1 = new KeyValue(Bytes.toBytes("row"), Bytes.toBytes("fam"), Bytes.toBytes("a"), 1, 600 Bytes.toBytes("value")); 601 KeyValue kv2 = new KeyValue(Bytes.toBytes("row"), Bytes.toBytes("fam"), Bytes.toBytes("b"), 2, 602 Bytes.toBytes("value")); 603 KeyValue kv3 = new KeyValue(Bytes.toBytes("row"), Bytes.toBytes("fam"), Bytes.toBytes("c"), 3, 604 Bytes.toBytes("value")); 605 KeyValue kv4 = new KeyValue(Bytes.toBytes("row"), Bytes.toBytes("fam"), Bytes.toBytes("c"), 4, 606 Bytes.toBytes("value")); 607 608 assertEquals(ReturnCode.SEEK_NEXT_USING_HINT, filterList.filterCell(kv1)); 609 assertEquals(ReturnCode.SEEK_NEXT_USING_HINT, filterList.filterCell(kv2)); 610 assertEquals(ReturnCode.INCLUDE_AND_NEXT_COL, filterList.filterCell(kv3)); 611 assertEquals(ReturnCode.NEXT_COL, filterList.filterCell(kv4)); 612 } 613 614 private static class MockFilter extends FilterBase { 615 private ReturnCode targetRetCode; 616 public boolean didCellPassToTheFilter = false; 617 618 public MockFilter(ReturnCode targetRetCode) { 619 this.targetRetCode = targetRetCode; 620 } 621 622 @Override 623 public ReturnCode filterCell(final Cell v) throws IOException { 624 this.didCellPassToTheFilter = true; 625 return targetRetCode; 626 } 627 628 @Override 629 public boolean equals(Object obj) { 630 if (obj == null || !(obj instanceof MockFilter)) { 631 return false; 632 } 633 if (obj == this) { 634 return true; 635 } 636 MockFilter f = (MockFilter) obj; 637 return this.targetRetCode.equals(f.targetRetCode); 638 } 639 640 @Override 641 public int hashCode() { 642 return Objects.hash(this.targetRetCode); 643 } 644 } 645 646 private static class HintingMockFilter extends MockFilter implements HintingFilter { 647 public HintingMockFilter(ReturnCode targetRetCode) { 648 super(targetRetCode); 649 } 650 } 651 652 @Test 653 public void testShouldPassCurrentCellToFilter() throws IOException { 654 KeyValue kv1 = new KeyValue(Bytes.toBytes("row"), Bytes.toBytes("fam"), Bytes.toBytes("a"), 1, 655 Bytes.toBytes("value")); 656 KeyValue kv2 = new KeyValue(Bytes.toBytes("row"), Bytes.toBytes("fam"), Bytes.toBytes("a"), 2, 657 Bytes.toBytes("value")); 658 KeyValue kv3 = new KeyValue(Bytes.toBytes("row"), Bytes.toBytes("fam"), Bytes.toBytes("b"), 3, 659 Bytes.toBytes("value")); 660 KeyValue kv4 = new KeyValue(Bytes.toBytes("row1"), Bytes.toBytes("fam"), Bytes.toBytes("c"), 4, 661 Bytes.toBytes("value")); 662 663 MockFilter mockFilter = new MockFilter(ReturnCode.NEXT_COL); 664 FilterList filter = new FilterList(Operator.MUST_PASS_ONE, mockFilter); 665 666 filter.filterCell(kv1); 667 assertTrue(mockFilter.didCellPassToTheFilter); 668 669 mockFilter.didCellPassToTheFilter = false; 670 filter.filterCell(kv2); 671 assertFalse(mockFilter.didCellPassToTheFilter); 672 673 mockFilter.didCellPassToTheFilter = false; 674 filter.filterCell(kv3); 675 assertTrue(mockFilter.didCellPassToTheFilter); 676 677 mockFilter = new MockFilter(ReturnCode.INCLUDE_AND_NEXT_COL); 678 filter = new FilterList(Operator.MUST_PASS_ONE, mockFilter); 679 680 filter.filterCell(kv1); 681 assertTrue(mockFilter.didCellPassToTheFilter); 682 683 mockFilter.didCellPassToTheFilter = false; 684 filter.filterCell(kv2); 685 assertFalse(mockFilter.didCellPassToTheFilter); 686 687 mockFilter.didCellPassToTheFilter = false; 688 filter.filterCell(kv3); 689 assertTrue(mockFilter.didCellPassToTheFilter); 690 691 mockFilter = new MockFilter(ReturnCode.NEXT_ROW); 692 filter = new FilterList(Operator.MUST_PASS_ONE, mockFilter); 693 filter.filterCell(kv1); 694 assertTrue(mockFilter.didCellPassToTheFilter); 695 696 mockFilter.didCellPassToTheFilter = false; 697 filter.filterCell(kv2); 698 assertFalse(mockFilter.didCellPassToTheFilter); 699 700 mockFilter.didCellPassToTheFilter = false; 701 filter.filterCell(kv3); 702 assertFalse(mockFilter.didCellPassToTheFilter); 703 704 filter.reset(); 705 mockFilter.didCellPassToTheFilter = false; 706 filter.filterCell(kv4); 707 assertTrue(mockFilter.didCellPassToTheFilter); 708 709 mockFilter = new MockFilter(ReturnCode.INCLUDE_AND_SEEK_NEXT_ROW); 710 filter = new FilterList(Operator.MUST_PASS_ONE, mockFilter); 711 filter.filterCell(kv1); 712 assertTrue(mockFilter.didCellPassToTheFilter); 713 714 mockFilter.didCellPassToTheFilter = false; 715 filter.filterCell(kv2); 716 assertFalse(mockFilter.didCellPassToTheFilter); 717 718 mockFilter.didCellPassToTheFilter = false; 719 filter.filterCell(kv3); 720 assertFalse(mockFilter.didCellPassToTheFilter); 721 722 filter.reset(); 723 mockFilter.didCellPassToTheFilter = false; 724 filter.filterCell(kv4); 725 assertTrue(mockFilter.didCellPassToTheFilter); 726 } 727 728 @Test 729 public void testTheMaximalRule() throws IOException { 730 KeyValue kv1 = new KeyValue(Bytes.toBytes("row"), Bytes.toBytes("fam"), Bytes.toBytes("a"), 1, 731 Bytes.toBytes("value")); 732 MockFilter filter1 = new MockFilter(ReturnCode.INCLUDE); 733 MockFilter filter2 = new MockFilter(ReturnCode.INCLUDE_AND_NEXT_COL); 734 MockFilter filter3 = new MockFilter(ReturnCode.INCLUDE_AND_SEEK_NEXT_ROW); 735 MockFilter filter4 = new MockFilter(ReturnCode.NEXT_COL); 736 MockFilter filter5 = new MockFilter(ReturnCode.SKIP); 737 MockFilter filter6 = new HintingMockFilter(ReturnCode.SEEK_NEXT_USING_HINT); 738 MockFilter filter7 = new MockFilter(ReturnCode.NEXT_ROW); 739 740 FilterList filterList = new FilterList(Operator.MUST_PASS_ALL, filter1, filter2); 741 assertEquals(ReturnCode.INCLUDE_AND_NEXT_COL, filterList.filterCell(kv1)); 742 743 filterList = new FilterList(Operator.MUST_PASS_ALL, filter2, filter3); 744 assertEquals(ReturnCode.INCLUDE_AND_SEEK_NEXT_ROW, filterList.filterCell(kv1)); 745 746 filterList = new FilterList(Operator.MUST_PASS_ALL, filter4, filter5, filter6); 747 assertEquals(ReturnCode.SEEK_NEXT_USING_HINT, filterList.filterCell(kv1)); 748 749 filterList = new FilterList(Operator.MUST_PASS_ALL, filter4, filter6); 750 assertEquals(ReturnCode.SEEK_NEXT_USING_HINT, filterList.filterCell(kv1)); 751 752 filterList = new FilterList(Operator.MUST_PASS_ALL, filter3, filter1); 753 assertEquals(ReturnCode.INCLUDE_AND_SEEK_NEXT_ROW, filterList.filterCell(kv1)); 754 755 filterList = new FilterList(Operator.MUST_PASS_ALL, filter3, filter2, filter1, filter5); 756 assertEquals(ReturnCode.NEXT_ROW, filterList.filterCell(kv1)); 757 758 filterList = new FilterList(Operator.MUST_PASS_ALL, filter2, 759 new FilterList(Operator.MUST_PASS_ALL, filter3, filter4)); 760 assertEquals(ReturnCode.NEXT_ROW, filterList.filterCell(kv1)); 761 762 filterList = new FilterList(Operator.MUST_PASS_ALL, filter3, filter7); 763 assertEquals(ReturnCode.NEXT_ROW, filterList.filterCell(kv1)); 764 } 765 766 @Test 767 public void testTheMinimalRule() throws IOException { 768 KeyValue kv1 = new KeyValue(Bytes.toBytes("row"), Bytes.toBytes("fam"), Bytes.toBytes("a"), 1, 769 Bytes.toBytes("value")); 770 MockFilter filter1 = new MockFilter(ReturnCode.INCLUDE); 771 MockFilter filter2 = new MockFilter(ReturnCode.INCLUDE_AND_NEXT_COL); 772 MockFilter filter3 = new MockFilter(ReturnCode.INCLUDE_AND_SEEK_NEXT_ROW); 773 MockFilter filter4 = new MockFilter(ReturnCode.NEXT_COL); 774 MockFilter filter5 = new MockFilter(ReturnCode.SKIP); 775 MockFilter filter6 = new HintingMockFilter(ReturnCode.SEEK_NEXT_USING_HINT); 776 FilterList filterList = new FilterList(Operator.MUST_PASS_ONE, filter1, filter2); 777 assertEquals(ReturnCode.INCLUDE, filterList.filterCell(kv1)); 778 779 filterList = new FilterList(Operator.MUST_PASS_ONE, filter2, filter3); 780 assertEquals(ReturnCode.INCLUDE_AND_NEXT_COL, filterList.filterCell(kv1)); 781 782 filterList = new FilterList(Operator.MUST_PASS_ONE, filter4, filter5, filter6); 783 assertEquals(ReturnCode.SKIP, filterList.filterCell(kv1)); 784 785 filterList = new FilterList(Operator.MUST_PASS_ONE, filter4, filter6); 786 assertEquals(ReturnCode.SKIP, filterList.filterCell(kv1)); 787 788 filterList = new FilterList(Operator.MUST_PASS_ONE, filter3, filter1); 789 assertEquals(ReturnCode.INCLUDE, filterList.filterCell(kv1)); 790 791 filterList = new FilterList(Operator.MUST_PASS_ONE, filter3, filter2, filter1, filter5); 792 assertEquals(ReturnCode.INCLUDE, filterList.filterCell(kv1)); 793 794 filterList = new FilterList(Operator.MUST_PASS_ONE, filter2, 795 new FilterList(Operator.MUST_PASS_ONE, filter3, filter4)); 796 assertEquals(ReturnCode.INCLUDE_AND_NEXT_COL, filterList.filterCell(kv1)); 797 798 filterList = new FilterList(Operator.MUST_PASS_ONE, filter2, 799 new FilterList(Operator.MUST_PASS_ONE, filter3, filter4)); 800 assertEquals(ReturnCode.INCLUDE_AND_NEXT_COL, filterList.filterCell(kv1)); 801 802 filterList = new FilterList(Operator.MUST_PASS_ONE, filter6, filter6); 803 assertEquals(ReturnCode.SEEK_NEXT_USING_HINT, filterList.filterCell(kv1)); 804 } 805 806 static class MockSeekHintFilter extends FilterBase { 807 private Cell returnCell; 808 809 public MockSeekHintFilter(Cell returnCell) { 810 this.returnCell = returnCell; 811 } 812 813 @Override 814 public ReturnCode filterCell(final Cell v) throws IOException { 815 return ReturnCode.SEEK_NEXT_USING_HINT; 816 } 817 818 @Override 819 public Cell getNextCellHint(Cell currentCell) throws IOException { 820 return this.returnCell; 821 } 822 823 @Override 824 public boolean equals(Object obj) { 825 if (obj == null || !(obj instanceof MockSeekHintFilter)) { 826 return false; 827 } 828 if (obj == this) { 829 return true; 830 } 831 MockSeekHintFilter f = (MockSeekHintFilter) obj; 832 return this.returnCell.equals(f.returnCell); 833 } 834 835 @Override 836 public int hashCode() { 837 return Objects.hash(this.returnCell); 838 } 839 } 840 841 @Test 842 public void testReversedFilterListWithMockSeekHintFilter() throws IOException { 843 KeyValue kv1 = new KeyValue(Bytes.toBytes("row1"), Bytes.toBytes("fam"), Bytes.toBytes("a"), 1, 844 Bytes.toBytes("value")); 845 KeyValue kv2 = new KeyValue(Bytes.toBytes("row2"), Bytes.toBytes("fam"), Bytes.toBytes("a"), 1, 846 Bytes.toBytes("value")); 847 KeyValue kv3 = new KeyValue(Bytes.toBytes("row3"), Bytes.toBytes("fam"), Bytes.toBytes("a"), 1, 848 Bytes.toBytes("value")); 849 Filter filter1 = new MockSeekHintFilter(kv1); 850 filter1.setReversed(true); 851 Filter filter2 = new MockSeekHintFilter(kv2); 852 filter2.setReversed(true); 853 Filter filter3 = new MockSeekHintFilter(kv3); 854 filter3.setReversed(true); 855 856 FilterList filterList = new FilterList(Operator.MUST_PASS_ONE); 857 filterList.setReversed(true); 858 filterList.addFilter(filter1); 859 filterList.addFilter(filter2); 860 filterList.addFilter(filter3); 861 862 assertEquals(ReturnCode.SEEK_NEXT_USING_HINT, filterList.filterCell(kv1)); 863 assertEquals(kv3, filterList.getNextCellHint(kv1)); 864 865 filterList = new FilterList(Operator.MUST_PASS_ALL); 866 filterList.setReversed(true); 867 filterList.addFilter(filter1); 868 filterList.addFilter(filter2); 869 filterList.addFilter(filter3); 870 871 assertEquals(ReturnCode.SEEK_NEXT_USING_HINT, filterList.filterCell(kv1)); 872 assertEquals(kv1, filterList.getNextCellHint(kv1)); 873 } 874 875 @Test 876 public void testReversedFilterListWithOR() throws IOException { 877 byte[] r22 = Bytes.toBytes("Row22"); 878 byte[] r2 = Bytes.toBytes("Row2"); 879 byte[] r1 = Bytes.toBytes("Row1"); 880 881 FilterList filterList = new FilterList(FilterList.Operator.MUST_PASS_ONE); 882 filterList.setReversed(true); 883 PrefixFilter prefixFilter = new PrefixFilter(r2); 884 prefixFilter.setReversed(true); 885 filterList.addFilter(prefixFilter); 886 filterList.filterRowKey(KeyValueUtil.createFirstOnRow(r22)); 887 assertEquals(ReturnCode.INCLUDE, filterList.filterCell(new KeyValue(r22, r22, r22))); 888 assertEquals(ReturnCode.INCLUDE, filterList.filterCell(new KeyValue(r2, r2, r2))); 889 890 filterList.reset(); 891 filterList.filterRowKey(KeyValueUtil.createFirstOnRow(r1)); 892 assertEquals(ReturnCode.SKIP, filterList.filterCell(new KeyValue(r1, r1, r1))); 893 894 filterList = new FilterList(FilterList.Operator.MUST_PASS_ONE); 895 filterList.setReversed(true); 896 AlwaysNextColFilter alwaysNextColFilter = new AlwaysNextColFilter(); 897 alwaysNextColFilter.setReversed(true); 898 prefixFilter = new PrefixFilter(r2); 899 prefixFilter.setReversed(true); 900 filterList.addFilter(alwaysNextColFilter); 901 filterList.addFilter(prefixFilter); 902 filterList.filterRowKey(KeyValueUtil.createFirstOnRow(r22)); 903 assertEquals(ReturnCode.INCLUDE, filterList.filterCell(new KeyValue(r22, r22, r22))); 904 assertEquals(ReturnCode.INCLUDE, filterList.filterCell(new KeyValue(r2, r2, r2))); 905 906 filterList.reset(); 907 filterList.filterRowKey(KeyValueUtil.createFirstOnRow(r1)); 908 assertEquals(ReturnCode.NEXT_COL, filterList.filterCell(new KeyValue(r1, r1, r1))); 909 } 910 911 @Test 912 public void testKeyOnlyFilterTransformCell() throws IOException { 913 Cell c; 914 KeyValue kv1 = new KeyValue(Bytes.toBytes("row"), Bytes.toBytes("cf"), Bytes.toBytes("column1"), 915 1, Bytes.toBytes("value1")); 916 KeyValue kv2 = new KeyValue(Bytes.toBytes("row"), Bytes.toBytes("cf"), Bytes.toBytes("column1"), 917 2, Bytes.toBytes("value2")); 918 919 Filter filter1 = new SingleColumnValueFilter(Bytes.toBytes("cf"), Bytes.toBytes("column1"), 920 CompareOperator.EQUAL, Bytes.toBytes("value1")); 921 Filter filter2 = new SingleColumnValueFilter(Bytes.toBytes("cf"), Bytes.toBytes("column1"), 922 CompareOperator.EQUAL, Bytes.toBytes("value2")); 923 FilterList internalFilterList = new FilterList(Operator.MUST_PASS_ONE, filter1, filter2); 924 925 FilterList keyOnlyFilterFirst = 926 new FilterList(Operator.MUST_PASS_ALL, new KeyOnlyFilter(), internalFilterList); 927 928 assertEquals(ReturnCode.INCLUDE, keyOnlyFilterFirst.filterCell(kv1)); 929 c = keyOnlyFilterFirst.transformCell(kv1); 930 assertEquals(0, c.getValueLength()); 931 assertEquals(ReturnCode.INCLUDE, keyOnlyFilterFirst.filterCell(kv2)); 932 c = keyOnlyFilterFirst.transformCell(kv2); 933 assertEquals(0, c.getValueLength()); 934 935 internalFilterList.reset(); 936 FilterList keyOnlyFilterLast = 937 new FilterList(Operator.MUST_PASS_ALL, new KeyOnlyFilter(), internalFilterList); 938 assertEquals(ReturnCode.INCLUDE, keyOnlyFilterLast.filterCell(kv1)); 939 c = keyOnlyFilterLast.transformCell(kv1); 940 assertEquals(0, c.getValueLength()); 941 assertEquals(ReturnCode.INCLUDE, keyOnlyFilterLast.filterCell(kv2)); 942 c = keyOnlyFilterLast.transformCell(kv2); 943 assertEquals(0, c.getValueLength()); 944 } 945 946 @Test 947 public void testEmptyFilterListTransformCell() throws IOException { 948 KeyValue kv = new KeyValue(Bytes.toBytes("row"), Bytes.toBytes("cf"), Bytes.toBytes("column1"), 949 1, Bytes.toBytes("value")); 950 FilterList filterList = new FilterList(Operator.MUST_PASS_ALL); 951 assertEquals(ReturnCode.INCLUDE, filterList.filterCell(kv)); 952 assertEquals(kv, filterList.transformCell(kv)); 953 954 filterList = new FilterList(Operator.MUST_PASS_ONE); 955 assertEquals(ReturnCode.INCLUDE, filterList.filterCell(kv)); 956 assertEquals(kv, filterList.transformCell(kv)); 957 } 958 959 private static class MockNextRowFilter extends FilterBase { 960 private int hitCount = 0; 961 962 @Override 963 public ReturnCode filterCell(final Cell v) throws IOException { 964 hitCount++; 965 return ReturnCode.NEXT_ROW; 966 } 967 968 public int getHitCount() { 969 return hitCount; 970 } 971 } 972 973 @Test 974 public void testRowCountFilter() throws IOException { 975 KeyValue kv1 = new KeyValue(Bytes.toBytes("row"), Bytes.toBytes("fam1"), Bytes.toBytes("a"), 1, 976 Bytes.toBytes("value")); 977 KeyValue kv2 = new KeyValue(Bytes.toBytes("row"), Bytes.toBytes("fam2"), Bytes.toBytes("a"), 2, 978 Bytes.toBytes("value")); 979 MockNextRowFilter mockNextRowFilter = new MockNextRowFilter(); 980 FilterList filter = new FilterList(Operator.MUST_PASS_ONE, mockNextRowFilter); 981 filter.filterCell(kv1); 982 filter.filterCell(kv2); 983 assertEquals(2, mockNextRowFilter.getHitCount()); 984 } 985 986 private static class TransformFilter extends FilterBase { 987 private ReturnCode targetRetCode; 988 private boolean transformed = false; 989 990 public TransformFilter(ReturnCode targetRetCode) { 991 this.targetRetCode = targetRetCode; 992 } 993 994 @Override 995 public ReturnCode filterCell(final Cell v) throws IOException { 996 return targetRetCode; 997 } 998 999 @Override 1000 public Cell transformCell(Cell c) throws IOException { 1001 transformed = true; 1002 return super.transformCell(c); 1003 } 1004 1005 public boolean getTransformed() { 1006 return this.transformed; 1007 } 1008 1009 @Override 1010 public boolean equals(Object obj) { 1011 if (!(obj instanceof TransformFilter)) { 1012 return false; 1013 } 1014 if (obj == this) { 1015 return true; 1016 } 1017 TransformFilter f = (TransformFilter) obj; 1018 return this.targetRetCode.equals(f.targetRetCode); 1019 } 1020 1021 @Override 1022 public int hashCode() { 1023 return Objects.hash(this.targetRetCode); 1024 } 1025 } 1026 1027 @Test 1028 public void testTransformCell() throws IOException { 1029 KeyValue kv = new KeyValue(Bytes.toBytes("row"), Bytes.toBytes("cf"), Bytes.toBytes("column1"), 1030 1, Bytes.toBytes("value")); 1031 1032 // case MUST_PASS_ONE 1033 TransformFilter filter1 = new TransformFilter(ReturnCode.INCLUDE); 1034 TransformFilter filter2 = new TransformFilter(ReturnCode.NEXT_ROW); 1035 TransformFilter filter3 = new TransformFilter(ReturnCode.SEEK_NEXT_USING_HINT); 1036 FilterList filterList = new FilterList(Operator.MUST_PASS_ONE, filter1, filter2, filter3); 1037 assertEquals(ReturnCode.INCLUDE, filterList.filterCell(kv)); 1038 assertEquals(kv, filterList.transformCell(kv)); 1039 assertEquals(true, filter1.getTransformed()); 1040 assertEquals(false, filter2.getTransformed()); 1041 assertEquals(false, filter3.getTransformed()); 1042 1043 // case MUST_PASS_ALL 1044 filter1 = new TransformFilter(ReturnCode.INCLUDE); 1045 filter2 = new TransformFilter(ReturnCode.INCLUDE_AND_SEEK_NEXT_ROW); 1046 filter3 = new TransformFilter(ReturnCode.INCLUDE_AND_NEXT_COL); 1047 filterList = new FilterList(Operator.MUST_PASS_ALL, filter1, filter2, filter3); 1048 1049 assertEquals(ReturnCode.INCLUDE_AND_SEEK_NEXT_ROW, filterList.filterCell(kv)); 1050 assertEquals(kv, filterList.transformCell(kv)); 1051 assertEquals(true, filter1.getTransformed()); 1052 assertEquals(true, filter2.getTransformed()); 1053 assertEquals(true, filter3.getTransformed()); 1054 } 1055 1056 @Test 1057 public void testFilterListWithORWhenPassingCellMismatchPreviousRC() throws IOException { 1058 // Mainly test FilterListWithOR#calculateReturnCodeByPrevCellAndRC method with two sub-filters. 1059 KeyValue kv1 = new KeyValue(Bytes.toBytes("row1"), Bytes.toBytes("fam"), Bytes.toBytes("a"), 1060 100, Bytes.toBytes("value")); 1061 KeyValue kv2 = new KeyValue(Bytes.toBytes("row1"), Bytes.toBytes("fam"), Bytes.toBytes("a"), 99, 1062 Bytes.toBytes("value")); 1063 KeyValue kv3 = new KeyValue(Bytes.toBytes("row1"), Bytes.toBytes("fam"), Bytes.toBytes("b"), 1, 1064 Bytes.toBytes("value")); 1065 KeyValue kv4 = new KeyValue(Bytes.toBytes("row1"), Bytes.toBytes("fan"), Bytes.toBytes("a"), 1, 1066 Bytes.toBytes("value")); 1067 Filter subFilter1 = Mockito.mock(FilterBase.class); 1068 Mockito.when(subFilter1.filterCell(kv1)).thenReturn(ReturnCode.INCLUDE_AND_NEXT_COL); 1069 Mockito.when(subFilter1.filterCell(kv2)).thenReturn(ReturnCode.NEXT_COL); 1070 Mockito.when(subFilter1.filterCell(kv3)).thenReturn(ReturnCode.INCLUDE_AND_NEXT_COL); 1071 Mockito.when(subFilter1.filterCell(kv4)).thenReturn(ReturnCode.INCLUDE_AND_NEXT_COL); 1072 1073 Filter subFilter2 = Mockito.mock(FilterBase.class); 1074 Mockito.when(subFilter2.filterCell(kv1)).thenReturn(ReturnCode.SKIP); 1075 Mockito.when(subFilter2.filterCell(kv2)).thenReturn(ReturnCode.NEXT_ROW); 1076 Mockito.when(subFilter2.filterCell(kv3)).thenReturn(ReturnCode.NEXT_ROW); 1077 Mockito.when(subFilter2.filterCell(kv4)).thenReturn(ReturnCode.INCLUDE_AND_SEEK_NEXT_ROW); 1078 1079 Filter filterList = new FilterList(Operator.MUST_PASS_ONE, subFilter1, subFilter2); 1080 assertEquals(ReturnCode.INCLUDE, filterList.filterCell(kv1)); 1081 assertEquals(ReturnCode.NEXT_COL, filterList.filterCell(kv2)); 1082 assertEquals(ReturnCode.INCLUDE_AND_NEXT_COL, filterList.filterCell(kv3)); 1083 assertEquals(ReturnCode.INCLUDE_AND_NEXT_COL, filterList.filterCell(kv4)); 1084 1085 // One sub-filter will filterAllRemaining but other sub-filter will return SEEK_HINT 1086 subFilter1 = Mockito.mock(FilterBase.class); 1087 Mockito.when(subFilter1.filterAllRemaining()).thenReturn(true); 1088 Mockito.when(subFilter1.filterCell(kv1)).thenReturn(ReturnCode.NEXT_ROW); 1089 1090 subFilter2 = Mockito.mock(FilterBase.class); 1091 Mockito.when(subFilter2.filterCell(kv1)).thenReturn(ReturnCode.SEEK_NEXT_USING_HINT); 1092 filterList = new FilterList(Operator.MUST_PASS_ONE, subFilter1, subFilter2); 1093 assertEquals(ReturnCode.SEEK_NEXT_USING_HINT, filterList.filterCell(kv1)); 1094 1095 // Two sub-filter returns SEEK_NEXT_USING_HINT, then we should return SEEK_NEXT_USING_HINT. 1096 subFilter1 = Mockito.mock(FilterBase.class); 1097 Mockito.when(subFilter1.filterCell(kv1)).thenReturn(ReturnCode.SEEK_NEXT_USING_HINT); 1098 1099 subFilter2 = Mockito.mock(FilterBase.class); 1100 Mockito.when(subFilter2.filterCell(kv1)).thenReturn(ReturnCode.SEEK_NEXT_USING_HINT); 1101 filterList = new FilterList(Operator.MUST_PASS_ONE, subFilter1, subFilter2); 1102 assertEquals(ReturnCode.SEEK_NEXT_USING_HINT, filterList.filterCell(kv1)); 1103 } 1104}