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