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