Package com.inadco.hbl.client.impl.scanner

Source Code of com.inadco.hbl.client.impl.scanner.CompositeKeyRowFilter

/*
*
*  Copyright © 2010, 2011 Inadco, Inc. All rights reserved.
*     Licensed under the Apache License, Version 2.0 (the "License");
*     you may not use this file except in compliance with the License.
*     You may obtain a copy of the License at
*         http://www.apache.org/licenses/LICENSE-2.0
*     Unless required by applicable law or agreed to in writing, software
*     distributed under the License is distributed on an "AS IS" BASIS,
*     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*     See the License for the specific language governing permissions and
*     limitations under the License.
*/
package com.inadco.hbl.client.impl.scanner;

import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.util.Arrays;

import org.apache.commons.lang.Validate;
import org.apache.hadoop.hbase.KeyValue;
import org.apache.hadoop.hbase.filter.FilterBase;
import org.apache.hadoop.hbase.util.Bytes;

import com.inadco.hbl.api.Range;
import com.inadco.hbl.util.HblUtil;

/**
* filter for ranges of individual parts of a composite key.
* <P>
*
* Let key be a composite key consisting of individual keys (key1,key2...keyn)
* concatenated together.
* <P>
*
* Let's also assume that a range is given for each key<sub>i</sub> as
* range<sub>i</sub>. Each range corresponds to a mathematical definition of an
* interval, i.e. it can be (left-, right-) open or closed, and we do not
* support (left-,right-) unbounded intervals at all.
* <P>
*
* Then this filter ensures that only rows satisfying individual range scan
* conditions of each key are getting in.
* <P>
*
*
* @author dmitriy
*
*/
public class CompositeKeyRowFilter extends FilterBase {
    private Range[]              pathRange;

    /*
     * of course since we are using Writable serialization rather than java
     * native, keyword 'transient' doesn't mean anything in this context, but
     * i'd like to use it as a marker for something i don't really serialize.
     */
    private transient int[]      keyOffsets;
    private transient int        compositeKeyLen;
    private transient byte[]     nextKeyHint;
    private transient ReturnCode nextKeyCode;
    private transient int        rowsSeen;

    public CompositeKeyRowFilter(Range[] pathRange) throws IOException {
        super();

        Validate.notNull(pathRange);

        this.pathRange = pathRange;

    }

    public CompositeKeyRowFilter() {
        super();
    }

    @Override
    public void readFields(DataInput in) throws IOException {
        int keyNum = HblUtil.readVarUint32(in);
        pathRange = new Range[keyNum];
        for (int i = 0; i < keyNum; i++) {
            Range r = new Range();
            r.readFields(in);
            pathRange[i] = r;
        }
        initTransients();
    }

    public void initTransients() {
        int keyNum = pathRange.length;
        keyOffsets = new int[keyNum];
        for (int i = 0; i < keyNum; i++) {
            if (i > 0)
                keyOffsets[i] = keyOffsets[i - 1] + pathRange[i - 1].getKeyLen();
        }
        compositeKeyLen = keyNum > 0 ? keyOffsets[keyNum - 1] + pathRange[keyNum - 1].getKeyLen() : 0;
    }

    @Override
    public void write(DataOutput out) throws IOException {
        HblUtil.writeVarUint32(out, pathRange.length);
        for (int i = 0; i < pathRange.length; i++)
            pathRange[i].write(out);
    }

    @Override
    public boolean filterRowKey(final byte[] buffer, final int rowKeyOffset, final int rowLength) {
        rowsSeen++;

        for (int i = 0; i < pathRange.length; i++) {
            int keyLen, keyOffset;
            Range r = pathRange[i];

            int comp =
                Bytes.BYTES_RAWCOMPARATOR.compare(buffer,
                                                  keyOffset = rowKeyOffset + keyOffsets[i],
                                                  keyLen = r.getKeyLen(),
                                                  r.getLeftBound(),
                                                  0,
                                                  keyLen);
            if (comp < 0 || comp == 0 && pathRange[i].isLeftOpen()) {
                /*
                 * if we are at rightmost key, doesn't make sense to reseek,
                 * just skip, as we know next hint would be next row.
                 */
                if (comp == 0 && i == pathRange.length - 1)
                    return true;

                if (setHint2LowerBound(i, buffer, rowKeyOffset, rowLength, false))
                    return true;

                /*
                 * if we are exactly at the left-open border already: increment
                 */

                nextKeyCode = ReturnCode.SEEK_NEXT_USING_HINT;
                return false;
            }
            comp = Bytes.BYTES_RAWCOMPARATOR.compare(buffer, keyOffset, keyLen, r.getRightBound(), 0, keyLen);
            if (comp > 0 || comp == 0 && r.isRightOpen()) {
                if (setHint2LowerBound(i, buffer, rowKeyOffset, rowLength, true))
                    return true;
                nextKeyCode = ReturnCode.SEEK_NEXT_USING_HINT;
                return false;
            }

            /*
             * hierarchy stuff
             */

            if (r.getLevelLen() >= 0) {

                /*
                 * level is greater than ours, we need to skip to the next slice
                 * at our level -- if this a hierarchical key
                 */

                if (testLevelGreater(i, buffer, rowKeyOffset)) {
                    setHint2NextHierarchicalLevelKey(i, buffer, rowKeyOffset, rowLength);
                    nextKeyCode = ReturnCode.SEEK_NEXT_USING_HINT;
                    return false;
                }

                /*
                 * if current key has level less than current level -- just skip
                 * it. This technically is never supposed to happen because less
                 * level keys would be filtered out by low boundary comparison
                 * (they always less than lower boundary).
                 */
                if (testLevelLess(i, buffer, rowKeyOffset)) {
                    return true;
                }
            }

        }
        nextKeyCode = ReturnCode.INCLUDE;
        return false;
    }

    @Override
    public ReturnCode filterKeyValue(KeyValue kv) {
        if (nextKeyCode == ReturnCode.SEEK_NEXT_USING_HINT) {

            /*
             * just a safeguard against indefinite loop. Once we iron out all
             * problems with reseeks, we can remove this.
             */

            if (Bytes.compareTo(kv.getRow(), nextKeyHint) < 0)
                return nextKeyCode;
            else
                return ReturnCode.INCLUDE;
        }
        return nextKeyCode;
    }

    @Override
    public KeyValue getNextKeyHint(KeyValue currentKV) {
        return new KeyValue(nextKeyHint, 0l);
    }

    /**
     *
     * @param lower
     *            return lower bound if true; otherwise, return upper bound.
     * @return lower or upper bound for the composite index scan given set of
     *         ranges.
     */
    public byte[] getCompositeBound(boolean lower) {
        if (keyOffsets == null)
            initTransients();
        byte[] bound = new byte[compositeKeyLen];
        if (lower) {
            for (int i = 0; i < pathRange.length; i++)
                System.arraycopy(pathRange[i].getLeftBound(), 0, bound, keyOffsets[i], pathRange[i].getKeyLen());
        } else {
            for (int i = 0; i < pathRange.length; i++)
                System.arraycopy(pathRange[i].getRightBound(), 0, bound, keyOffsets[i], pathRange[i].getKeyLen());
        }
        return bound;
    }

    public int getRowsSeen() {
        return rowsSeen;
    }

    /**
     * Adjust hint to the next hierarchy member key (assuming level < maxdepth).
     *
     * @param dimIndex
     *            current dimension range index
     * @param compositeKey
     *            composite key buffer
     * @param compositeKeyOffset
     *            offset of composite key
     * @param composteKeyLength
     *            length of entire composite key
     * @param keylen
     *            keylength of hierarchical key corresponding to dimension
     *            dimIndex
     * @param subkeyLength
     *            level key length of hierarchy dimIndex
     */
    private void setHint2NextHierarchicalLevelKey(int dimIndex,
                                                  final byte[] compositeKey,
                                                  int compositeKeyOffset,
                                                  int compositeKeyLength) {

        int keyOffset = keyOffsets[dimIndex];

        if (nextKeyHint == null)
            nextKeyHint = new byte[compositeKeyLen];

        Range r = pathRange[dimIndex];
        int subkeylen = r.getSubkeyLen();
        int keylen = r.getKeyLen();

        System.arraycopy(compositeKey, compositeKeyOffset, nextKeyHint, 0, keyOffset + subkeylen);
        HblUtil.incrementKey(nextKeyHint, keyOffset, subkeylen);
        Arrays.fill(nextKeyHint, keyOffset + subkeylen, keyOffset + keylen, (byte) 0);
        adjustHint2LowerBound(dimIndex + 1);
    }

    /**
     *
     * @param dimIndex
     * @param key
     * @param keyOffset
     * @param keyLength
     * @param prefixPlus1
     *            add +1 to prefix part before forming low bound tail.
     * @return true if it was the last key, no more ranges
     */
    private boolean setHint2LowerBound(final int dimIndex,
                                       final byte[] compositeKey,
                                       int compositeKeyOffset,
                                       int compositeKeyLength,
                                       boolean prefixPlus1) {

        int keyOffset = keyOffsets[dimIndex];
        int keyLen = pathRange[dimIndex].getKeyLen();
        int subkeyLen = pathRange[dimIndex].getSubkeyLen();

        if (nextKeyHint == null)
            nextKeyHint = new byte[compositeKeyLen];

        /* copy prefix */
        System.arraycopy(compositeKey, compositeKeyOffset, nextKeyHint, 0, keyOffset);

        if (prefixPlus1) {
            if (HblUtil.incrementKey(nextKeyHint, 0, keyOffset))
                return true;
        }

        /* copy key */
        System.arraycopy(pathRange[dimIndex].getLeftBound(), 0, nextKeyHint, keyOffset, keyLen);

        /* reseek must start at +1 of subkeylen in case of open bound */
        if (pathRange[dimIndex].isLeftOpen())
            HblUtil.incrementKey(nextKeyHint, keyOffset, subkeyLen);

        /* set the "tail" to lower bound */
        adjustHint2LowerBound(dimIndex + 1);
        return false;
    }

    /**
     * test whether current key's hierarchy level is greater than spec'd in
     * range path
     */
    private boolean testLevelGreater(int dimIndex, byte[] compositeKey, int compositeKeyOffset) {
        Range r = pathRange[dimIndex];
        int keylen = r.getKeyLen();
        int nextLevelOffset = r.getLevelOffset() + r.getLevelLen();
        if (nextLevelOffset >= keylen)
            return false;

        int keyOffset = keyOffsets[dimIndex] + compositeKeyOffset;

        return !HblUtil.test0s(compositeKey, keyOffset + nextLevelOffset, keylen - nextLevelOffset);
    }

    /**
     * tests whether current key's hierarchy level is smaller that spec'd in
     * range path
     *
     * @param dimIndex
     * @param compositeKey
     * @param compositeKeyOffset
     * @return
     */
    private boolean testLevelLess(int dimIndex, byte[] compositeKey, int compositeKeyOffset) {
        Range r = pathRange[dimIndex];
        if (r.getLevelLen() == 0)
            return false; // smallest level already

        int keyOffset = keyOffsets[dimIndex] + compositeKeyOffset;

        return HblUtil.test0s(compositeKey, keyOffset + r.getLevelOffset(), r.getLevelLen());
    }

    private void adjustHint2LowerBound(final int fromDimIndex) {
        for (int dimIndex = fromDimIndex; dimIndex < pathRange.length; dimIndex++) {
            // copy the "lower bound tail"
            int offset = keyOffsets[dimIndex];
            System.arraycopy(pathRange[dimIndex].getLeftBound(),
                             0,
                             nextKeyHint,
                             offset,
                             pathRange[dimIndex].getKeyLen());
            // increment if open left is meant:
            if (pathRange[dimIndex].isLeftOpen()) {
                int subkeylen = pathRange[dimIndex].getSubkeyLen();
                HblUtil.incrementKey(nextKeyHint, offset, subkeylen);
            }

        }
    }

}
TOP

Related Classes of com.inadco.hbl.client.impl.scanner.CompositeKeyRowFilter

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.