com.yahoo.search.predicate.optimization.FeatureConjunctionTransformer 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.optimization;
import com.yahoo.document.predicate.Conjunction;
import com.yahoo.document.predicate.Disjunction;
import com.yahoo.document.predicate.FeatureConjunction;
import com.yahoo.document.predicate.FeatureRange;
import com.yahoo.document.predicate.FeatureSet;
import com.yahoo.document.predicate.Negation;
import com.yahoo.document.predicate.Predicate;
import com.yahoo.search.predicate.index.conjunction.ConjunctionIndex;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
/**
* Transforms Conjunctions with only (negated) {@link FeatureSet} instances to {@link FeatureConjunction}.
* The {@link FeatureConjunction}s are indexed by the {@link ConjunctionIndex}.
*
* @author bjorncs
*/
public class FeatureConjunctionTransformer implements PredicateProcessor {
// Only Conjunctions having less or equal number of FeatureSet operands than threshold are converted to FeatureConjunction.
private static final int CONVERSION_THRESHOLD = Integer.MAX_VALUE;
private final boolean useConjunctionAlgorithm;
public FeatureConjunctionTransformer(boolean useConjunctionAlgorithm) {
this.useConjunctionAlgorithm = useConjunctionAlgorithm;
}
@Override
public Predicate process(Predicate predicate, PredicateOptions options) {
if (useConjunctionAlgorithm) {
return transform(predicate);
}
return predicate;
}
private static Predicate transform(Predicate predicate) {
if (predicate instanceof Conjunction) {
Conjunction conjunction = (Conjunction) predicate;
conjunction.getOperands().replaceAll(FeatureConjunctionTransformer::transform);
long nValidOperands = numberOfValidFeatureSetOperands(conjunction);
if (nValidOperands > 1 && nValidOperands <= CONVERSION_THRESHOLD) {
return convertConjunction(conjunction, nValidOperands);
}
} else if (predicate instanceof Disjunction) {
((Disjunction)predicate).getOperands().replaceAll(FeatureConjunctionTransformer::transform);
} else if (predicate instanceof Negation) {
Negation negation = (Negation) predicate;
negation.setOperand(transform(negation.getOperand()));
}
return predicate;
}
/**
* Conversion rules:
* 1) A {@link FeatureConjunction} may only consist of FeatureSets having unique keys.
* If multiple {@link FeatureSet} share the same key, they have to be placed into separate FeatureConjunctions.
* 2) A FeatureConjunction must have at least 2 operands.
* 3) Any operand that is not a FeatureSet, negated or not,
* (e.g {@link FeatureRange}) cannot be placed into a FeatureConjunction.
* 4) All FeatureSets may only have a single value.
*
* See the tests in FeatureConjunctionTransformerTest for conversion examples.
*/
private static Predicate convertConjunction(Conjunction conjunction, long nValidOperands) {
List operands = conjunction.getOperands();
// All operands are instance of FeatureSet are valid and may therefor be placed into a single FeatureConjunction.
if (nValidOperands == operands.size()) {
return new FeatureConjunction(operands);
}
List invalidFeatureConjunctionOperands = new ArrayList<>();
List
© 2015 - 2024 Weber Informatics LLC | Privacy Policy