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.nio.ByteBuffer;
024import java.util.ArrayList;
025import java.util.List;
026import org.apache.hadoop.hbase.Cell;
027import org.apache.hadoop.hbase.CellUtil;
028import org.apache.hadoop.hbase.HBaseTestingUtil;
029import org.apache.hadoop.hbase.TableName;
030import org.apache.hadoop.hbase.client.Durability;
031import org.apache.hadoop.hbase.client.Put;
032import org.apache.hadoop.hbase.client.Result;
033import org.apache.hadoop.hbase.client.ResultScanner;
034import org.apache.hadoop.hbase.client.Scan;
035import org.apache.hadoop.hbase.client.Table;
036import org.apache.hadoop.hbase.testclassification.FilterTests;
037import org.apache.hadoop.hbase.testclassification.MediumTests;
038import org.apache.hadoop.hbase.util.Bytes;
039import org.apache.hadoop.hbase.util.EnvironmentEdgeManager;
040import org.apache.hadoop.hbase.util.Pair;
041import org.junit.jupiter.api.AfterAll;
042import org.junit.jupiter.api.BeforeAll;
043import org.junit.jupiter.api.Tag;
044import org.junit.jupiter.api.Test;
045import org.junit.jupiter.api.TestInfo;
046import org.slf4j.Logger;
047import org.slf4j.LoggerFactory;
048
049import org.apache.hbase.thirdparty.com.google.common.collect.Lists;
050
051@Tag(FilterTests.TAG)
052@Tag(MediumTests.TAG)
053public class TestFuzzyRowAndColumnRangeFilter {
054
055  private final static HBaseTestingUtil TEST_UTIL = new HBaseTestingUtil();
056  private static final Logger LOG = LoggerFactory.getLogger(TestFuzzyRowAndColumnRangeFilter.class);
057
058  /**
059   * @throws java.lang.Exception
060   */
061  @BeforeAll
062  public static void setUpBeforeClass() throws Exception {
063    TEST_UTIL.startMiniCluster();
064  }
065
066  /**
067   * @throws java.lang.Exception
068   */
069  @AfterAll
070  public static void tearDownAfterClass() throws Exception {
071    TEST_UTIL.shutdownMiniCluster();
072  }
073
074  @Test
075  public void Test(TestInfo testInfo) throws Exception {
076    String cf = "f";
077    Table ht = TEST_UTIL.createTable(TableName.valueOf(testInfo.getTestMethod().get().getName()),
078      Bytes.toBytes(cf), Integer.MAX_VALUE);
079
080    // 10 byte row key - (2 bytes 4 bytes 4 bytes)
081    // 4 byte qualifier
082    // 4 byte value
083
084    for (int i1 = 0; i1 < 2; i1++) {
085      for (int i2 = 0; i2 < 5; i2++) {
086        byte[] rk = new byte[10];
087
088        ByteBuffer buf = ByteBuffer.wrap(rk);
089        buf.clear();
090        buf.putShort((short) 2);
091        buf.putInt(i1);
092        buf.putInt(i2);
093
094        for (int c = 0; c < 5; c++) {
095          byte[] cq = new byte[4];
096          Bytes.putBytes(cq, 0, Bytes.toBytes(c), 0, 4);
097
098          Put p = new Put(rk);
099          p.setDurability(Durability.SKIP_WAL);
100          p.addColumn(Bytes.toBytes(cf), cq, Bytes.toBytes(c));
101          ht.put(p);
102          LOG.info(
103            "Inserting: rk: " + Bytes.toStringBinary(rk) + " cq: " + Bytes.toStringBinary(cq));
104        }
105      }
106    }
107
108    TEST_UTIL.flush();
109
110    // test passes
111    runTest(ht, 0, 10);
112
113    // test fails
114    runTest(ht, 1, 8);
115  }
116
117  private void runTest(Table hTable, int cqStart, int expectedSize) throws IOException {
118    // [0, 2, ?, ?, ?, ?, 0, 0, 0, 1]
119    byte[] fuzzyKey = new byte[10];
120    ByteBuffer buf = ByteBuffer.wrap(fuzzyKey);
121    buf.clear();
122    buf.putShort((short) 2);
123    for (int i = 0; i < 4; i++)
124      buf.put((byte) 63);
125    buf.putInt((short) 1);
126
127    byte[] mask = new byte[] { 0, 0, 1, 1, 1, 1, 0, 0, 0, 0 };
128
129    Pair<byte[], byte[]> pair = new Pair<>(fuzzyKey, mask);
130    FuzzyRowFilter fuzzyRowFilter = new FuzzyRowFilter(Lists.newArrayList(pair));
131    ColumnRangeFilter columnRangeFilter =
132      new ColumnRangeFilter(Bytes.toBytes(cqStart), true, Bytes.toBytes(4), true);
133    // regular test
134    runScanner(hTable, expectedSize, fuzzyRowFilter, columnRangeFilter);
135    // reverse filter order test
136    runScanner(hTable, expectedSize, columnRangeFilter, fuzzyRowFilter);
137  }
138
139  private void runScanner(Table hTable, int expectedSize, Filter... filters) throws IOException {
140    String cf = "f";
141    Scan scan = new Scan();
142    scan.addFamily(Bytes.toBytes(cf));
143    FilterList filterList = new FilterList(filters);
144    scan.setFilter(filterList);
145
146    ResultScanner scanner = hTable.getScanner(scan);
147    List<Cell> results = new ArrayList<>();
148    Result result;
149    long timeBeforeScan = EnvironmentEdgeManager.currentTime();
150    while ((result = scanner.next()) != null) {
151      for (Cell kv : result.listCells()) {
152        LOG.info("Got rk: " + Bytes.toStringBinary(CellUtil.cloneRow(kv)) + " cq: "
153          + Bytes.toStringBinary(CellUtil.cloneQualifier(kv)));
154        results.add(kv);
155      }
156    }
157    long scanTime = EnvironmentEdgeManager.currentTime() - timeBeforeScan;
158    scanner.close();
159
160    LOG.info("scan time = " + scanTime + "ms");
161    LOG.info("found " + results.size() + " results");
162
163    assertEquals(expectedSize, results.size());
164  }
165}