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.*;
21  
22  import java.io.IOException;
23  import java.util.ArrayList;
24  import java.util.HashMap;
25  import java.util.HashSet;
26  import java.util.List;
27  import java.util.Map;
28  import java.util.Set;
29  
30  import org.apache.commons.logging.Log;
31  import org.apache.commons.logging.LogFactory;
32  import org.apache.hadoop.hbase.*;
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.client.Durability;
38  import org.apache.hadoop.hbase.client.Table;
39  import org.apache.hadoop.hbase.filter.FilterList.Operator;
40  import org.apache.hadoop.hbase.testclassification.MediumTests;
41  import org.apache.hadoop.hbase.util.Bytes;
42  import org.junit.Test;
43  import org.junit.After;
44  import org.junit.AfterClass;
45  import org.junit.Before;
46  import org.junit.BeforeClass;
47  import org.junit.experimental.categories.Category;
48  
49  
50  class StringRange {
51    private String start = null;
52    private String end = null;
53    private boolean startInclusive = true;
54    private boolean endInclusive = false;
55  
56    public StringRange(String start, boolean startInclusive, String end,
57        boolean endInclusive) {
58      this.start = start;
59      this.startInclusive = startInclusive;
60      this.end = end;
61      this.endInclusive = endInclusive;
62    }
63  
64    public String getStart() {
65      return this.start;
66    }
67  
68    public String getEnd() {
69      return this.end;
70    }
71  
72    public boolean isStartInclusive() {
73      return this.startInclusive;
74    }
75  
76    public boolean isEndInclusive() {
77      return this.endInclusive;
78    }
79  
80    @Override
81    public int hashCode() {
82      int hashCode = 0;
83      if (this.start != null) {
84        hashCode ^= this.start.hashCode();
85      }
86  
87      if (this.end != null) {
88        hashCode ^= this.end.hashCode();
89      }
90      return hashCode;
91    }
92  
93    @Override
94    public String toString() {
95      String result = (this.startInclusive ? "[" : "(")
96            + (this.start == null ? null : this.start) + ", "
97            + (this.end == null ? null : this.end)
98            + (this.endInclusive ? "]" : ")");
99      return result;
100   }
101 
102    public boolean inRange(String value) {
103     boolean afterStart = true;
104     if (this.start != null) {
105       int startCmp = value.compareTo(this.start);
106       afterStart = this.startInclusive ? startCmp >= 0 : startCmp > 0;
107     }
108 
109     boolean beforeEnd = true;
110     if (this.end != null) {
111       int endCmp = value.compareTo(this.end);
112       beforeEnd = this.endInclusive ? endCmp <= 0 : endCmp < 0;
113     }
114 
115     return afterStart && beforeEnd;
116   }
117 
118 }
119 
120 
121 @Category(MediumTests.class)
122 public class TestColumnRangeFilter {
123 
124   private final static HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility();
125 
126   private static final Log LOG = LogFactory.getLog(TestColumnRangeFilter.class);
127 
128   /**
129    * @throws java.lang.Exception
130    */
131   @BeforeClass
132   public static void setUpBeforeClass() throws Exception {
133     TEST_UTIL.startMiniCluster();
134   }
135 
136   /**
137    * @throws java.lang.Exception
138    */
139   @AfterClass
140   public static void tearDownAfterClass() throws Exception {
141     TEST_UTIL.shutdownMiniCluster();
142   }
143 
144   /**
145    * @throws java.lang.Exception
146    */
147   @Before
148   public void setUp() throws Exception {
149     // Nothing to do.
150   }
151 
152   /**
153    * @throws java.lang.Exception
154    */
155   @After
156   public void tearDown() throws Exception {
157     // Nothing to do.
158   }
159 
160   @Test
161   public void TestColumnRangeFilterClient() throws Exception {
162     String family = "Family";
163     String table = "TestColumnRangeFilterClient";
164     Table ht =
165         TEST_UTIL.createTable(TableName.valueOf(table), Bytes.toBytes(family), Integer.MAX_VALUE);
166 
167     List<String> rows = generateRandomWords(10, 8);
168     long maxTimestamp = 2;
169     List<String> columns = generateRandomWords(20000, 8);
170 
171     List<KeyValue> kvList = new ArrayList<KeyValue>();
172 
173     Map<StringRange, List<KeyValue>> rangeMap = new HashMap<StringRange, List<KeyValue>>();
174 
175     rangeMap.put(new StringRange(null, true, "b", false), new ArrayList<KeyValue>());
176     rangeMap.put(new StringRange("p", true, "q", false), new ArrayList<KeyValue>());
177     rangeMap.put(new StringRange("r", false, "s", true), new ArrayList<KeyValue>());
178     rangeMap.put(new StringRange("z", false, null, false), new ArrayList<KeyValue>());
179     String valueString = "ValueString";
180 
181     for (String row : rows) {
182       Put p = new Put(Bytes.toBytes(row));
183       p.setDurability(Durability.SKIP_WAL);
184       for (String column : columns) {
185         for (long timestamp = 1; timestamp <= maxTimestamp; timestamp++) {
186           KeyValue kv = KeyValueTestUtil.create(row, family, column, timestamp, valueString);
187           p.add(kv);
188           kvList.add(kv);
189           for (StringRange s : rangeMap.keySet()) {
190             if (s.inRange(column)) {
191               rangeMap.get(s).add(kv);
192             }
193           }
194         }
195       }
196       ht.put(p);
197     }
198 
199     TEST_UTIL.flush();
200 
201     ColumnRangeFilter filter;
202     Scan scan = new Scan();
203     scan.setMaxVersions();
204     for (StringRange s : rangeMap.keySet()) {
205       filter = new ColumnRangeFilter(s.getStart() == null ? null : Bytes.toBytes(s.getStart()),
206           s.isStartInclusive(), s.getEnd() == null ? null : Bytes.toBytes(s.getEnd()),
207           s.isEndInclusive());
208       scan.setFilter(filter);
209       assertEquals(rangeMap.get(s).size(), cellsCount(ht, filter));
210     }
211     ht.close();
212   }
213 
214   @Test
215   public void TestColumnRangeFilterWithColumnPaginationFilter() throws Exception {
216     String family = "Family";
217     String table = "TestColumnRangeFilterWithColumnPaginationFilter";
218     try (Table ht =
219         TEST_UTIL.createTable(TableName.valueOf(table), Bytes.toBytes(family), Integer.MAX_VALUE)) {
220       // one row.
221       String row = "row";
222       // One version
223       long timestamp = 100;
224       // 10 columns
225       int[] columns = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
226       String valueString = "ValueString";
227 
228       Put p = new Put(Bytes.toBytes(row));
229       p.setDurability(Durability.SKIP_WAL);
230       for (int column : columns) {
231         KeyValue kv =
232             KeyValueTestUtil.create(row, family, Integer.toString(column), timestamp, valueString);
233         p.add(kv);
234       }
235       ht.put(p);
236 
237       TEST_UTIL.flush();
238 
239       // Column range from 1 to 9.
240       StringRange stringRange = new StringRange("1", true, "9", false);
241       ColumnRangeFilter filter1 = new ColumnRangeFilter(Bytes.toBytes(stringRange.getStart()),
242           stringRange.isStartInclusive(), Bytes.toBytes(stringRange.getEnd()),
243           stringRange.isEndInclusive());
244 
245       ColumnPaginationFilter filter2 = new ColumnPaginationFilter(5, 0);
246       ColumnPaginationFilter filter3 = new ColumnPaginationFilter(5, 1);
247       ColumnPaginationFilter filter4 = new ColumnPaginationFilter(5, 2);
248       ColumnPaginationFilter filter5 = new ColumnPaginationFilter(5, 6);
249       ColumnPaginationFilter filter6 = new ColumnPaginationFilter(5, 9);
250       assertEquals(5, cellsCount(ht, new FilterList(Operator.MUST_PASS_ALL, filter1, filter2)));
251       assertEquals(5, cellsCount(ht, new FilterList(Operator.MUST_PASS_ALL, filter1, filter3)));
252       assertEquals(5, cellsCount(ht, new FilterList(Operator.MUST_PASS_ALL, filter1, filter4)));
253       assertEquals(2, cellsCount(ht, new FilterList(Operator.MUST_PASS_ALL, filter1, filter5)));
254       assertEquals(0, cellsCount(ht, new FilterList(Operator.MUST_PASS_ALL, filter1, filter6)));
255     }
256   }
257 
258   private int cellsCount(Table table, Filter filter) throws IOException {
259     Scan scan = new Scan().setFilter(filter).setMaxVersions();
260     try (ResultScanner scanner = table.getScanner(scan)) {
261       List<Cell> results = new ArrayList<>();
262       Result result;
263       while ((result = scanner.next()) != null) {
264         results.addAll(result.listCells());
265       }
266       return results.size();
267     }
268   }
269 
270   List<String> generateRandomWords(int numberOfWords, int maxLengthOfWords) {
271     Set<String> wordSet = new HashSet<String>();
272     for (int i = 0; i < numberOfWords; i++) {
273       int lengthOfWords = (int) (Math.random() * maxLengthOfWords) + 1;
274       char[] wordChar = new char[lengthOfWords];
275       for (int j = 0; j < wordChar.length; j++) {
276         wordChar[j] = (char) (Math.random() * 26 + 97);
277       }
278       String word = new String(wordChar);
279       wordSet.add(word);
280     }
281     List<String> wordList = new ArrayList<String>(wordSet);
282     return wordList;
283   }
284 }
285