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