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.jupiter.api.Assertions.assertEquals;
021
022import java.io.IOException;
023import java.util.ArrayList;
024import java.util.HashMap;
025import java.util.HashSet;
026import java.util.List;
027import java.util.Map;
028import java.util.Set;
029import org.apache.hadoop.hbase.Cell;
030import org.apache.hadoop.hbase.HBaseTestingUtil;
031import org.apache.hadoop.hbase.KeyValue;
032import org.apache.hadoop.hbase.KeyValueTestUtil;
033import org.apache.hadoop.hbase.TableName;
034import org.apache.hadoop.hbase.client.ColumnFamilyDescriptor;
035import org.apache.hadoop.hbase.client.ColumnFamilyDescriptorBuilder;
036import org.apache.hadoop.hbase.client.Durability;
037import org.apache.hadoop.hbase.client.Put;
038import org.apache.hadoop.hbase.client.RegionInfo;
039import org.apache.hadoop.hbase.client.RegionInfoBuilder;
040import org.apache.hadoop.hbase.client.Scan;
041import org.apache.hadoop.hbase.client.TableDescriptor;
042import org.apache.hadoop.hbase.client.TableDescriptorBuilder;
043import org.apache.hadoop.hbase.regionserver.HRegion;
044import org.apache.hadoop.hbase.regionserver.InternalScanner;
045import org.apache.hadoop.hbase.testclassification.FilterTests;
046import org.apache.hadoop.hbase.testclassification.MediumTests;
047import org.apache.hadoop.hbase.util.Bytes;
048import org.junit.jupiter.api.Tag;
049import org.junit.jupiter.api.Test;
050import org.junit.jupiter.api.TestInfo;
051
052@Tag(FilterTests.TAG)
053@Tag(MediumTests.TAG)
054public class TestMultipleColumnPrefixFilter {
055
056  private final static HBaseTestingUtil TEST_UTIL = new HBaseTestingUtil();
057
058  @Test
059  public void testMultipleColumnPrefixFilter(TestInfo testInfo) throws IOException {
060    String family = "Family";
061    TableDescriptorBuilder tableDescriptorBuilder = TableDescriptorBuilder
062      .newBuilder(TableName.valueOf(testInfo.getTestMethod().get().getName()));
063    ColumnFamilyDescriptor columnFamilyDescriptor =
064      ColumnFamilyDescriptorBuilder.newBuilder(Bytes.toBytes(family)).setMaxVersions(3).build();
065    tableDescriptorBuilder.setColumnFamily(columnFamilyDescriptor);
066    TableDescriptor tableDescriptor = tableDescriptorBuilder.build();
067    // HRegionInfo info = new HRegionInfo(htd, null, null, false);
068    RegionInfo info = RegionInfoBuilder.newBuilder(tableDescriptor.getTableName()).build();
069    HRegion region = HBaseTestingUtil.createRegionAndWAL(info, TEST_UTIL.getDataTestDir(),
070      TEST_UTIL.getConfiguration(), tableDescriptor);
071
072    List<String> rows = generateRandomWords(100, "row");
073    List<String> columns = generateRandomWords(10000, "column");
074    long maxTimestamp = 2;
075
076    List<Cell> kvList = new ArrayList<>();
077
078    Map<String, List<Cell>> prefixMap = new HashMap<>();
079
080    prefixMap.put("p", new ArrayList<>());
081    prefixMap.put("q", new ArrayList<>());
082    prefixMap.put("s", new ArrayList<>());
083
084    String valueString = "ValueString";
085
086    for (String row : rows) {
087      Put p = new Put(Bytes.toBytes(row));
088      p.setDurability(Durability.SKIP_WAL);
089      for (String column : columns) {
090        for (long timestamp = 1; timestamp <= maxTimestamp; timestamp++) {
091          KeyValue kv = KeyValueTestUtil.create(row, family, column, timestamp, valueString);
092          p.add(kv);
093          kvList.add(kv);
094          for (String s : prefixMap.keySet()) {
095            if (column.startsWith(s)) {
096              prefixMap.get(s).add(kv);
097            }
098          }
099        }
100      }
101      region.put(p);
102    }
103
104    MultipleColumnPrefixFilter filter;
105    Scan scan = new Scan();
106    scan.readAllVersions();
107    byte[][] filter_prefix = new byte[2][];
108    filter_prefix[0] = new byte[] { 'p' };
109    filter_prefix[1] = new byte[] { 'q' };
110
111    filter = new MultipleColumnPrefixFilter(filter_prefix);
112    scan.setFilter(filter);
113    List<Cell> results = new ArrayList<>();
114    InternalScanner scanner = region.getScanner(scan);
115    while (scanner.next(results))
116      ;
117    assertEquals(prefixMap.get("p").size() + prefixMap.get("q").size(), results.size());
118
119    HBaseTestingUtil.closeRegionAndWAL(region);
120  }
121
122  @Test
123  public void testMultipleColumnPrefixFilterWithManyFamilies(TestInfo testInfo) throws IOException {
124    String family1 = "Family1";
125    String family2 = "Family2";
126    TableDescriptorBuilder tableDescriptorBuilder = TableDescriptorBuilder
127      .newBuilder(TableName.valueOf(testInfo.getTestMethod().get().getName()));
128    ColumnFamilyDescriptor columnFamilyDescriptor =
129      ColumnFamilyDescriptorBuilder.newBuilder(Bytes.toBytes(family1)).setMaxVersions(3).build();
130    tableDescriptorBuilder.setColumnFamily(columnFamilyDescriptor);
131    columnFamilyDescriptor =
132      ColumnFamilyDescriptorBuilder.newBuilder(Bytes.toBytes(family2)).setMaxVersions(3).build();
133    tableDescriptorBuilder.setColumnFamily(columnFamilyDescriptor);
134    TableDescriptor tableDescriptor = tableDescriptorBuilder.build();
135    RegionInfo info = RegionInfoBuilder.newBuilder(tableDescriptor.getTableName()).build();
136    HRegion region = HBaseTestingUtil.createRegionAndWAL(info, TEST_UTIL.getDataTestDir(),
137      TEST_UTIL.getConfiguration(), tableDescriptor);
138
139    List<String> rows = generateRandomWords(100, "row");
140    List<String> columns = generateRandomWords(10000, "column");
141    long maxTimestamp = 3;
142
143    List<Cell> kvList = new ArrayList<>();
144
145    Map<String, List<Cell>> prefixMap = new HashMap<>();
146
147    prefixMap.put("p", new ArrayList<>());
148    prefixMap.put("q", new ArrayList<>());
149    prefixMap.put("s", new ArrayList<>());
150
151    String valueString = "ValueString";
152
153    for (String row : rows) {
154      Put p = new Put(Bytes.toBytes(row));
155      p.setDurability(Durability.SKIP_WAL);
156      for (String column : columns) {
157        for (long timestamp = 1; timestamp <= maxTimestamp; timestamp++) {
158          double rand = Math.random();
159          Cell kv;
160          if (rand < 0.5) {
161            kv = KeyValueTestUtil.create(row, family1, column, timestamp, valueString);
162          } else {
163            kv = KeyValueTestUtil.create(row, family2, column, timestamp, valueString);
164          }
165          p.add(kv);
166          kvList.add(kv);
167          for (String s : prefixMap.keySet()) {
168            if (column.startsWith(s)) {
169              prefixMap.get(s).add(kv);
170            }
171          }
172        }
173      }
174      region.put(p);
175    }
176
177    MultipleColumnPrefixFilter filter;
178    Scan scan = new Scan();
179    scan.readAllVersions();
180    byte[][] filter_prefix = new byte[2][];
181    filter_prefix[0] = new byte[] { 'p' };
182    filter_prefix[1] = new byte[] { 'q' };
183
184    filter = new MultipleColumnPrefixFilter(filter_prefix);
185    scan.setFilter(filter);
186    List<Cell> results = new ArrayList<>();
187    InternalScanner scanner = region.getScanner(scan);
188    while (scanner.next(results))
189      ;
190    assertEquals(prefixMap.get("p").size() + prefixMap.get("q").size(), results.size());
191
192    HBaseTestingUtil.closeRegionAndWAL(region);
193  }
194
195  @Test
196  public void testMultipleColumnPrefixFilterWithColumnPrefixFilter(TestInfo testInfo)
197    throws IOException {
198    String family = "Family";
199    TableDescriptorBuilder tableDescriptorBuilder = TableDescriptorBuilder
200      .newBuilder(TableName.valueOf(testInfo.getTestMethod().get().getName()));
201    ColumnFamilyDescriptor columnFamilyDescriptor =
202      ColumnFamilyDescriptorBuilder.newBuilder(Bytes.toBytes(family)).build();
203    tableDescriptorBuilder.setColumnFamily(columnFamilyDescriptor);
204    TableDescriptor tableDescriptor = tableDescriptorBuilder.build();
205    RegionInfo info = RegionInfoBuilder.newBuilder(tableDescriptor.getTableName()).build();
206    HRegion region = HBaseTestingUtil.createRegionAndWAL(info, TEST_UTIL.getDataTestDir(),
207      TEST_UTIL.getConfiguration(), tableDescriptor);
208
209    List<String> rows = generateRandomWords(100, "row");
210    List<String> columns = generateRandomWords(10000, "column");
211    long maxTimestamp = 2;
212
213    String valueString = "ValueString";
214
215    for (String row : rows) {
216      Put p = new Put(Bytes.toBytes(row));
217      p.setDurability(Durability.SKIP_WAL);
218      for (String column : columns) {
219        for (long timestamp = 1; timestamp <= maxTimestamp; timestamp++) {
220          KeyValue kv = KeyValueTestUtil.create(row, family, column, timestamp, valueString);
221          p.add(kv);
222        }
223      }
224      region.put(p);
225    }
226
227    MultipleColumnPrefixFilter multiplePrefixFilter;
228    Scan scan1 = new Scan();
229    scan1.readAllVersions();
230    byte[][] filter_prefix = new byte[1][];
231    filter_prefix[0] = new byte[] { 'p' };
232
233    multiplePrefixFilter = new MultipleColumnPrefixFilter(filter_prefix);
234    scan1.setFilter(multiplePrefixFilter);
235    List<Cell> results1 = new ArrayList<>();
236    InternalScanner scanner1 = region.getScanner(scan1);
237    while (scanner1.next(results1))
238      ;
239
240    ColumnPrefixFilter singlePrefixFilter;
241    Scan scan2 = new Scan();
242    scan2.readAllVersions();
243    singlePrefixFilter = new ColumnPrefixFilter(Bytes.toBytes("p"));
244
245    scan2.setFilter(singlePrefixFilter);
246    List<Cell> results2 = new ArrayList<>();
247    InternalScanner scanner2 = region.getScanner(scan1);
248    while (scanner2.next(results2))
249      ;
250
251    assertEquals(results1.size(), results2.size());
252
253    HBaseTestingUtil.closeRegionAndWAL(region);
254  }
255
256  List<String> generateRandomWords(int numberOfWords, String suffix) {
257    Set<String> wordSet = new HashSet<>();
258    for (int i = 0; i < numberOfWords; i++) {
259      int lengthOfWords = (int) (Math.random() * 2) + 1;
260      char[] wordChar = new char[lengthOfWords];
261      for (int j = 0; j < wordChar.length; j++) {
262        wordChar[j] = (char) (Math.random() * 26 + 97);
263      }
264      String word;
265      if (suffix == null) {
266        word = new String(wordChar);
267      } else {
268        word = new String(wordChar) + suffix;
269      }
270      wordSet.add(word);
271    }
272    List<String> wordList = new ArrayList<>(wordSet);
273    return wordList;
274  }
275
276}