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

ai.vespa.client.dsl.Query Maven / Gradle / Ivy

There is a newer version: 8.443.12
Show newest version
// Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package ai.vespa.client.dsl;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * Vespa query object
 */
public class Query extends QueryChain {

    final List queries = new ArrayList<>();
    private Annotation annotation;

    Query(Sources sources, QueryChain queryChain) {
        this.sources = sources;
        this.queries.add(queryChain);
        this.nonEmpty = queryChain.nonEmpty;
    }

    Query(Sources sources) {
        this.sources = sources;
    }

    String toCommaSeparatedAndQueries() {
        return queries.stream()
            .filter(qc -> "and".equals(qc.getOp()))
            .map(Objects::toString)
            .collect(Collectors.joining(", "));
    }

    @Override
    public String toString() {
        // TODO: need to refactor
        if (!nonEmpty) {
            return "";
        }

        boolean hasAnnotation = A.hasAnnotation(annotation);

        StringBuilder sb = new StringBuilder();

        if (hasAnnotation) {
            sb.append("([").append(annotation).append("](");
        }

        boolean firstQuery = true;
        for (int i = 0; i < queries.size(); i++) {
            QueryChain qc = queries.get(i);
            if (!qc.nonEmpty) {
                continue;
            }

            boolean isNotAnd = "andnot".equals(qc.getOp());

            if (!firstQuery) {
                sb.append(" ");
                if (isNotAnd) {
                    sb.append("and !");
                } else {
                    sb.append(qc.getOp()).append(' ');
                }
            } else {
                firstQuery = false;
            }

            boolean appendBrackets =
                (qc instanceof Query && ((Query) qc).queries.size() > 1 && !A.hasAnnotation(((Query) qc).annotation))
                || isNotAnd;
            if (appendBrackets) {
                sb.append("(");
            }

            sb.append(qc);

            if (appendBrackets) {
                sb.append(")");
            }

        }

        if (hasAnnotation) {
            sb.append("))");
        }
        return sb.toString().trim();
    }

    /**
     * And.
     * https://docs.vespa.ai/en/reference/query-language-reference.html#and
     *
     * @param fieldName the field name
     * @return the field
     */
    public Field and(String fieldName) {
        Field f = new Field(this, fieldName);
        f.setOp("and");
        queries.add(f);
        nonEmpty = true;
        return f;
    }

    /**
     * Andnot.
     * https://docs.vespa.ai/en/reference/query-language-reference.html#andnot
     *
     * @param fieldName the field name
     * @return the field
     */
    public Field andnot(String fieldName) {
        Field f = new Field(this, fieldName);
        f.setOp("andnot");
        queries.add(f);
        nonEmpty = true;
        return f;
    }

    /**
     * Or.
     * https://docs.vespa.ai/en/reference/query-language-reference.html#or
     *
     * @param fieldName the field name
     * @return the field
     */
    public Field or(String fieldName) {
        Field f = new Field(this, fieldName);
        f.setOp("or");
        queries.add(f);
        nonEmpty = true;
        return f;
    }

    /**
     * And.
     * https://docs.vespa.ai/en/reference/query-language-reference.html#and
     *
     * @param query the query
     * @return the query
     */
    public Query and(QueryChain query) {
        query.setOp("and");
        queries.add(query);
        nonEmpty = nonEmpty || query.nonEmpty;
        return this;
    }

    /**
     * Andnot.
     * https://docs.vespa.ai/en/reference/query-language-reference.html#andnot
     *
     * @param query the query
     * @return the query
     */
    public Query andnot(QueryChain query) {
        query.setOp("andnot");
        queries.add(query);
        nonEmpty = nonEmpty || query.nonEmpty;
        return this;
    }

    /**
     * Or.
     * https://docs.vespa.ai/en/reference/query-language-reference.html#or
     *
     * @param query the query
     * @return the query
     */
    public Query or(QueryChain query) {
        query.setOp("or");
        queries.add(query);
        nonEmpty = nonEmpty || query.nonEmpty;
        return this;
    }

    /**
     * Annotate a query (sub-expression).
     * https://docs.vespa.ai/en/reference/query-language-reference.html#annotations-of-sub-expressions
     *
     * @param annotation the annotation
     * @return the query
     */
    public Query annotate(Annotation annotation) {
        this.annotation = annotation;
        return this;
    }

    /**
     * Offset.
     * https://docs.vespa.ai/en/reference/query-language-reference.html#limit-offset
     *
     * @param offset the offset
     * @return the end query
     */
    public EndQuery offset(int offset) {
        return new EndQuery(this).offset(offset);
    }

    /**
     * Limit.
     * https://docs.vespa.ai/en/reference/query-language-reference.html#limit-offset
     *
     * @param hits the hits
     * @return the end query
     */
    public EndQuery limit(int hits) {
        return new EndQuery(this).limit(hits);
    }

    /**
     * Timeout.
     * https://docs.vespa.ai/en/reference/query-language-reference.html#timeout
     *
     * @param timeout the timeout
     * @return the end query
     */
    public EndQuery timeout(int timeout) {
        return new EndQuery(this).timeout(timeout);
    }

    /**
     * Group.
     * https://docs.vespa.ai/en/reference/query-language-reference.html#grouping
     *
     * @param group the group
     * @return the end query
     */
    public EndQuery group(Group group) {
        return new EndQuery(this).group(group);
    }

    /**
     * Group.
     * https://docs.vespa.ai/en/reference/query-language-reference.html#grouping
     *
     * @param groupStr the group str
     * @return the end query
     */
    public EndQuery group(String groupStr) {
        return new EndQuery(this).group(groupStr);
    }

    /**
     * Order by asc.
     * https://docs.vespa.ai/en/reference/query-language-reference.html#order-by
     *
     * @param fieldName the field name
     * @return the end query
     */
    public EndQuery orderByAsc(String fieldName) {
        return new EndQuery(this).orderByAsc(fieldName);
    }

    /**
     * Order by asc.
     * https://docs.vespa.ai/en/reference/query-language-reference.html#order-by
     *
     * @param annotation the annotation
     * @param fieldName the field name
     * @return the end query
     */
    public EndQuery orderByAsc(Annotation annotation, String fieldName) {
        return new EndQuery(this).orderByAsc(annotation, fieldName);
    }

    /**
     * Order by desc.
     * https://docs.vespa.ai/en/reference/query-language-reference.html#order-by
     *
     * @param fieldName the field name
     * @return the end query
     */
    public EndQuery orderByDesc(String fieldName) {
        return new EndQuery(this).orderByDesc(fieldName);
    }


    /**
     * Order by desc.
     * https://docs.vespa.ai/en/reference/query-language-reference.html#order-by
     *
     * @param annotation the annotation
     * @param fieldName the field name
     * @return the end query
     */
    public EndQuery orderByDesc(Annotation annotation, String fieldName) {
        return new EndQuery(this).orderByDesc(annotation, fieldName);
    }

    /**
     * Calls fix()
     *
     * @return the fixed query
     * @deprecated use {@link #fix()}, {@link #end()} or {@link #build} instead
     */
    @Deprecated // TODO: Remove on Vespa 9
    public FixedQuery semicolon() { return fix(); }

    /** Returns this as an ended query. */
    public EndQuery end() {
        return new EndQuery(this);
    }

    /** Calls end().fix(). */
    public FixedQuery fix() {
        return end().fix();
    }

    /** Calls fix().build(). */
    public String build() {
        return fix().build();
    }

    @Override
    public Sources getSources() {
        return sources;
    }

    @Override
    public void setSources(Sources sources) {
        this.sources = sources;
    }

    @Override
    public Query getQuery() {
        return this;
    }

    @Override
    public Select getSelect() {
        return sources.select;
    }

    @Override
    public boolean hasPositiveSearchField(String fieldName) {
        boolean hasPositiveInSubqueries = queries.stream().anyMatch(q -> q.hasPositiveSearchField(fieldName));
        boolean hasNegativeInSubqueries = queries.stream().anyMatch(q -> q.hasNegativeSearchField(fieldName));
        return nonEmpty
               && ((!"andnot".equals(this.op) && hasPositiveInSubqueries)
                   || ("andnot".equals(this.op) && hasNegativeInSubqueries));
    }


    @Override
    public boolean hasPositiveSearchField(String fieldName, Object value) {
        boolean hasPositiveInSubqueries = queries.stream().anyMatch(q -> q.hasPositiveSearchField(fieldName, value));
        boolean hasNegativeInSubqueries = queries.stream().anyMatch(q -> q.hasNegativeSearchField(fieldName, value));
        return nonEmpty &&
               (!"andnot".equals(this.op) && hasPositiveInSubqueries)
               || ("andnot".equals(this.op) && hasNegativeInSubqueries);
    }

    @Override
    public boolean hasNegativeSearchField(String fieldName) {
        boolean hasPositiveInSubqueries = queries.stream().anyMatch(q -> q.hasPositiveSearchField(fieldName));
        boolean hasNegativeInSubqueries = queries.stream().anyMatch(q -> q.hasNegativeSearchField(fieldName));
        return nonEmpty
               && (!"andnot".equals(this.op) && hasNegativeInSubqueries)
               || ("andnot".equals(this.op) && hasPositiveInSubqueries);
    }

    @Override
    public boolean hasNegativeSearchField(String fieldName, Object value) {
        boolean hasPositiveInSubqueries = queries.stream().anyMatch(q -> q.hasPositiveSearchField(fieldName, value));
        boolean hasNegativeInSubqueries = queries.stream().anyMatch(q -> q.hasNegativeSearchField(fieldName, value));
        return nonEmpty
               && (!"andnot".equals(this.op) && hasNegativeInSubqueries)
               || ("andnot".equals(this.op) && hasPositiveInSubqueries);
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy