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.Assert.assertEquals;
021
022import java.io.IOException;
023import java.util.ArrayList;
024import java.util.Arrays;
025import java.util.List;
026import org.apache.hadoop.hbase.Cell;
027import org.apache.hadoop.hbase.HBaseClassTestRule;
028import org.apache.hadoop.hbase.HBaseTestingUtility;
029import org.apache.hadoop.hbase.HConstants;
030import org.apache.hadoop.hbase.KeyValueUtil;
031import org.apache.hadoop.hbase.TableName;
032import org.apache.hadoop.hbase.client.Put;
033import org.apache.hadoop.hbase.client.Result;
034import org.apache.hadoop.hbase.client.ResultScanner;
035import org.apache.hadoop.hbase.client.Scan;
036import org.apache.hadoop.hbase.client.Table;
037import org.apache.hadoop.hbase.filter.MultiRowRangeFilter.RowRange;
038import org.apache.hadoop.hbase.testclassification.MediumTests;
039import org.apache.hadoop.hbase.util.Bytes;
040import org.junit.AfterClass;
041import org.junit.Assert;
042import org.junit.BeforeClass;
043import org.junit.ClassRule;
044import org.junit.Rule;
045import org.junit.Test;
046import org.junit.experimental.categories.Category;
047import org.junit.rules.TestName;
048import org.slf4j.Logger;
049import org.slf4j.LoggerFactory;
050
051@Category(MediumTests.class)
052public class TestMultiRowRangeFilter {
053
054  @ClassRule
055  public static final HBaseClassTestRule CLASS_RULE =
056      HBaseClassTestRule.forClass(TestMultiRowRangeFilter.class);
057
058  private final static HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility();
059  private static final Logger LOG = LoggerFactory.getLogger(TestMultiRowRangeFilter.class);
060  private byte[] family = Bytes.toBytes("family");
061  private byte[] qf = Bytes.toBytes("qf");
062  private byte[] value = Bytes.toBytes("val");
063  private TableName tableName;
064  private int numRows = 100;
065
066  @Rule
067  public TestName name = new TestName();
068
069  /**
070   * @throws Exception
071   */
072  @BeforeClass
073  public static void setUpBeforeClass() throws Exception {
074    TEST_UTIL.startMiniCluster();
075  }
076
077  /**
078   * @throws Exception
079   */
080  @AfterClass
081  public static void tearDownAfterClass() throws Exception {
082    TEST_UTIL.shutdownMiniCluster();
083  }
084
085  @Test
086  public void testRanges() throws IOException {
087    byte[] key1Start = new byte[] {-3};
088    byte[] key1End  = new byte[] {-2};
089
090    byte[] key2Start = new byte[] {5};
091    byte[] key2End  = new byte[] {6};
092
093    byte[] badKey = new byte[] {-10};
094
095    MultiRowRangeFilter filter = new MultiRowRangeFilter(Arrays.asList(
096      new MultiRowRangeFilter.RowRange(key1Start, true, key1End, false),
097      new MultiRowRangeFilter.RowRange(key2Start, true, key2End, false)
098        ));
099    filter.filterRowKey(KeyValueUtil.createFirstOnRow(badKey));
100    /*
101     * FAILS -- includes BAD key!
102     * Expected :SEEK_NEXT_USING_HINT
103     * Actual   :INCLUDE
104     * */
105    assertEquals(Filter.ReturnCode.SEEK_NEXT_USING_HINT, filter.filterCell(null));
106  }
107
108  @Test
109  public void testOutOfOrderScannerNextException() throws Exception {
110    MultiRowRangeFilter filter = new MultiRowRangeFilter(Arrays.asList(
111            new MultiRowRangeFilter.RowRange(Bytes.toBytes("b"), true, Bytes.toBytes("c"), true),
112            new MultiRowRangeFilter.RowRange(Bytes.toBytes("d"), true, Bytes.toBytes("e"), true)
113    ));
114    filter.filterRowKey(KeyValueUtil.createFirstOnRow(Bytes.toBytes("a")));
115    assertEquals(Filter.ReturnCode.SEEK_NEXT_USING_HINT, filter.filterCell(null));
116    filter.filterRowKey(KeyValueUtil.createFirstOnRow(Bytes.toBytes("b")));
117    assertEquals(Filter.ReturnCode.INCLUDE, filter.filterCell(null));
118    filter.filterRowKey(KeyValueUtil.createFirstOnRow(Bytes.toBytes("c")));
119    assertEquals(Filter.ReturnCode.INCLUDE, filter.filterCell(null));
120    filter.filterRowKey(KeyValueUtil.createFirstOnRow(Bytes.toBytes("d")));
121    assertEquals(Filter.ReturnCode.INCLUDE, filter.filterCell(null));
122    filter.filterRowKey(KeyValueUtil.createFirstOnRow(Bytes.toBytes("e")));
123    assertEquals(Filter.ReturnCode.INCLUDE, filter.filterCell(null));
124  }
125
126  @Test
127  public void testMergeAndSortWithEmptyStartRow() throws IOException {
128    List<RowRange> ranges = new ArrayList<>();
129    ranges.add(new RowRange(Bytes.toBytes(""), true, Bytes.toBytes(20), false));
130    ranges.add(new RowRange(Bytes.toBytes(15), true, Bytes.toBytes(40), false));
131    List<RowRange> actualRanges = MultiRowRangeFilter.sortAndMerge(ranges);
132    List<RowRange> expectedRanges = new ArrayList<>();
133    expectedRanges.add(new RowRange(Bytes.toBytes(""), true, Bytes.toBytes(40), false));
134    assertRangesEqual(expectedRanges, actualRanges);
135  }
136
137  @Test
138  public void testMergeAndSortWithEmptyStopRow() throws IOException {
139    List<RowRange> ranges = new ArrayList<>();
140    ranges.add(new RowRange(Bytes.toBytes(10), true, Bytes.toBytes(20), false));
141    ranges.add(new RowRange(Bytes.toBytes(15), true, Bytes.toBytes(""), false));
142    ranges.add(new RowRange(Bytes.toBytes(30), true, Bytes.toBytes(70), false));
143    List<RowRange> actualRanges = MultiRowRangeFilter.sortAndMerge(ranges);
144    List<RowRange> expectedRanges = new ArrayList<>();
145    expectedRanges.add(new RowRange(Bytes.toBytes(10), true, Bytes.toBytes(""), false));
146    assertRangesEqual(expectedRanges, actualRanges);
147  }
148
149  @Test
150  public void testMergeAndSortWithEmptyStartRowAndStopRow() throws IOException {
151    List<RowRange> ranges = new ArrayList<>();
152    ranges.add(new RowRange(Bytes.toBytes(10), true, Bytes.toBytes(20), false));
153    ranges.add(new RowRange(Bytes.toBytes(""), true, Bytes.toBytes(""), false));
154    ranges.add(new RowRange(Bytes.toBytes(30), true, Bytes.toBytes(70), false));
155    List<RowRange> actualRanges = MultiRowRangeFilter.sortAndMerge(ranges);
156    List<RowRange> expectedRanges = new ArrayList<>();
157    expectedRanges.add(new RowRange(Bytes.toBytes(""), true, Bytes.toBytes(""), false));
158    assertRangesEqual(expectedRanges, actualRanges);
159  }
160
161  @Test(expected=IllegalArgumentException.class)
162  public void testMultiRowRangeWithoutRange() throws IOException {
163    List<RowRange> ranges = new ArrayList<>();
164    new MultiRowRangeFilter(ranges);
165  }
166
167  @Test(expected=IllegalArgumentException.class)
168  public void testMultiRowRangeWithInvalidRange() throws IOException {
169    List<RowRange> ranges = new ArrayList<>();
170    ranges.add(new RowRange(Bytes.toBytes(10), true, Bytes.toBytes(20), false));
171    // the start row larger than the stop row
172    ranges.add(new RowRange(Bytes.toBytes(80), true, Bytes.toBytes(20), false));
173    ranges.add(new RowRange(Bytes.toBytes(30), true, Bytes.toBytes(70), false));
174    new MultiRowRangeFilter(ranges);
175  }
176
177  @Test
178  public void testMergeAndSortWithoutOverlap() throws IOException {
179    List<RowRange> ranges = new ArrayList<>();
180    ranges.add(new RowRange(Bytes.toBytes(10), true, Bytes.toBytes(20), false));
181    ranges.add(new RowRange(Bytes.toBytes(30), true, Bytes.toBytes(40), false));
182    ranges.add(new RowRange(Bytes.toBytes(60), true, Bytes.toBytes(70), false));
183    List<RowRange> actualRanges = MultiRowRangeFilter.sortAndMerge(ranges);
184    List<RowRange> expectedRanges = new ArrayList<>();
185    expectedRanges.add(new RowRange(Bytes.toBytes(10), true, Bytes.toBytes(20), false));
186    expectedRanges.add(new RowRange(Bytes.toBytes(30), true, Bytes.toBytes(40), false));
187    expectedRanges.add(new RowRange(Bytes.toBytes(60), true, Bytes.toBytes(70), false));
188    assertRangesEqual(expectedRanges, actualRanges);
189  }
190
191  @Test
192  public void testMergeAndSortWithOverlap() throws IOException {
193    List<RowRange> ranges = new ArrayList<>();
194    ranges.add(new RowRange(Bytes.toBytes(10), true, Bytes.toBytes(20), false));
195    ranges.add(new RowRange(Bytes.toBytes(15), true, Bytes.toBytes(40), false));
196    ranges.add(new RowRange(Bytes.toBytes(20), true, Bytes.toBytes(30), false));
197    ranges.add(new RowRange(Bytes.toBytes(30), true, Bytes.toBytes(50), false));
198    ranges.add(new RowRange(Bytes.toBytes(30), true, Bytes.toBytes(70), false));
199    ranges.add(new RowRange(Bytes.toBytes(90), true, Bytes.toBytes(100), false));
200    ranges.add(new RowRange(Bytes.toBytes(95), true, Bytes.toBytes(100), false));
201    List<RowRange> actualRanges = MultiRowRangeFilter.sortAndMerge(ranges);
202    List<RowRange> expectedRanges = new ArrayList<>();
203    expectedRanges.add(new RowRange(Bytes.toBytes(10), true, Bytes.toBytes(70), false));
204    expectedRanges.add(new RowRange(Bytes.toBytes(90), true, Bytes.toBytes(100), false));
205    assertRangesEqual(expectedRanges, actualRanges);
206  }
207
208  @Test
209  public void testMergeAndSortWithStartRowInclusive() throws IOException {
210    List<RowRange> ranges = new ArrayList<>();
211    ranges.add(new RowRange(Bytes.toBytes(10), true, Bytes.toBytes(20), false));
212    ranges.add(new RowRange(Bytes.toBytes(20), true, Bytes.toBytes(""), false));
213    List<RowRange> actualRanges = MultiRowRangeFilter.sortAndMerge(ranges);
214    List<RowRange> expectedRanges = new ArrayList<>();
215    expectedRanges.add(new RowRange(Bytes.toBytes(10), true, Bytes.toBytes(""), false));
216    assertRangesEqual(expectedRanges, actualRanges);
217  }
218
219  @Test
220  public void testMergeAndSortWithRowExclusive() throws IOException {
221    List<RowRange> ranges = new ArrayList<>();
222    ranges.add(new RowRange(Bytes.toBytes(10), true, Bytes.toBytes(20), false));
223    ranges.add(new RowRange(Bytes.toBytes(20), false, Bytes.toBytes(""), false));
224    List<RowRange> actualRanges = MultiRowRangeFilter.sortAndMerge(ranges);
225    List<RowRange> expectedRanges = new ArrayList<>();
226    expectedRanges.add(new RowRange(Bytes.toBytes(10), true, Bytes.toBytes(20), false));
227    expectedRanges.add(new RowRange(Bytes.toBytes(20), false, Bytes.toBytes(""), false));
228    assertRangesEqual(expectedRanges, actualRanges);
229  }
230
231  @Test
232  public void testMergeAndSortWithRowInclusive() throws IOException {
233    List<RowRange> ranges = new ArrayList<>();
234    ranges.add(new RowRange(Bytes.toBytes(10), true, Bytes.toBytes(20), true));
235    ranges.add(new RowRange(Bytes.toBytes(20), false, Bytes.toBytes(""), false));
236    List<RowRange> actualRanges = MultiRowRangeFilter.sortAndMerge(ranges);
237    List<RowRange> expectedRanges = new ArrayList<>();
238    expectedRanges.add(new RowRange(Bytes.toBytes(10), true, Bytes.toBytes(""), false));
239    assertRangesEqual(expectedRanges, actualRanges);
240  }
241
242  public void assertRangesEqual(List<RowRange> expected, List<RowRange> actual) {
243    assertEquals(expected.size(), actual.size());
244    for(int i = 0; i < expected.size(); i++) {
245      Assert.assertTrue(Bytes.equals(expected.get(i).getStartRow(), actual.get(i).getStartRow()));
246      Assert.assertTrue(expected.get(i).isStartRowInclusive() ==
247          actual.get(i).isStartRowInclusive());
248      Assert.assertTrue(Bytes.equals(expected.get(i).getStopRow(), actual.get(i).getStopRow()));
249      Assert.assertTrue(expected.get(i).isStopRowInclusive() ==
250          actual.get(i).isStopRowInclusive());
251    }
252  }
253
254  @Test
255  public void testMultiRowRangeFilterWithRangeOverlap() throws IOException {
256    tableName = TableName.valueOf(name.getMethodName());
257    Table ht = TEST_UTIL.createTable(tableName, family, Integer.MAX_VALUE);
258    generateRows(numRows, ht, family, qf, value);
259
260    Scan scan = new Scan();
261    scan.setMaxVersions();
262
263    List<RowRange> ranges = new ArrayList<>();
264    ranges.add(new RowRange(Bytes.toBytes(10), true, Bytes.toBytes(20), false));
265    ranges.add(new RowRange(Bytes.toBytes(15), true, Bytes.toBytes(40), false));
266    ranges.add(new RowRange(Bytes.toBytes(65), true, Bytes.toBytes(75), false));
267    ranges.add(new RowRange(Bytes.toBytes(60), true, null, false));
268    ranges.add(new RowRange(Bytes.toBytes(60), true, Bytes.toBytes(80), false));
269
270    MultiRowRangeFilter filter = new MultiRowRangeFilter(ranges);
271    scan.setFilter(filter);
272    int resultsSize = getResultsSize(ht, scan);
273    LOG.info("found " + resultsSize + " results");
274    List<Cell> results1 = getScanResult(Bytes.toBytes(10), Bytes.toBytes(40), ht);
275    List<Cell> results2 = getScanResult(Bytes.toBytes(60), Bytes.toBytes(""), ht);
276
277    assertEquals(results1.size() + results2.size(), resultsSize);
278
279    ht.close();
280  }
281
282  @Test
283  public void testMultiRowRangeFilterWithoutRangeOverlap() throws IOException {
284    tableName = TableName.valueOf(name.getMethodName());
285    Table ht = TEST_UTIL.createTable(tableName, family, Integer.MAX_VALUE);
286    generateRows(numRows, ht, family, qf, value);
287
288    Scan scan = new Scan();
289    scan.setMaxVersions();
290
291    List<RowRange> ranges = new ArrayList<>();
292    ranges.add(new RowRange(Bytes.toBytes(30), true, Bytes.toBytes(40), false));
293    ranges.add(new RowRange(Bytes.toBytes(10), true, Bytes.toBytes(20), false));
294    ranges.add(new RowRange(Bytes.toBytes(60), true, Bytes.toBytes(70), false));
295
296    MultiRowRangeFilter filter = new MultiRowRangeFilter(ranges);
297    scan.setFilter(filter);
298    int resultsSize = getResultsSize(ht, scan);
299    LOG.info("found " + resultsSize + " results");
300    List<Cell> results1 = getScanResult(Bytes.toBytes(10), Bytes.toBytes(20), ht);
301    List<Cell> results2 = getScanResult(Bytes.toBytes(30), Bytes.toBytes(40), ht);
302    List<Cell> results3 = getScanResult(Bytes.toBytes(60), Bytes.toBytes(70), ht);
303
304    assertEquals(results1.size() + results2.size() + results3.size(), resultsSize);
305
306    ht.close();
307  }
308
309  @Test
310  public void testMultiRowRangeFilterWithEmptyStartRow() throws IOException {
311    tableName = TableName.valueOf(name.getMethodName());
312    Table ht = TEST_UTIL.createTable(tableName, family, Integer.MAX_VALUE);
313    generateRows(numRows, ht, family, qf, value);
314    Scan scan = new Scan();
315    scan.setMaxVersions();
316
317    List<RowRange> ranges = new ArrayList<>();
318    ranges.add(new RowRange(Bytes.toBytes(""), true, Bytes.toBytes(10), false));
319    ranges.add(new RowRange(Bytes.toBytes(30), true, Bytes.toBytes(40), false));
320
321    MultiRowRangeFilter filter = new MultiRowRangeFilter(ranges);
322    scan.setFilter(filter);
323    int resultsSize = getResultsSize(ht, scan);
324    List<Cell> results1 = getScanResult(Bytes.toBytes(""), Bytes.toBytes(10), ht);
325    List<Cell> results2 = getScanResult(Bytes.toBytes(30), Bytes.toBytes(40), ht);
326    assertEquals(results1.size() + results2.size(), resultsSize);
327
328    ht.close();
329  }
330
331  @Test
332  public void testMultiRowRangeFilterWithEmptyStopRow() throws IOException {
333    tableName = TableName.valueOf(name.getMethodName());
334    Table ht = TEST_UTIL.createTable(tableName, family, Integer.MAX_VALUE);
335    generateRows(numRows, ht, family, qf, value);
336    Scan scan = new Scan();
337    scan.setMaxVersions();
338
339    List<RowRange> ranges = new ArrayList<>();
340    ranges.add(new RowRange(Bytes.toBytes(10), true, Bytes.toBytes(""), false));
341    ranges.add(new RowRange(Bytes.toBytes(30), true, Bytes.toBytes(40), false));
342
343    MultiRowRangeFilter filter = new MultiRowRangeFilter(ranges);
344    scan.setFilter(filter);
345    int resultsSize = getResultsSize(ht, scan);
346    List<Cell> results1 = getScanResult(Bytes.toBytes(10), Bytes.toBytes(""), ht);
347    assertEquals(results1.size(), resultsSize);
348
349    ht.close();
350  }
351
352  @Test
353  public void testMultiRowRangeFilterWithInclusive() throws IOException {
354    tableName = TableName.valueOf(name.getMethodName());
355    Table ht = TEST_UTIL.createTable(tableName, family, Integer.MAX_VALUE);
356    generateRows(numRows, ht, family, qf, value);
357
358    Scan scan = new Scan();
359    scan.setMaxVersions();
360
361    List<RowRange> ranges = new ArrayList<>();
362    ranges.add(new RowRange(Bytes.toBytes(10), true, Bytes.toBytes(20), false));
363    ranges.add(new RowRange(Bytes.toBytes(20), true, Bytes.toBytes(40), false));
364    ranges.add(new RowRange(Bytes.toBytes(65), true, Bytes.toBytes(75), false));
365    ranges.add(new RowRange(Bytes.toBytes(60), true, null, false));
366    ranges.add(new RowRange(Bytes.toBytes(60), true, Bytes.toBytes(80), false));
367
368    MultiRowRangeFilter filter = new MultiRowRangeFilter(ranges);
369    scan.setFilter(filter);
370    int resultsSize = getResultsSize(ht, scan);
371    LOG.info("found " + resultsSize + " results");
372    List<Cell> results1 = getScanResult(Bytes.toBytes(10), Bytes.toBytes(40), ht);
373    List<Cell> results2 = getScanResult(Bytes.toBytes(60), Bytes.toBytes(""), ht);
374
375    assertEquals(results1.size() + results2.size(), resultsSize);
376
377    ht.close();
378  }
379
380  @Test
381  public void testMultiRowRangeFilterWithExclusive() throws IOException {
382    tableName = TableName.valueOf(name.getMethodName());
383    TEST_UTIL.getConfiguration().setInt(HConstants.HBASE_CLIENT_SCANNER_TIMEOUT_PERIOD, 6000000);
384    Table ht = TEST_UTIL.createTable(tableName, family, Integer.MAX_VALUE);
385    ht.setReadRpcTimeout(600000);
386    ht.setOperationTimeout(6000000);
387    generateRows(numRows, ht, family, qf, value);
388
389    Scan scan = new Scan();
390    scan.setMaxVersions();
391
392    List<RowRange> ranges = new ArrayList<>();
393    ranges.add(new RowRange(Bytes.toBytes(10), true, Bytes.toBytes(20), false));
394    ranges.add(new RowRange(Bytes.toBytes(20), false, Bytes.toBytes(40), false));
395    ranges.add(new RowRange(Bytes.toBytes(65), true, Bytes.toBytes(75), false));
396
397    MultiRowRangeFilter filter = new MultiRowRangeFilter(ranges);
398    scan.setFilter(filter);
399    int resultsSize = getResultsSize(ht, scan);
400    LOG.info("found " + resultsSize + " results");
401    List<Cell> results1 = getScanResult(Bytes.toBytes(10), Bytes.toBytes(40), ht);
402    List<Cell> results2 = getScanResult(Bytes.toBytes(65), Bytes.toBytes(75), ht);
403
404    assertEquals((results1.size() - 1) + results2.size(), resultsSize);
405
406    ht.close();
407  }
408
409  @Test
410  public void testMultiRowRangeWithFilterListAndOperator() throws IOException {
411    tableName = TableName.valueOf(name.getMethodName());
412    Table ht = TEST_UTIL.createTable(tableName, family, Integer.MAX_VALUE);
413    generateRows(numRows, ht, family, qf, value);
414
415    Scan scan = new Scan();
416    scan.setMaxVersions();
417
418    List<RowRange> ranges1 = new ArrayList<>();
419    ranges1.add(new RowRange(Bytes.toBytes(10), true, Bytes.toBytes(20), false));
420    ranges1.add(new RowRange(Bytes.toBytes(30), true, Bytes.toBytes(40), false));
421    ranges1.add(new RowRange(Bytes.toBytes(60), true, Bytes.toBytes(70), false));
422
423    MultiRowRangeFilter filter1 = new MultiRowRangeFilter(ranges1);
424
425    List<RowRange> ranges2 = new ArrayList<>();
426    ranges2.add(new RowRange(Bytes.toBytes(20), true, Bytes.toBytes(40), false));
427    ranges2.add(new RowRange(Bytes.toBytes(80), true, Bytes.toBytes(90), false));
428
429    MultiRowRangeFilter filter2 = new MultiRowRangeFilter(ranges2);
430
431    FilterList filterList = new FilterList(FilterList.Operator.MUST_PASS_ALL);
432    filterList.addFilter(filter1);
433    filterList.addFilter(filter2);
434    scan.setFilter(filterList);
435    int resultsSize = getResultsSize(ht, scan);
436    LOG.info("found " + resultsSize + " results");
437    List<Cell> results1 = getScanResult(Bytes.toBytes(30), Bytes.toBytes(40), ht);
438
439    assertEquals(results1.size(), resultsSize);
440
441    ht.close();
442  }
443
444  @Test
445  public void testMultiRowRangeWithFilterListOrOperator() throws IOException {
446    tableName = TableName.valueOf(name.getMethodName());
447    Table ht = TEST_UTIL.createTable(tableName, family, Integer.MAX_VALUE);
448    generateRows(numRows, ht, family, qf, value);
449
450    Scan scan = new Scan();
451    scan.setMaxVersions();
452
453    List<RowRange> ranges1 = new ArrayList<>();
454    ranges1.add(new RowRange(Bytes.toBytes(30), true, Bytes.toBytes(40), false));
455    ranges1.add(new RowRange(Bytes.toBytes(10), true, Bytes.toBytes(20), false));
456    ranges1.add(new RowRange(Bytes.toBytes(60), true, Bytes.toBytes(70), false));
457
458    MultiRowRangeFilter filter1 = new MultiRowRangeFilter(ranges1);
459
460    List<RowRange> ranges2 = new ArrayList<>();
461    ranges2.add(new RowRange(Bytes.toBytes(20), true, Bytes.toBytes(40), false));
462    ranges2.add(new RowRange(Bytes.toBytes(80), true, Bytes.toBytes(90), false));
463
464    MultiRowRangeFilter filter2 = new MultiRowRangeFilter(ranges2);
465
466    FilterList filterList = new FilterList(FilterList.Operator.MUST_PASS_ONE);
467    filterList.addFilter(filter1);
468    filterList.addFilter(filter2);
469    scan.setFilter(filterList);
470    int resultsSize = getResultsSize(ht, scan);
471    LOG.info("found " + resultsSize + " results");
472    List<Cell> results1 = getScanResult(Bytes.toBytes(10), Bytes.toBytes(40), ht);
473    List<Cell> results2 = getScanResult(Bytes.toBytes(60), Bytes.toBytes(70), ht);
474    List<Cell> results3 = getScanResult(Bytes.toBytes(80), Bytes.toBytes(90), ht);
475
476    assertEquals(results1.size() + results2.size() + results3.size(),resultsSize);
477
478    ht.close();
479  }
480
481  @Test
482  public void testOneRowRange() throws IOException {
483    tableName = TableName.valueOf(name.getMethodName());
484    Table ht = TEST_UTIL.createTable(tableName, family, Integer.MAX_VALUE);
485    generateRows(numRows, ht, family, qf, value);
486    ArrayList<MultiRowRangeFilter.RowRange> rowRangesList = new ArrayList<>();
487    rowRangesList
488        .add(new MultiRowRangeFilter.RowRange(Bytes.toBytes(50), true, Bytes.toBytes(50), true));
489    Scan scan = new Scan();
490    scan.setFilter(new MultiRowRangeFilter(rowRangesList));
491    int resultsSize = getResultsSize(ht, scan);
492    assertEquals(1, resultsSize);
493    rowRangesList.clear();
494    rowRangesList
495        .add(new MultiRowRangeFilter.RowRange(Bytes.toBytes(50), true, Bytes.toBytes(51), false));
496    scan = new Scan();
497    scan.setFilter(new MultiRowRangeFilter(rowRangesList));
498    resultsSize = getResultsSize(ht, scan);
499    assertEquals(1, resultsSize);
500    rowRangesList.clear();
501    rowRangesList
502        .add(new MultiRowRangeFilter.RowRange(Bytes.toBytes(50), true, Bytes.toBytes(51), true));
503    scan = new Scan();
504    scan.setFilter(new MultiRowRangeFilter(rowRangesList));
505    resultsSize = getResultsSize(ht, scan);
506    assertEquals(2, resultsSize);
507    ht.close();
508  }
509
510  @Test
511  public void testReverseMultiRowRangeFilterWithinTable() throws IOException {
512    tableName = TableName.valueOf(name.getMethodName());
513    Table ht = TEST_UTIL.createTable(tableName, family);
514    generateRows(numRows, ht, family, qf, value);
515
516    Scan scan = new Scan();
517    scan.setReversed(true);
518    List<RowRange> ranges = Arrays.asList(
519        new RowRange(Bytes.toBytes(20), true, Bytes.toBytes(30), true),
520        new RowRange(Bytes.toBytes(50), true, Bytes.toBytes(60), true)
521    );
522    MultiRowRangeFilter filter = new MultiRowRangeFilter(ranges);
523    scan.setFilter(filter);
524
525    List<Integer> expectedResults = new ArrayList<>();
526    for (int i = 60; i >= 50; i--) {
527      expectedResults.add(i);
528    }
529    for (int i = 30; i >= 20; i--) {
530      expectedResults.add(i);
531    }
532
533    List<Cell> results = getResults(ht, scan);
534    List<Integer> actualResults = new ArrayList<>();
535    StringBuilder sb = new StringBuilder();
536    for (Cell result : results) {
537      int observedValue = Bytes.toInt(
538          result.getRowArray(), result.getRowOffset(), result.getRowLength());
539      actualResults.add(observedValue);
540      if (sb.length() > 0) {
541        sb.append(", ");
542      }
543      sb.append(observedValue);
544    }
545    assertEquals("Saw results: " + sb.toString(), 22, results.size());
546  }
547
548  @Test
549  public void testReverseMultiRowRangeFilterIncludingMaxRow() throws IOException {
550    tableName = TableName.valueOf(name.getMethodName());
551    Table ht = TEST_UTIL.createTable(tableName, family);
552    for (String rowkey : Arrays.asList("a", "b", "c", "d", "e", "f", "g", "h")) {
553      byte[] row = Bytes.toBytes(rowkey);
554      Put p = new Put(row);
555      p.addColumn(family, qf, value);
556      ht.put(p);
557    }
558    TEST_UTIL.flush();
559
560    Scan scan = new Scan();
561    scan.setReversed(true);
562    List<RowRange> ranges = Arrays.asList(
563        new RowRange(Bytes.toBytes("b"), true, Bytes.toBytes("c"), true),
564        new RowRange(Bytes.toBytes("f"), true, Bytes.toBytes("h"), true)
565    );
566    MultiRowRangeFilter filter = new MultiRowRangeFilter(ranges);
567    scan.setFilter(filter);
568
569    List<String> expected = Arrays.asList("h", "g", "f", "c", "b");
570    List<String> actual = new ArrayList<>();
571    for (Cell cell : getResults(ht, scan)) {
572      actual.add(Bytes.toString(cell.getRowArray(), cell.getRowOffset(), cell.getRowLength()));
573    }
574
575    assertEquals(expected, actual);
576  }
577
578  @Test
579  public void testReverseMultiRowRangeFilterIncludingMinRow() throws IOException {
580    tableName = TableName.valueOf(name.getMethodName());
581    Table ht = TEST_UTIL.createTable(tableName, family);
582    for (String rowkey : Arrays.asList("a", "b", "c", "d", "e", "f", "g", "h")) {
583      byte[] row = Bytes.toBytes(rowkey);
584      Put p = new Put(row);
585      p.addColumn(family, qf, value);
586      ht.put(p);
587    }
588    TEST_UTIL.flush();
589
590    Scan scan = new Scan();
591    scan.setReversed(true);
592    List<RowRange> ranges = Arrays.asList(
593        new RowRange(Bytes.toBytes("a"), true, Bytes.toBytes("c"), true),
594        new RowRange(Bytes.toBytes("f"), true, Bytes.toBytes("g"), true)
595    );
596    MultiRowRangeFilter filter = new MultiRowRangeFilter(ranges);
597    scan.setFilter(filter);
598
599    List<String> expected = Arrays.asList("g", "f", "c", "b", "a");
600    List<String> actual = new ArrayList<>();
601    for (Cell cell : getResults(ht, scan)) {
602      actual.add(Bytes.toString(cell.getRowArray(), cell.getRowOffset(), cell.getRowLength()));
603    }
604
605    assertEquals(expected, actual);
606  }
607
608  @Test
609  public void testReverseMultiRowRangeFilterIncludingMinAndMaxRow() throws IOException {
610    tableName = TableName.valueOf(name.getMethodName());
611    Table ht = TEST_UTIL.createTable(tableName, family);
612    for (String rowkey : Arrays.asList("a", "b", "c", "d", "e", "f", "g", "h")) {
613      byte[] row = Bytes.toBytes(rowkey);
614      Put p = new Put(row);
615      p.addColumn(family, qf, value);
616      ht.put(p);
617    }
618    TEST_UTIL.flush();
619
620    Scan scan = new Scan();
621    scan.setReversed(true);
622    List<RowRange> ranges = Arrays.asList(
623        new RowRange(Bytes.toBytes("a"), true, Bytes.toBytes("c"), true),
624        new RowRange(Bytes.toBytes("f"), true, Bytes.toBytes("h"), true)
625    );
626    MultiRowRangeFilter filter = new MultiRowRangeFilter(ranges);
627    scan.setFilter(filter);
628
629    List<String> expected = Arrays.asList("h", "g", "f", "c", "b", "a");
630    List<String> actual = new ArrayList<>();
631    for (Cell cell : getResults(ht, scan)) {
632      actual.add(Bytes.toString(cell.getRowArray(), cell.getRowOffset(), cell.getRowLength()));
633    }
634
635    assertEquals(expected, actual);
636  }
637
638  private void generateRows(int numberOfRows, Table ht, byte[] family, byte[] qf, byte[] value)
639      throws IOException {
640    for (int i = 0; i < numberOfRows; i++) {
641      byte[] row = Bytes.toBytes(i);
642      Put p = new Put(row);
643      p.addColumn(family, qf, value);
644      ht.put(p);
645    }
646    TEST_UTIL.flush();
647  }
648
649  private List<Cell> getScanResult(byte[] startRow, byte[] stopRow, Table ht) throws IOException {
650    Scan scan = new Scan();
651    scan.setMaxVersions();
652    if(!Bytes.toString(startRow).isEmpty()) {
653      scan.setStartRow(startRow);
654    }
655    if(!Bytes.toString(stopRow).isEmpty()) {
656      scan.setStopRow(stopRow);
657    }
658    ResultScanner scanner = ht.getScanner(scan);
659    List<Cell> kvList = new ArrayList<>();
660    Result r;
661    while ((r = scanner.next()) != null) {
662      for (Cell kv : r.listCells()) {
663        kvList.add(kv);
664      }
665    }
666    scanner.close();
667    return kvList;
668  }
669
670  private List<Cell> getResults(Table ht, Scan scan) throws IOException {
671    ResultScanner scanner = ht.getScanner(scan);
672    List<Cell> results = new ArrayList<>();
673    Result r;
674    while ((r = scanner.next()) != null) {
675      for (Cell kv : r.listCells()) {
676        results.add(kv);
677      }
678    }
679    scanner.close();
680    return results;
681  }
682
683  private int getResultsSize(Table ht, Scan scan) throws IOException {
684    return getResults(ht, scan).size();
685  }
686}