com.yahoo.search.predicate.index.PredicateRangeTermExpander Maven / Gradle / Ivy
// Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.search.predicate.index;
import com.yahoo.document.predicate.PredicateHash;
/**
* Expands range terms from a query to find the set of features they translate to.
*
* @author bjorncs
* @author Magnar Nedland
*/
public class PredicateRangeTermExpander {
private final int arity;
private final int maxPositiveLevels;
private final int maxNegativeLevels;
private final long lowerBound;
private final long upperBound;
/**
* Creates a PredicateRangeTermExpander with default value range.
*
* @param arity The arity used to index the predicates
*/
public PredicateRangeTermExpander(int arity) {
this(arity, Long.MIN_VALUE, Long.MAX_VALUE);
}
/**
* @param arity The arity used to index the predicates
* @param lowerBound The minimum value used by any range predicate in the system
* @param upperBound The maximum value used by any range predicate in the system
*/
public PredicateRangeTermExpander(int arity, long lowerBound, long upperBound) {
this.arity = arity;
this.lowerBound = lowerBound;
this.upperBound = upperBound;
this.maxPositiveLevels = calculateMaxLevels(upperBound);
this.maxNegativeLevels = calculateMaxLevels(-lowerBound);
}
private int calculateMaxLevels(long t) {
int maxLevels = 1;
while ((t /= this.arity) != 0) {
maxLevels++;
}
return maxLevels;
}
/**
* Expands a range term to a set of features (ranges and edges) to be used in a query.
*
* @param key The term key
* @param value The term value
* @param rangeHandler Handler for range features (long)
* @param edgeHandler Handler for edge features (long, int)
*/
public void expand(String key, long value, RangeHandler rangeHandler, EdgeHandler edgeHandler) {
if (value < lowerBound || value > upperBound) {
// Value outside bounds -> expand to nothing.
return;
}
int maxLevels = value > 0 ? maxPositiveLevels : maxNegativeLevels;
int sign = value > 0 ? 1 : -1;
// Append key to feature string builder
StringBuilder builder = new StringBuilder(128);
builder.append(key).append('=');
long levelSize = arity;
long edgeInterval = (value / arity) * arity;
edgeHandler.handleEdge(createEdgeFeatureHash(builder, edgeInterval), (int) Math.abs(value - edgeInterval));
for (int i = 0; i < maxLevels; ++i) {
long start = (value / levelSize) * levelSize;
if (Math.abs(start) + levelSize - 1 < 0) { // overflow
break;
}
rangeHandler.handleRange(createRangeFeatureHash(builder, start, start + sign * (levelSize - 1)));
levelSize *= arity;
if (levelSize <= 0 && levelSize != Long.MIN_VALUE) { //overflow
break;
}
}
}
private long createRangeFeatureHash(StringBuilder builder, long start, long end) {
int prefixLength = builder.length();
String feature = end > 0
? builder.append(start).append('-').append(end).toString()
: builder.append(end).append('-').append(Math.abs(start)).toString();
builder.setLength(prefixLength);
return PredicateHash.hash64(feature);
}
private long createEdgeFeatureHash(StringBuilder builder, long edgeInterval) {
int prefixLength = builder.length();
String feature = builder.append(edgeInterval).toString();
builder.setLength(prefixLength);
return PredicateHash.hash64(feature);
}
/**
* Callback for ranges generated by the expansion.
*/
@FunctionalInterface
public interface RangeHandler {
void handleRange(long featureHash);
}
/**
* Callback for edges generated by the expansion.
*/
@FunctionalInterface
public interface EdgeHandler {
void handleEdge(long featureHash, int value);
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy