blob: 192d1ef7bdceb771f89dc2ac83e6f65b0f7b954a [file] [log] [blame]
/*
* Copyright 2013-2014 eBay Software Foundation
*
* 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.kylinolap.storage.hbase;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.kylinolap.common.util.BytesUtil;
import com.kylinolap.cube.CubeSegment;
import com.kylinolap.cube.cuboid.Cuboid;
import com.kylinolap.cube.kv.AbstractRowKeyEncoder;
import com.kylinolap.cube.kv.FuzzyKeyEncoder;
import com.kylinolap.cube.kv.FuzzyMaskEncoder;
import com.kylinolap.cube.kv.RowConstants;
import com.kylinolap.dict.DateStrDictionary;
import com.kylinolap.metadata.model.cube.CubeDesc;
import com.kylinolap.metadata.model.cube.TblColRef;
import org.apache.hadoop.hbase.util.Bytes;
import org.apache.hadoop.hbase.util.Pair;
import java.util.*;
/**
* @author xjiang
*/
public class HBaseKeyRange implements Comparable<HBaseKeyRange> {
private static final int FUZZY_VALUE_CAP = 20;
private static final byte[] ZERO_TAIL_BYTES = new byte[]{0};
private final CubeSegment cubeSeg;
private final Cuboid cuboid;
private final List<Collection<ColumnValueRange>> flatOrAndFilter; // OR-AND filter, (A AND B AND ..) OR (C AND D AND ..) OR ..
private byte[] startKey;
private byte[] stopKey;
private List<Pair<byte[], byte[]>> fuzzyKeys;
private String startKeyString;
private String stopKeyString;
private String fuzzyKeyString;
private long partitionColumnStartDate = Long.MIN_VALUE;
private long partitionColumnEndDate = Long.MAX_VALUE;
public HBaseKeyRange(CubeSegment cubeSeg, Cuboid cuboid, byte[] startKey, byte[] stopKey,
List<Pair<byte[], byte[]>> fuzzyKeys, List<Collection<ColumnValueRange>> flatColumnValueFilter,
long partitionColumnStartDate, long partitionColumnEndDate) {
this.cubeSeg = cubeSeg;
this.cuboid = cuboid;
this.startKey = startKey;
this.stopKey = stopKey;
this.fuzzyKeys = fuzzyKeys;
this.flatOrAndFilter = flatColumnValueFilter;
this.partitionColumnStartDate = partitionColumnStartDate;
this.partitionColumnEndDate = partitionColumnEndDate;
initDebugString();
}
public HBaseKeyRange(Collection<TblColRef> dimensionColumns,
Collection<ColumnValueRange> andDimensionRanges, CubeSegment cubeSeg, CubeDesc cubeDesc) {
this.cubeSeg = cubeSeg;
long cuboidId = this.calculateCuboidID(cubeDesc, dimensionColumns);
this.cuboid = Cuboid.findById(cubeDesc, cuboidId);
this.flatOrAndFilter = Lists.newLinkedList();
this.flatOrAndFilter.add(andDimensionRanges);
init(andDimensionRanges);
initDebugString();
}
private long calculateCuboidID(CubeDesc cube, Collection<TblColRef> dimensions) {
long cuboidID = 0;
for (TblColRef column : dimensions) {
int index = cube.getRowkey().getColumnBitIndex(column);
cuboidID |= 1L << index;
}
return cuboidID;
}
private void init(Collection<ColumnValueRange> andDimensionRanges) {
int size = andDimensionRanges.size();
Map<TblColRef, String> startValues = Maps.newHashMapWithExpectedSize(size);
Map<TblColRef, String> stopValues = Maps.newHashMapWithExpectedSize(size);
Map<TblColRef, Set<String>> fuzzyValues = Maps.newHashMapWithExpectedSize(size);
for (ColumnValueRange dimRange : andDimensionRanges) {
TblColRef column = dimRange.getColumn();
startValues.put(column, dimRange.getBeginValue());
stopValues.put(column, dimRange.getEndValue());
fuzzyValues.put(column, dimRange.getEqualValues());
TblColRef partitionDateColumnRef =
cubeSeg.getCubeDesc().getCubePartitionDesc().getPartitionDateColumnRef();
if (column.equals(partitionDateColumnRef)) {
initPartitionRange(dimRange);
}
}
AbstractRowKeyEncoder encoder = AbstractRowKeyEncoder.createInstance(cubeSeg, cuboid);
encoder.setBlankByte(RowConstants.ROWKEY_LOWER_BYTE);
this.startKey = encoder.encode(startValues);
encoder.setBlankByte(RowConstants.ROWKEY_UPPER_BYTE);
// In order to make stopRow inclusive add a trailing 0 byte. #See Scan.setStopRow(byte [] stopRow)
this.stopKey = Bytes.add(encoder.encode(stopValues), ZERO_TAIL_BYTES);
// restore encoder defaults for later reuse (note AbstractRowKeyEncoder.createInstance() caches instances)
encoder.setBlankByte(AbstractRowKeyEncoder.DEFAULT_BLANK_BYTE);
// always fuzzy match cuboid ID to lock on the selected cuboid
this.fuzzyKeys = buildFuzzyKeys(fuzzyValues);
}
private void initPartitionRange(ColumnValueRange dimRange) {
if (null != dimRange.getBeginValue()) {
this.partitionColumnStartDate =
DateStrDictionary.stringToDate(dimRange.getBeginValue()).getTime();
}
if (null != dimRange.getEndValue()) {
this.partitionColumnEndDate = DateStrDictionary.stringToDate(dimRange.getEndValue()).getTime();
}
}
private void initDebugString() {
this.startKeyString = BytesUtil.toHex(this.startKey);
this.stopKeyString = BytesUtil.toHex(this.stopKey);
StringBuilder buf = new StringBuilder();
for (Pair<byte[], byte[]> fuzzyKey : this.fuzzyKeys) {
buf.append(BytesUtil.toHex(fuzzyKey.getFirst()));
buf.append(" ");
buf.append(BytesUtil.toHex(fuzzyKey.getSecond()));
}
this.fuzzyKeyString = buf.toString();
}
private List<Pair<byte[], byte[]>> buildFuzzyKeys(Map<TblColRef, Set<String>> fuzzyValueSet) {
ArrayList<Pair<byte[], byte[]>> result = new ArrayList<Pair<byte[], byte[]>>();
FuzzyKeyEncoder fuzzyKeyEncoder = new FuzzyKeyEncoder(cubeSeg, cuboid);
FuzzyMaskEncoder fuzzyMaskEncoder = new FuzzyMaskEncoder(cubeSeg, cuboid);
List<Map<TblColRef, String>> fuzzyValues =
FuzzyValueCombination.calculate(fuzzyValueSet, FUZZY_VALUE_CAP);
for (Map<TblColRef, String> fuzzyValue : fuzzyValues) {
result.add(new Pair<byte[], byte[]>(fuzzyKeyEncoder.encode(fuzzyValue), fuzzyMaskEncoder
.encode(fuzzyValue)));
}
return result;
}
public CubeSegment getCubeSegment() {
return this.cubeSeg;
}
public Cuboid getCuboid() {
return cuboid;
}
public byte[] getStartKey() {
return startKey;
}
public byte[] getStopKey() {
return stopKey;
}
public List<Pair<byte[], byte[]>> getFuzzyKeys() {
return fuzzyKeys;
}
public String getStartKeyAsString() {
return startKeyString;
}
public String getStopKeyAsString() {
return stopKeyString;
}
public String getFuzzyKeyAsString() {
return fuzzyKeyString;
}
public List<Collection<ColumnValueRange>> getFlatOrAndFilter() {
return flatOrAndFilter;
}
public long getPartitionColumnStartDate() {
return partitionColumnStartDate;
}
public long getPartitionColumnEndDate() {
return partitionColumnEndDate;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((cubeSeg == null) ? 0 : cubeSeg.hashCode());
result = prime * result + ((cuboid == null) ? 0 : cuboid.hashCode());
result = prime * result + ((fuzzyKeyString == null) ? 0 : fuzzyKeyString.hashCode());
result = prime * result + ((startKeyString == null) ? 0 : startKeyString.hashCode());
result = prime * result + ((stopKeyString == null) ? 0 : stopKeyString.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
HBaseKeyRange other = (HBaseKeyRange) obj;
if (cubeSeg == null) {
if (other.cubeSeg != null)
return false;
} else if (!cubeSeg.equals(other.cubeSeg))
return false;
if (cuboid == null) {
if (other.cuboid != null)
return false;
} else if (!cuboid.equals(other.cuboid))
return false;
if (fuzzyKeyString == null) {
if (other.fuzzyKeyString != null)
return false;
} else if (!fuzzyKeyString.equals(other.fuzzyKeyString))
return false;
if (startKeyString == null) {
if (other.startKeyString != null)
return false;
} else if (!startKeyString.equals(other.startKeyString))
return false;
if (stopKeyString == null) {
if (other.stopKeyString != null)
return false;
} else if (!stopKeyString.equals(other.stopKeyString))
return false;
return true;
}
@Override
public int compareTo(HBaseKeyRange other) {
return Bytes.compareTo(this.startKey, other.startKey);
}
public boolean hitSegment() {
return cubeSeg.getDateRangeStart() <= getPartitionColumnEndDate()
&& cubeSeg.getDateRangeEnd() >= getPartitionColumnStartDate();
}
}