diff options
Diffstat (limited to 'predicate-search/src/main/java/com/yahoo/search/predicate/index/PredicateRangeTermExpander.java')
-rw-r--r-- | predicate-search/src/main/java/com/yahoo/search/predicate/index/PredicateRangeTermExpander.java | 116 |
1 files changed, 116 insertions, 0 deletions
diff --git a/predicate-search/src/main/java/com/yahoo/search/predicate/index/PredicateRangeTermExpander.java b/predicate-search/src/main/java/com/yahoo/search/predicate/index/PredicateRangeTermExpander.java new file mode 100644 index 00000000000..290c81c2ca8 --- /dev/null +++ b/predicate-search/src/main/java/com/yahoo/search/predicate/index/PredicateRangeTermExpander.java @@ -0,0 +1,116 @@ +// Copyright 2016 Yahoo Inc. 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 <a href="mailto:magnarn@yahoo-inc.com">Magnar Nedland</a> + */ +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); + } +} |