View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  package org.apache.hadoop.hbase.filter;
19  
20  import static org.junit.Assert.assertEquals;
21  
22  import java.io.IOException;
23  import java.util.ArrayList;
24  import java.util.Arrays;
25  import java.util.List;
26  
27  import org.apache.commons.logging.Log;
28  import org.apache.commons.logging.LogFactory;
29  import org.apache.hadoop.hbase.Cell;
30  import org.apache.hadoop.hbase.HBaseTestingUtility;
31  import org.apache.hadoop.hbase.HConstants;
32  import org.apache.hadoop.hbase.client.HTable;
33  import org.apache.hadoop.hbase.client.Put;
34  import org.apache.hadoop.hbase.client.Result;
35  import org.apache.hadoop.hbase.client.ResultScanner;
36  import org.apache.hadoop.hbase.client.Scan;
37  import org.apache.hadoop.hbase.filter.MultiRowRangeFilter.RowRange;
38  import org.apache.hadoop.hbase.testclassification.MediumTests;
39  import org.apache.hadoop.hbase.util.Bytes;
40  import org.junit.AfterClass;
41  import org.junit.Assert;
42  import org.junit.BeforeClass;
43  import org.junit.Test;
44  import org.junit.experimental.categories.Category;
45  
46  @Category(MediumTests.class)
47  public class TestMultiRowRangeFilter {
48  
49    private final static HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility();
50    private static final Log LOG = LogFactory.getLog(TestMultiRowRangeFilter.class);
51    private byte[] family = Bytes.toBytes("family");
52    private byte[] qf = Bytes.toBytes("qf");
53    private byte[] value = Bytes.toBytes("val");
54    private byte[] tableName;
55    private int numRows = 100;
56  
57    /**
58     * @throws Exception
59     */
60    @BeforeClass
61    public static void setUpBeforeClass() throws Exception {
62      TEST_UTIL.startMiniCluster();
63    }
64  
65    /**
66     * @throws Exception
67     */
68    @AfterClass
69    public static void tearDownAfterClass() throws Exception {
70      TEST_UTIL.shutdownMiniCluster();
71    }
72  
73    @Test
74    public void testRowKeyPrefixWithEmptyPrefix() throws IOException {
75      byte[] prefix = {};
76      byte[][] rowKeyPrefixes = new byte[1][];
77      rowKeyPrefixes[0] = prefix;
78      MultiRowRangeFilter filter = new MultiRowRangeFilter(rowKeyPrefixes);
79      List<RowRange> actualRanges = filter.getRowRanges();
80      List<RowRange> expectedRanges = new ArrayList<>();
81      expectedRanges.add(
82        new RowRange(HConstants.EMPTY_START_ROW, true, HConstants.EMPTY_END_ROW, false)
83      );
84      assertRangesEqual(expectedRanges, actualRanges);
85    }
86  
87    @Test
88    public void testRowKeyPrefixWithLastIncrementablePrefix() throws IOException {
89      byte[] prefix = {(byte) 0x12, (byte) 0x23, (byte) 0xFF, (byte) 0xFE};
90      byte[][] rowKeyPrefixes = new byte[1][];
91      rowKeyPrefixes[0] = prefix;
92      MultiRowRangeFilter filter = new MultiRowRangeFilter(rowKeyPrefixes);
93      List<RowRange> actualRanges = filter.getRowRanges();
94      List<RowRange> expectedRanges = new ArrayList<>();
95      final byte[] expectedStop = {(byte) 0x12, (byte) 0x23, (byte) 0xFF, (byte) 0xFF};
96      expectedRanges.add(new RowRange(prefix, true, expectedStop , false));
97      assertRangesEqual(expectedRanges, actualRanges);
98    }
99  
100   @Test
101   public void testRowKeyPrefixWithoutLastIncrementablePrefix() throws IOException {
102     byte[] prefix = {(byte) 0x12, (byte) 0x23, (byte) 0xFF, (byte) 0xFF};
103     byte[][] rowKeyPrefixes = new byte[1][];
104     rowKeyPrefixes[0] = prefix;
105     MultiRowRangeFilter filter = new MultiRowRangeFilter(rowKeyPrefixes);
106     List<RowRange> actualRanges = filter.getRowRanges();
107     List<RowRange> expectedRanges = new ArrayList<>();
108     final byte[] expectedStop = {(byte) 0x12, (byte) 0x24};
109     expectedRanges.add(new RowRange(prefix, true, expectedStop , false));
110     assertRangesEqual(expectedRanges, actualRanges);
111   }
112 
113   @Test
114   public void testRowKeyPrefixWithMergablePrefix() throws IOException {
115     byte[] prefix1 = {(byte) 0x12, (byte) 0x23, (byte) 0xFF, (byte) 0xFE};
116     byte[] prefix2 = {(byte) 0x12, (byte) 0x23, (byte) 0xFF, (byte) 0xFF};
117     byte[][] rowKeyPrefixes = new byte[2][];
118     rowKeyPrefixes[0] = prefix1;
119     rowKeyPrefixes[1] = prefix2;
120     MultiRowRangeFilter filter = new MultiRowRangeFilter(rowKeyPrefixes);
121     List<RowRange> actualRanges = filter.getRowRanges();
122     List<RowRange> expectedRanges = new ArrayList<>();
123     final byte[] expectedStop = {(byte) 0x12, (byte) 0x24};
124     expectedRanges.add(new RowRange(prefix1, true, expectedStop , false));
125     assertRangesEqual(expectedRanges, actualRanges);
126   }
127 
128   @Test
129   public void testRanges() throws IOException {
130     byte[] key1Start = new byte[] {-3};
131     byte[] key1End  = new byte[] {-2};
132 
133     byte[] key2Start = new byte[] {5};
134     byte[] key2End  = new byte[] {6};
135 
136     byte[] badKey = new byte[] {-10};
137 
138     MultiRowRangeFilter filter = new MultiRowRangeFilter(Arrays.asList(
139       new MultiRowRangeFilter.RowRange(key1Start, true, key1End, false),
140       new MultiRowRangeFilter.RowRange(key2Start, true, key2End, false)
141         ));
142     filter.filterRowKey(badKey, 0, 1);
143     assertEquals(Filter.ReturnCode.SEEK_NEXT_USING_HINT, filter.filterKeyValue(null));
144   }
145 
146   @Test
147   public void testOutOfOrderScannerNextException() throws Exception {
148     MultiRowRangeFilter filter = new MultiRowRangeFilter(Arrays.asList(
149             new MultiRowRangeFilter.RowRange(Bytes.toBytes("b"), true, Bytes.toBytes("c"), true),
150             new MultiRowRangeFilter.RowRange(Bytes.toBytes("d"), true, Bytes.toBytes("e"), true)
151     ));
152     filter.filterRowKey(Bytes.toBytes("a"), 0, 1);
153     assertEquals(Filter.ReturnCode.SEEK_NEXT_USING_HINT, filter.filterKeyValue(null));
154     filter.filterRowKey(Bytes.toBytes("b"), 0, 1);
155     assertEquals(Filter.ReturnCode.INCLUDE, filter.filterKeyValue(null));
156     filter.filterRowKey(Bytes.toBytes("c"), 0, 1);
157     assertEquals(Filter.ReturnCode.INCLUDE, filter.filterKeyValue(null));
158     filter.filterRowKey(Bytes.toBytes("d"), 0, 1);
159     assertEquals(Filter.ReturnCode.INCLUDE, filter.filterKeyValue(null));
160     filter.filterRowKey(Bytes.toBytes("e"), 0, 1);
161     assertEquals(Filter.ReturnCode.INCLUDE, filter.filterKeyValue(null));
162   }
163 
164   @Test
165   public void testMergeAndSortWithEmptyStartRow() throws IOException {
166     List<RowRange> ranges = new ArrayList<RowRange>();
167     ranges.add(new RowRange(Bytes.toBytes(""), true, Bytes.toBytes(20), false));
168     ranges.add(new RowRange(Bytes.toBytes(15), true, Bytes.toBytes(40), false));
169     List<RowRange> actualRanges = MultiRowRangeFilter.sortAndMerge(ranges);
170     List<RowRange> expectedRanges = new ArrayList<RowRange>();
171     expectedRanges.add(new RowRange(Bytes.toBytes(""), true, Bytes.toBytes(40), false));
172     assertRangesEqual(expectedRanges, actualRanges);
173   }
174 
175   @Test
176   public void testMergeAndSortWithEmptyStopRow() throws IOException {
177     List<RowRange> ranges = new ArrayList<RowRange>();
178     ranges.add(new RowRange(Bytes.toBytes(10), true, Bytes.toBytes(20), false));
179     ranges.add(new RowRange(Bytes.toBytes(15), true, Bytes.toBytes(""), false));
180     ranges.add(new RowRange(Bytes.toBytes(30), true, Bytes.toBytes(70), false));
181     List<RowRange> actualRanges = MultiRowRangeFilter.sortAndMerge(ranges);
182     List<RowRange> expectedRanges = new ArrayList<RowRange>();
183     expectedRanges.add(new RowRange(Bytes.toBytes(10), true, Bytes.toBytes(""), false));
184     assertRangesEqual(expectedRanges, actualRanges);
185   }
186 
187   @Test
188   public void testMergeAndSortWithEmptyStartRowAndStopRow() throws IOException {
189     List<RowRange> ranges = new ArrayList<RowRange>();
190     ranges.add(new RowRange(Bytes.toBytes(10), true, Bytes.toBytes(20), false));
191     ranges.add(new RowRange(Bytes.toBytes(""), true, Bytes.toBytes(""), false));
192     ranges.add(new RowRange(Bytes.toBytes(30), true, Bytes.toBytes(70), false));
193     List<RowRange> actualRanges = MultiRowRangeFilter.sortAndMerge(ranges);
194     List<RowRange> expectedRanges = new ArrayList<RowRange>();
195     expectedRanges.add(new RowRange(Bytes.toBytes(""), true, Bytes.toBytes(""), false));
196     assertRangesEqual(expectedRanges, actualRanges);
197   }
198 
199   @Test(expected=IllegalArgumentException.class)
200   public void testMultiRowRangeWithoutRange() throws IOException {
201     List<RowRange> ranges = new ArrayList<RowRange>();
202     new MultiRowRangeFilter(ranges);
203   }
204 
205   @Test(expected=IllegalArgumentException.class)
206   public void testMultiRowRangeWithInvalidRange() throws IOException {
207     List<RowRange> ranges = new ArrayList<RowRange>();
208     ranges.add(new RowRange(Bytes.toBytes(10), true, Bytes.toBytes(20), false));
209     // the start row larger than the stop row
210     ranges.add(new RowRange(Bytes.toBytes(80), true, Bytes.toBytes(20), false));
211     ranges.add(new RowRange(Bytes.toBytes(30), true, Bytes.toBytes(70), false));
212     new MultiRowRangeFilter(ranges);
213   }
214 
215   @Test
216   public void testMergeAndSortWithoutOverlap() throws IOException {
217     List<RowRange> ranges = new ArrayList<RowRange>();
218     ranges.add(new RowRange(Bytes.toBytes(10), true, Bytes.toBytes(20), false));
219     ranges.add(new RowRange(Bytes.toBytes(30), true, Bytes.toBytes(40), false));
220     ranges.add(new RowRange(Bytes.toBytes(60), true, Bytes.toBytes(70), false));
221     List<RowRange> actualRanges = MultiRowRangeFilter.sortAndMerge(ranges);
222     List<RowRange> expectedRanges = new ArrayList<RowRange>();
223     expectedRanges.add(new RowRange(Bytes.toBytes(10), true, Bytes.toBytes(20), false));
224     expectedRanges.add(new RowRange(Bytes.toBytes(30), true, Bytes.toBytes(40), false));
225     expectedRanges.add(new RowRange(Bytes.toBytes(60), true, Bytes.toBytes(70), false));
226     assertRangesEqual(expectedRanges, actualRanges);
227   }
228 
229   @Test
230   public void testMergeAndSortWithOverlap() throws IOException {
231     List<RowRange> ranges = new ArrayList<RowRange>();
232     ranges.add(new RowRange(Bytes.toBytes(10), true, Bytes.toBytes(20), false));
233     ranges.add(new RowRange(Bytes.toBytes(15), true, Bytes.toBytes(40), false));
234     ranges.add(new RowRange(Bytes.toBytes(20), true, Bytes.toBytes(30), false));
235     ranges.add(new RowRange(Bytes.toBytes(30), true, Bytes.toBytes(50), false));
236     ranges.add(new RowRange(Bytes.toBytes(30), true, Bytes.toBytes(70), false));
237     ranges.add(new RowRange(Bytes.toBytes(90), true, Bytes.toBytes(100), false));
238     ranges.add(new RowRange(Bytes.toBytes(95), true, Bytes.toBytes(100), false));
239     List<RowRange> actualRanges = MultiRowRangeFilter.sortAndMerge(ranges);
240     List<RowRange> expectedRanges = new ArrayList<RowRange>();
241     expectedRanges.add(new RowRange(Bytes.toBytes(10), true, Bytes.toBytes(70), false));
242     expectedRanges.add(new RowRange(Bytes.toBytes(90), true, Bytes.toBytes(100), false));
243     assertRangesEqual(expectedRanges, actualRanges);
244   }
245 
246   @Test
247   public void testMergeAndSortWithStartRowInclusive() throws IOException {
248     List<RowRange> ranges = new ArrayList<RowRange>();
249     ranges.add(new RowRange(Bytes.toBytes(10), true, Bytes.toBytes(20), false));
250     ranges.add(new RowRange(Bytes.toBytes(20), true, Bytes.toBytes(""), false));
251     List<RowRange> actualRanges = MultiRowRangeFilter.sortAndMerge(ranges);
252     List<RowRange> expectedRanges = new ArrayList<RowRange>();
253     expectedRanges.add(new RowRange(Bytes.toBytes(10), true, Bytes.toBytes(""), false));
254     assertRangesEqual(expectedRanges, actualRanges);
255   }
256 
257   @Test
258   public void testMergeAndSortWithRowExclusive() throws IOException {
259     List<RowRange> ranges = new ArrayList<RowRange>();
260     ranges.add(new RowRange(Bytes.toBytes(10), true, Bytes.toBytes(20), false));
261     ranges.add(new RowRange(Bytes.toBytes(20), false, Bytes.toBytes(""), false));
262     List<RowRange> actualRanges = MultiRowRangeFilter.sortAndMerge(ranges);
263     List<RowRange> expectedRanges = new ArrayList<RowRange>();
264     expectedRanges.add(new RowRange(Bytes.toBytes(10), true, Bytes.toBytes(20), false));
265     expectedRanges.add(new RowRange(Bytes.toBytes(20), false, Bytes.toBytes(""), false));
266     assertRangesEqual(expectedRanges, actualRanges);
267   }
268 
269   @Test
270   public void testMergeAndSortWithRowInclusive() throws IOException {
271     List<RowRange> ranges = new ArrayList<RowRange>();
272     ranges.add(new RowRange(Bytes.toBytes(10), true, Bytes.toBytes(20), true));
273     ranges.add(new RowRange(Bytes.toBytes(20), false, Bytes.toBytes(""), false));
274     List<RowRange> actualRanges = MultiRowRangeFilter.sortAndMerge(ranges);
275     List<RowRange> expectedRanges = new ArrayList<RowRange>();
276     expectedRanges.add(new RowRange(Bytes.toBytes(10), true, Bytes.toBytes(""), false));
277     assertRangesEqual(expectedRanges, actualRanges);
278   }
279 
280   public void assertRangesEqual(List<RowRange> expected, List<RowRange> actual) {
281     assertEquals(expected.size(), actual.size());
282     for(int i = 0; i < expected.size(); i++) {
283       Assert.assertTrue(Bytes.equals(expected.get(i).getStartRow(), actual.get(i).getStartRow()));
284       Assert.assertTrue(expected.get(i).isStartRowInclusive() ==
285           actual.get(i).isStartRowInclusive());
286       Assert.assertTrue(Bytes.equals(expected.get(i).getStopRow(), actual.get(i).getStopRow()));
287       Assert.assertTrue(expected.get(i).isStopRowInclusive() ==
288           actual.get(i).isStopRowInclusive());
289     }
290   }
291 
292   @Test
293   public void testMultiRowRangeFilterWithRangeOverlap() throws IOException {
294     tableName = Bytes.toBytes("testMultiRowRangeFilterWithRangeOverlap");
295     HTable ht = TEST_UTIL.createTable(tableName, family, Integer.MAX_VALUE);
296     generateRows(numRows, ht, family, qf, value);
297 
298     Scan scan = new Scan();
299     scan.setMaxVersions();
300 
301     List<RowRange> ranges = new ArrayList<RowRange>();
302     ranges.add(new RowRange(Bytes.toBytes(10), true, Bytes.toBytes(20), false));
303     ranges.add(new RowRange(Bytes.toBytes(15), true, Bytes.toBytes(40), false));
304     ranges.add(new RowRange(Bytes.toBytes(65), true, Bytes.toBytes(75), false));
305     ranges.add(new RowRange(Bytes.toBytes(60), true, null, false));
306     ranges.add(new RowRange(Bytes.toBytes(60), true, Bytes.toBytes(80), false));
307 
308     MultiRowRangeFilter filter = new MultiRowRangeFilter(ranges);
309     scan.setFilter(filter);
310     int resultsSize = getResultsSize(ht, scan);
311     LOG.info("found " + resultsSize + " results");
312     List<Cell> results1 = getScanResult(Bytes.toBytes(10), Bytes.toBytes(40), ht);
313     List<Cell> results2 = getScanResult(Bytes.toBytes(60), Bytes.toBytes(""), ht);
314 
315     assertEquals(results1.size() + results2.size(), resultsSize);
316 
317     ht.close();
318   }
319 
320   @Test
321   public void testMultiRowRangeFilterWithoutRangeOverlap() throws IOException {
322     tableName = Bytes.toBytes("testMultiRowRangeFilterWithoutRangeOverlap");
323     HTable ht = TEST_UTIL.createTable(tableName, family, Integer.MAX_VALUE);
324     generateRows(numRows, ht, family, qf, value);
325 
326     Scan scan = new Scan();
327     scan.setMaxVersions();
328 
329     List<RowRange> ranges = new ArrayList<RowRange>();
330     ranges.add(new RowRange(Bytes.toBytes(30), true, Bytes.toBytes(40), false));
331     ranges.add(new RowRange(Bytes.toBytes(10), true, Bytes.toBytes(20), false));
332     ranges.add(new RowRange(Bytes.toBytes(60), true, Bytes.toBytes(70), false));
333 
334     MultiRowRangeFilter filter = new MultiRowRangeFilter(ranges);
335     scan.setFilter(filter);
336     int resultsSize = getResultsSize(ht, scan);
337     LOG.info("found " + resultsSize + " results");
338     List<Cell> results1 = getScanResult(Bytes.toBytes(10), Bytes.toBytes(20), ht);
339     List<Cell> results2 = getScanResult(Bytes.toBytes(30), Bytes.toBytes(40), ht);
340     List<Cell> results3 = getScanResult(Bytes.toBytes(60), Bytes.toBytes(70), ht);
341 
342     assertEquals(results1.size() + results2.size() + results3.size(), resultsSize);
343 
344     ht.close();
345   }
346 
347   @Test
348   public void testMultiRowRangeFilterWithEmptyStartRow() throws IOException {
349     tableName = Bytes.toBytes("testMultiRowRangeFilterWithEmptyStartRow");
350     HTable ht = TEST_UTIL.createTable(tableName, family, Integer.MAX_VALUE);
351     generateRows(numRows, ht, family, qf, value);
352     Scan scan = new Scan();
353     scan.setMaxVersions();
354 
355     List<RowRange> ranges = new ArrayList<RowRange>();
356     ranges.add(new RowRange(Bytes.toBytes(""), true, Bytes.toBytes(10), false));
357     ranges.add(new RowRange(Bytes.toBytes(30), true, Bytes.toBytes(40), false));
358 
359     MultiRowRangeFilter filter = new MultiRowRangeFilter(ranges);
360     scan.setFilter(filter);
361     int resultsSize = getResultsSize(ht, scan);
362     List<Cell> results1 = getScanResult(Bytes.toBytes(""), Bytes.toBytes(10), ht);
363     List<Cell> results2 = getScanResult(Bytes.toBytes(30), Bytes.toBytes(40), ht);
364     assertEquals(results1.size() + results2.size(), resultsSize);
365 
366     ht.close();
367   }
368 
369   @Test
370   public void testMultiRowRangeFilterWithEmptyStopRow() throws IOException {
371     tableName = Bytes.toBytes("testMultiRowRangeFilterWithEmptyStopRow");
372     HTable ht = TEST_UTIL.createTable(tableName, family, Integer.MAX_VALUE);
373     generateRows(numRows, ht, family, qf, value);
374     Scan scan = new Scan();
375     scan.setMaxVersions();
376 
377     List<RowRange> ranges = new ArrayList<RowRange>();
378     ranges.add(new RowRange(Bytes.toBytes(10), true, Bytes.toBytes(""), false));
379     ranges.add(new RowRange(Bytes.toBytes(30), true, Bytes.toBytes(40), false));
380 
381     MultiRowRangeFilter filter = new MultiRowRangeFilter(ranges);
382     scan.setFilter(filter);
383     int resultsSize = getResultsSize(ht, scan);
384     List<Cell> results1 = getScanResult(Bytes.toBytes(10), Bytes.toBytes(""), ht);
385     assertEquals(results1.size(), resultsSize);
386 
387     ht.close();
388   }
389 
390   @Test
391   public void testMultiRowRangeFilterWithInclusive() throws IOException {
392     tableName = Bytes.toBytes("testMultiRowRangeFilterWithInclusive");
393     HTable ht = TEST_UTIL.createTable(tableName, family, Integer.MAX_VALUE);
394     generateRows(numRows, ht, family, qf, value);
395 
396     Scan scan = new Scan();
397     scan.setMaxVersions();
398 
399     List<RowRange> ranges = new ArrayList<RowRange>();
400     ranges.add(new RowRange(Bytes.toBytes(10), true, Bytes.toBytes(20), false));
401     ranges.add(new RowRange(Bytes.toBytes(20), true, Bytes.toBytes(40), false));
402     ranges.add(new RowRange(Bytes.toBytes(65), true, Bytes.toBytes(75), false));
403     ranges.add(new RowRange(Bytes.toBytes(60), true, null, false));
404     ranges.add(new RowRange(Bytes.toBytes(60), true, Bytes.toBytes(80), false));
405 
406     MultiRowRangeFilter filter = new MultiRowRangeFilter(ranges);
407     scan.setFilter(filter);
408     int resultsSize = getResultsSize(ht, scan);
409     LOG.info("found " + resultsSize + " results");
410     List<Cell> results1 = getScanResult(Bytes.toBytes(10), Bytes.toBytes(40), ht);
411     List<Cell> results2 = getScanResult(Bytes.toBytes(60), Bytes.toBytes(""), ht);
412 
413     assertEquals(results1.size() + results2.size(), resultsSize);
414 
415     ht.close();
416   }
417 
418   @Test
419   public void testMultiRowRangeFilterWithExclusive() throws IOException {
420     tableName = Bytes.toBytes("testMultiRowRangeFilterWithExclusive");
421     HTable ht = TEST_UTIL.createTable(tableName, family, Integer.MAX_VALUE);
422     generateRows(numRows, ht, family, qf, value);
423 
424     Scan scan = new Scan();
425     scan.setMaxVersions();
426 
427     List<RowRange> ranges = new ArrayList<RowRange>();
428     ranges.add(new RowRange(Bytes.toBytes(10), true, Bytes.toBytes(20), false));
429     ranges.add(new RowRange(Bytes.toBytes(20), false, Bytes.toBytes(40), false));
430     ranges.add(new RowRange(Bytes.toBytes(65), true, Bytes.toBytes(75), false));
431 
432     MultiRowRangeFilter filter = new MultiRowRangeFilter(ranges);
433     scan.setFilter(filter);
434     int resultsSize = getResultsSize(ht, scan);
435     LOG.info("found " + resultsSize + " results");
436     List<Cell> results1 = getScanResult(Bytes.toBytes(10), Bytes.toBytes(40), ht);
437     List<Cell> results2 = getScanResult(Bytes.toBytes(65), Bytes.toBytes(75), ht);
438 
439     assertEquals((results1.size() - 1) + results2.size(), resultsSize);
440 
441     ht.close();
442   }
443 
444   @Test
445   public void testMultiRowRangeWithFilterListAndOperator() throws IOException {
446     tableName = Bytes.toBytes("TestMultiRowRangeFilterWithFilterListAndOperator");
447     HTable 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<RowRange>();
454     ranges1.add(new RowRange(Bytes.toBytes(10), true, Bytes.toBytes(20), false));
455     ranges1.add(new RowRange(Bytes.toBytes(30), true, Bytes.toBytes(40), 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<RowRange>();
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_ALL);
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(30), Bytes.toBytes(40), ht);
473 
474     assertEquals(results1.size(), resultsSize);
475 
476     ht.close();
477   }
478 
479   @Test
480   public void testMultiRowRangeWithFilterListOrOperator() throws IOException {
481     tableName = Bytes.toBytes("TestMultiRowRangeFilterWithFilterListOrOperator");
482     HTable ht = TEST_UTIL.createTable(tableName, family, Integer.MAX_VALUE);
483     generateRows(numRows, ht, family, qf, value);
484 
485     Scan scan = new Scan();
486     scan.setMaxVersions();
487 
488     List<RowRange> ranges1 = new ArrayList<RowRange>();
489     ranges1.add(new RowRange(Bytes.toBytes(30), true, Bytes.toBytes(40), false));
490     ranges1.add(new RowRange(Bytes.toBytes(10), true, Bytes.toBytes(20), false));
491     ranges1.add(new RowRange(Bytes.toBytes(60), true, Bytes.toBytes(70), false));
492 
493     MultiRowRangeFilter filter1 = new MultiRowRangeFilter(ranges1);
494 
495     List<RowRange> ranges2 = new ArrayList<RowRange>();
496     ranges2.add(new RowRange(Bytes.toBytes(20), true, Bytes.toBytes(40), false));
497     ranges2.add(new RowRange(Bytes.toBytes(80), true, Bytes.toBytes(90), false));
498 
499     MultiRowRangeFilter filter2 = new MultiRowRangeFilter(ranges2);
500 
501     FilterList filterList = new FilterList(FilterList.Operator.MUST_PASS_ONE);
502     filterList.addFilter(filter1);
503     filterList.addFilter(filter2);
504     scan.setFilter(filterList);
505     int resultsSize = getResultsSize(ht, scan);
506     LOG.info("found " + resultsSize + " results");
507     List<Cell> results1 = getScanResult(Bytes.toBytes(10), Bytes.toBytes(40), ht);
508     List<Cell> results2 = getScanResult(Bytes.toBytes(60), Bytes.toBytes(70), ht);
509     List<Cell> results3 = getScanResult(Bytes.toBytes(80), Bytes.toBytes(90), ht);
510 
511     assertEquals(results1.size() + results2.size() + results3.size(),resultsSize);
512 
513     ht.close();
514   }
515 
516   @Test
517   public void testOneRowRange() throws IOException {
518     tableName = Bytes.toBytes("testOneRowRange");
519     HTable ht = TEST_UTIL.createTable(tableName, family, Integer.MAX_VALUE);
520     generateRows(numRows, ht, family, qf, value);
521     ArrayList<MultiRowRangeFilter.RowRange> rowRangesList = new ArrayList<>();
522     rowRangesList
523         .add(new MultiRowRangeFilter.RowRange(Bytes.toBytes(50), true, Bytes.toBytes(50), true));
524     Scan scan = new Scan();
525     scan.setFilter(new MultiRowRangeFilter(rowRangesList));
526     int resultsSize = getResultsSize(ht, scan);
527     assertEquals(1, resultsSize);
528     rowRangesList.clear();
529     rowRangesList
530         .add(new MultiRowRangeFilter.RowRange(Bytes.toBytes(50), true, Bytes.toBytes(51), false));
531     scan = new Scan();
532     scan.setFilter(new MultiRowRangeFilter(rowRangesList));
533     resultsSize = getResultsSize(ht, scan);
534     assertEquals(1, resultsSize);
535     rowRangesList.clear();
536     rowRangesList
537         .add(new MultiRowRangeFilter.RowRange(Bytes.toBytes(50), true, Bytes.toBytes(51), true));
538     scan = new Scan();
539     scan.setFilter(new MultiRowRangeFilter(rowRangesList));
540     resultsSize = getResultsSize(ht, scan);
541     assertEquals(2, resultsSize);
542     ht.close();
543   }
544 
545   @Test
546   public void testReverseMultiRowRangeFilterWithinTable() throws IOException {
547     tableName = Bytes.toBytes("testReverseMultiRowRangeFilterWithinTable");
548     HTable ht = TEST_UTIL.createTable(tableName, family);
549     generateRows(numRows, ht, family, qf, value);
550 
551     Scan scan = new Scan();
552     scan.setReversed(true);
553     List<RowRange> ranges = Arrays.asList(
554         new RowRange(Bytes.toBytes(20), true, Bytes.toBytes(30), true),
555         new RowRange(Bytes.toBytes(50), true, Bytes.toBytes(60), true)
556     );
557     MultiRowRangeFilter filter = new MultiRowRangeFilter(ranges);
558     scan.setFilter(filter);
559 
560     List<Integer> expectedResults = new ArrayList<>();
561     for (int i = 60; i >= 50; i--) {
562       expectedResults.add(i);
563     }
564     for (int i = 30; i >= 20; i--) {
565       expectedResults.add(i);
566     }
567 
568     List<Cell> results = getResults(ht, scan);
569     List<Integer> actualResults = new ArrayList<>();
570     StringBuilder sb = new StringBuilder();
571     for (Cell result : results) {
572       int observedValue = Bytes.toInt(
573           result.getRowArray(), result.getRowOffset(), result.getRowLength());
574       actualResults.add(observedValue);
575       if (sb.length() > 0) {
576         sb.append(", ");
577       }
578       sb.append(observedValue);
579     }
580     assertEquals("Saw results: " + sb.toString(), 22, results.size());
581   }
582 
583   @Test
584   public void testReverseMultiRowRangeFilterIncludingMaxRow() throws IOException {
585     tableName = Bytes.toBytes("testReverseMultiRowRangeFilterIncludingMaxRow");
586     HTable ht = TEST_UTIL.createTable(tableName, family);
587     for (String rowkey : Arrays.asList("a", "b", "c", "d", "e", "f", "g", "h")) {
588       byte[] row = Bytes.toBytes(rowkey);
589       Put p = new Put(row);
590       p.addColumn(family, qf, value);
591       ht.put(p);
592     }
593     TEST_UTIL.flush();
594 
595     Scan scan = new Scan();
596     scan.setReversed(true);
597     List<RowRange> ranges = Arrays.asList(
598         new RowRange(Bytes.toBytes("b"), true, Bytes.toBytes("c"), true),
599         new RowRange(Bytes.toBytes("f"), true, Bytes.toBytes("h"), true)
600     );
601     MultiRowRangeFilter filter = new MultiRowRangeFilter(ranges);
602     scan.setFilter(filter);
603 
604     List<String> expected = Arrays.asList("h", "g", "f", "c", "b");
605     List<String> actual = new ArrayList<>();
606     for (Cell cell : getResults(ht, scan)) {
607       actual.add(Bytes.toString(cell.getRowArray(), cell.getRowOffset(), cell.getRowLength()));
608     }
609 
610     assertEquals(expected, actual);
611   }
612 
613   @Test
614   public void testReverseMultiRowRangeFilterIncludingMinRow() throws IOException {
615     tableName = Bytes.toBytes("testReverseMultiRowRangeFilterIncludingMinRow");
616     HTable ht = TEST_UTIL.createTable(tableName, family);
617     for (String rowkey : Arrays.asList("a", "b", "c", "d", "e", "f", "g", "h")) {
618       byte[] row = Bytes.toBytes(rowkey);
619       Put p = new Put(row);
620       p.addColumn(family, qf, value);
621       ht.put(p);
622     }
623     TEST_UTIL.flush();
624 
625     Scan scan = new Scan();
626     scan.setReversed(true);
627     List<RowRange> ranges = Arrays.asList(
628         new RowRange(Bytes.toBytes("a"), true, Bytes.toBytes("c"), true),
629         new RowRange(Bytes.toBytes("f"), true, Bytes.toBytes("g"), true)
630     );
631     MultiRowRangeFilter filter = new MultiRowRangeFilter(ranges);
632     scan.setFilter(filter);
633 
634     List<String> expected = Arrays.asList("g", "f", "c", "b", "a");
635     List<String> actual = new ArrayList<>();
636     for (Cell cell : getResults(ht, scan)) {
637       actual.add(Bytes.toString(cell.getRowArray(), cell.getRowOffset(), cell.getRowLength()));
638     }
639 
640     assertEquals(expected, actual);
641   }
642 
643   @Test
644   public void testReverseMultiRowRangeFilterIncludingMinAndMaxRow() throws IOException {
645     tableName = Bytes.toBytes("testReverseMultiRowRangeFilterIncludingMinAndMaxRow");
646     HTable ht = TEST_UTIL.createTable(tableName, family);
647     for (String rowkey : Arrays.asList("a", "b", "c", "d", "e", "f", "g", "h")) {
648       byte[] row = Bytes.toBytes(rowkey);
649       Put p = new Put(row);
650       p.addColumn(family, qf, value);
651       ht.put(p);
652     }
653     TEST_UTIL.flush();
654 
655     Scan scan = new Scan();
656     scan.setReversed(true);
657     List<RowRange> ranges = Arrays.asList(
658         new RowRange(Bytes.toBytes("a"), true, Bytes.toBytes("c"), true),
659         new RowRange(Bytes.toBytes("f"), true, Bytes.toBytes("h"), true)
660     );
661     MultiRowRangeFilter filter = new MultiRowRangeFilter(ranges);
662     scan.setFilter(filter);
663 
664     List<String> expected = Arrays.asList("h", "g", "f", "c", "b", "a");
665     List<String> actual = new ArrayList<>();
666     for (Cell cell : getResults(ht, scan)) {
667       actual.add(Bytes.toString(cell.getRowArray(), cell.getRowOffset(), cell.getRowLength()));
668     }
669 
670     assertEquals(expected, actual);
671   }
672 
673   private void generateRows(int numberOfRows, HTable ht, byte[] family, byte[] qf, byte[] value)
674       throws IOException {
675     for (int i = 0; i < numberOfRows; i++) {
676       byte[] row = Bytes.toBytes(i);
677       Put p = new Put(row);
678       p.add(family, qf, value);
679       ht.put(p);
680     }
681     TEST_UTIL.flush();
682   }
683 
684   private List<Cell> getScanResult(byte[] startRow, byte[] stopRow, HTable ht) throws IOException {
685     Scan scan = new Scan();
686     scan.setMaxVersions();
687     if(!Bytes.toString(startRow).isEmpty()) {
688       scan.setStartRow(startRow);
689     }
690     if(!Bytes.toString(stopRow).isEmpty()) {
691       scan.setStopRow(stopRow);
692     }
693     ResultScanner scanner = ht.getScanner(scan);
694     List<Cell> kvList = new ArrayList<Cell>();
695     Result r;
696     while ((r = scanner.next()) != null) {
697       for (Cell kv : r.listCells()) {
698         kvList.add(kv);
699       }
700     }
701     scanner.close();
702     return kvList;
703   }
704 
705   private List<Cell> getResults(HTable ht, Scan scan) throws IOException {
706     ResultScanner scanner = ht.getScanner(scan);
707     List<Cell> results = new ArrayList<Cell>();
708     Result r;
709     while ((r = scanner.next()) != null) {
710       for (Cell kv : r.listCells()) {
711         results.add(kv);
712       }
713     }
714     scanner.close();
715     return results;
716   }
717 
718   private int getResultsSize(HTable ht, Scan scan) throws IOException {
719     return getResults(ht, scan).size();
720   }
721 }