All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.yahoo.document.select.ResultList Maven / Gradle / Ivy

There is a newer version: 8.458.13
Show newest version
// 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