com.yahoo.document.select.ResultList 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.document.select;
import com.yahoo.document.datatypes.FieldPathIteratorHandler;
import com.yahoo.document.select.rule.AttributeNode;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;
import java.util.Map;
import java.util.function.BiFunction;
public class ResultList {
public static class ResultPair {
ResultPair(FieldPathIteratorHandler.VariableMap var, Result res) {
variables = var;
result = res;
}
FieldPathIteratorHandler.VariableMap variables;
Result result;
public FieldPathIteratorHandler.VariableMap getVariables() { return variables; }
public Result getResult() { return result; }
public String toString() {
return variables.toString() + " => " + result;
}
}
public static class VariableValue {
public VariableValue(FieldPathIteratorHandler.VariableMap vars, Object value) {
variables = vars;
this.value = value;
}
FieldPathIteratorHandler.VariableMap variables;
Object value;
public FieldPathIteratorHandler.VariableMap getVariables() { return variables; }
public Object getValue() { return value; }
public String toString() {
return variables.toString() + " => " + value;
}
}
private final List results = new ArrayList<>();
public ResultList() {
}
public ResultList(Result result) {
add(new FieldPathIteratorHandler.VariableMap(), result);
}
public void add(FieldPathIteratorHandler.VariableMap variables, Result result) {
results.add(new ResultPair(variables, result));
}
public List getResults() {
return results;
}
public static ResultList fromBoolean(boolean result) {
return new ResultList(result ? Result.TRUE : Result.FALSE);
}
public Result toResult() {
if (results.isEmpty()) {
return Result.FALSE;
}
boolean foundFalse = false;
for (ResultPair rp : results) {
if (rp.result == Result.TRUE) {
return Result.TRUE;
} else if (rp.result == Result.FALSE) {
foundFalse = true;
}
}
if (foundFalse) {
return Result.FALSE;
} else {
return Result.INVALID;
}
}
private static boolean combineVariables(FieldPathIteratorHandler.VariableMap output, FieldPathIteratorHandler.VariableMap input) {
// First, verify that all variables are overlapping
for (Map.Entry entry : output.entrySet()) {
FieldPathIteratorHandler.IndexValue found = input.get(entry.getKey());
if (found != null) {
if (!(found.equals(entry.getValue()))) {
return false;
}
}
}
for (Map.Entry entry : input.entrySet()) {
FieldPathIteratorHandler.IndexValue found = output.get(entry.getKey());
if (found != null) {
if (!(found.equals(entry.getValue()))) {
return false;
}
}
}
// Ok, variables are overlapping. Add all variables from input to output.
for (Map.Entry entry : input.entrySet()) {
output.put(entry.getKey(), entry.getValue());
}
return true;
}
public interface LazyResultList {
ResultList getResult();
}
public ResultList combineAND(LazyResultList other) {
if (Result.FALSE == toResult()){
return ResultList.toResultList(false);
}
return combineBinaryOp(other, ResultList::combineAND);
}
private static Result combineAND(Result lhs, Result rhs) {
if (lhs == Result.TRUE && rhs == Result.TRUE) {
return Result.TRUE;
}
if (lhs == Result.FALSE || rhs == Result.FALSE) {
return Result.FALSE;
}
return Result.INVALID;
}
public ResultList combineOR(LazyResultList other) {
if (Result.TRUE == toResult()) {
return ResultList.toResultList(true);
}
return combineBinaryOp(other, ResultList::combineOR);
}
private ResultList combineBinaryOp(LazyResultList other, BiFunction op) {
var result = new ResultList();
// TODO: optimize further
var observedNoVarResults = EnumSet.noneOf(Result.class);
for (ResultPair pair : results) {
for (ResultPair otherPair : other.getResult().results) {
// If possible, collapse all non-variable results from O(nm) entries down to O(1).
// This the expected common case.
if (pair.variables.isEmpty() && otherPair.variables.isEmpty()) {
// combineVariables(lhs, rhs) is always true when both sides are empty, so we can
// elide that check entirely here.
observedNoVarResults.add(op.apply(pair.result, otherPair.result));
} else {
var varMap = (FieldPathIteratorHandler.VariableMap) pair.variables.clone();
if (combineVariables(varMap, otherPair.variables)) {
result.add(varMap, op.apply(pair.result, otherPair.result));
}
}
}
}
for (var mergedResult : observedNoVarResults) {
result.add(new FieldPathIteratorHandler.VariableMap(), mergedResult); // TODO sentinel empty var map
}
return result;
}
private static Result combineOR(Result lhs, Result rhs) {
if (lhs == Result.TRUE || rhs == Result.TRUE) {
return Result.TRUE;
}
if (lhs == Result.FALSE && rhs == Result.FALSE) {
return Result.FALSE;
}
return Result.INVALID;
}
/**
* Converts the given object value into a result list, so it can be compared by logical operators.
*
* @param value The object to convert.
* @return The corresponding result value.
*/
public static ResultList toResultList(Object value) {
if (value instanceof ResultList) {
return (ResultList)value;
} else if (value instanceof AttributeNode.VariableValueList) {
ResultList retVal = new ResultList();
for (VariableValue vv : (AttributeNode.VariableValueList)value) {
retVal.add(vv.getVariables(), Result.TRUE);
}
return retVal;
} else if (value == null || value == Result.FALSE || value == Boolean.FALSE ||
(value instanceof Number && ((Number)value).doubleValue() == 0)) {
return new ResultList(Result.FALSE);
} else if (value == Result.INVALID) {
return new ResultList(Result.INVALID);
} else {
return new ResultList(Result.TRUE);
}
}
public String toString() {
return results.toString();
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy