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

com.couchbase.client.java.query.N1qlParams Maven / Gradle / Ivy

/*
 * Copyright (c) 2016 Couchbase, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.couchbase.client.java.query;

import com.couchbase.client.core.annotations.InterfaceStability;
import com.couchbase.client.core.message.kv.MutationToken;
import com.couchbase.client.java.MutationState;
import com.couchbase.client.java.document.Document;
import com.couchbase.client.java.document.json.JsonArray;
import com.couchbase.client.java.document.json.JsonObject;
import com.couchbase.client.java.document.json.JsonValue;
import com.couchbase.client.java.query.dsl.functions.ObjectFunctions;
import com.couchbase.client.java.subdoc.DocumentFragment;
import com.couchbase.client.java.query.consistency.ScanConsistency;
import java.io.Serializable;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * Parameter Object for {@link N1qlQuery queries} that allows to fluently set most of the N1QL query parameters:
 *  - server side timeout
 *  - client context ID
 *  - scan consistency (with associated scan vector and/or scan wait if relevant)
 *  - max parallelism
 *
 * Note that these are different from statement-related named parameters or positional parameters.
 *
 * @author Simon Baslé
 * @since 2.1
 */
public class N1qlParams implements Serializable {

    private static final long serialVersionUID = 8888370260267213830L;

    private String serverSideTimeout;
    private ScanConsistency consistency;
    private String scanWait;
    private String clientContextId;
    private Integer maxParallelism;
    private boolean disableMetrics;
    private MutationState mutationState;
    private Map rawParams;

    /**
     * If adhoc, the query should never be prepared.
     */
    private boolean adhoc;

    private N1qlParams() {
        adhoc = true;
        disableMetrics = false;
    }

    /**
     * Modifies the given N1QL query (as a {@link JsonObject}) to reflect these {@link N1qlParams}.
     * @param queryJson the N1QL query
     */
    public void injectParams(JsonObject queryJson) {
        if (this.serverSideTimeout != null) {
            queryJson.put("timeout", this.serverSideTimeout);
        }
        if (this.consistency != null) {
            queryJson.put("scan_consistency", this.consistency.n1ql());
        }
        if (this.scanWait != null
                && (ScanConsistency.REQUEST_PLUS == this.consistency
                || ScanConsistency.STATEMENT_PLUS == this.consistency)) {
            queryJson.put("scan_wait", this.scanWait);
        }
        if (this.clientContextId != null) {
            queryJson.put("client_context_id", this.clientContextId);
        }
        if (this.maxParallelism != null) {
            queryJson.put("max_parallelism", this.maxParallelism.toString());
        }
        if (this.disableMetrics) {
            queryJson.put("metrics", false);
        }

        if (this.mutationState != null) {
            if (this.consistency != null) {
                throw new IllegalArgumentException("`consistency(...)` cannot be used "
                    + "together with `consistentWith(...)`");
            }
            queryJson.put("scan_vectors", mutationState.export());
            queryJson.put("scan_consistency", "at_plus");
        }

        if (this.rawParams != null) {
            for (Map.Entry entry : rawParams.entrySet()) {
                queryJson.put(entry.getKey(), entry.getValue());
            }
        }
    }

    private static String durationToN1qlFormat(long duration, TimeUnit unit) {
        switch (unit) {
            case NANOSECONDS:
                return duration + "ns";
            case MICROSECONDS:
                return duration + "us";
            case MILLISECONDS:
                return duration + "ms";
            case SECONDS:
                return duration + "s";
            case MINUTES:
                return duration + "m";
            case HOURS:
                return duration + "h";
            case DAYS:
            default:
                return unit.toHours(duration) + "h";
        }
    }

    /**
     * Start building a {@link N1qlParams}, allowing to customize an N1QL request.
     *
     * @return a new {@link N1qlParams}
     */
    public static N1qlParams build() {
        return new N1qlParams();
    }

    /**
     * Sets a maximum timeout for processing on the server side.
     *
     * @param timeout the duration of the timeout.
     * @param unit the unit of the timeout, from nanoseconds to hours.
     * @return this {@link N1qlParams} for chaining.
     */
    public N1qlParams serverSideTimeout(long timeout, TimeUnit unit) {
        this.serverSideTimeout = durationToN1qlFormat(timeout, unit);
        return this;
    }

    /**
     * Adds a client context ID to the request, that will be sent back in the response, allowing clients
     * to meaningfully trace requests/responses when many are exchanged.
     *
     * @param clientContextId the client context ID (null to send none)
     * @return this {@link N1qlParams} for chaining.
     */
    public N1qlParams withContextId(String clientContextId) {
        this.clientContextId = clientContextId;
        return this;
    }

    /**
     * If set to true (false being the default), the metrics object will not be returned from N1QL and
     * as a result be more efficient. Note that if metrics are disabled you are loosing information
     * to diagnose problems - so use with care!
     *
     * @param disableMetrics true if disabled, false otherwise (false = default).
     * @return this {@link N1qlParams} for chaining.
     */
    public N1qlParams disableMetrics(boolean disableMetrics) {
        this.disableMetrics = disableMetrics;
        return this;
    }

    /**
     * Sets scan consistency.
     *
     * Note that {@link ScanConsistency#NOT_BOUNDED NOT_BOUNDED} will unset the {@link #scanWait} if it was set.
     *
     * @return this {@link N1qlParams} for chaining.
     */
    public N1qlParams consistency(ScanConsistency consistency) {
        this.consistency = consistency;
        if (consistency == ScanConsistency.NOT_BOUNDED) {
            this.scanWait = null;
        }
        return this;
    }

    /**
     * Sets the {@link Document}s resulting of a mutation this query should be consistent with.
     *
     * @param documents the documents returned from a mutation.
     * @return this {@link N1qlParams} for chaining.
     */
    @InterfaceStability.Experimental
    public N1qlParams consistentWith(Document... documents) {
        return consistentWith(MutationState.from(documents));
    }

    /**
     * Sets the {@link DocumentFragment}s resulting of a mutation this query should be consistent with.
     *
     * @param fragments the fragments returned from a mutation.
     * @return this {@link N1qlParams} for chaining.
     */
    @InterfaceStability.Experimental
    public N1qlParams consistentWith(DocumentFragment... fragments) {
        return consistentWith(MutationState.from(fragments));
    }

    /**
     * Sets the {@link MutationState} this query should be consistent with.
     *
     * @param mutationState the mutation state which accumulates tokens from one or more mutation results.
     * @return this {@link N1qlParams} for chaining.
     */
    @InterfaceStability.Experimental
    public N1qlParams consistentWith(MutationState mutationState) {
        this.mutationState = mutationState;
        return this;
    }

    /**
     * If the {@link ScanConsistency#NOT_BOUNDED NOT_BOUNDED scan consistency} has been chosen, does nothing.
     *
     * Otherwise, sets the maximum time the client is willing to wait for an index to catch up to the
     * vector timestamp in the request.
     *
     * @param wait the duration.
     * @param unit the unit for the duration.
     * @return this {@link N1qlParams} for chaining.
     */
    public N1qlParams scanWait(long wait, TimeUnit unit) {
        if (this.consistency == ScanConsistency.NOT_BOUNDED) {
            this.scanWait = null;
        } else {
            this.scanWait = durationToN1qlFormat(wait, unit);
        }
        return this;
    }

    /**
     * Allows to override the default maximum parallelism for the query execution on the server side.
     *
     * @param maxParallelism the maximum parallelism for this query, 0 or negative values disable it.
     * @return this {@link N1qlParams} for chaining.
     */
    public N1qlParams maxParallelism(int maxParallelism) {
        this.maxParallelism = maxParallelism;
        return this;
    }


    /**
     * Allows to specify if this query is adhoc or not.
     *
     * If it is not adhoc (so performed often), the client will try to perform optimizations
     * transparently based on the server capabilities, like preparing the statement and
     * then executing a query plan instead of the raw query.
     *
     * @param adhoc if the query is adhoc, default is true (plain execution).
     * @return this {@link N1qlParams} for chaining.
     */
    public N1qlParams adhoc(boolean adhoc) {
        this.adhoc = adhoc;
        return this;
    }

    /**
     * Allows to specify an arbitrary, raw N1QL param.
     *
     * Use with care and only provide options that are supported by the server and are not exposed as part of the
     * overall stable API in the {@link N1qlParams} class.
     *
     * @param name the name of the property.
     * @param value the value of the property, only JSON value types are supported.
     * @return this {@link N1qlParams} for chaining.
     */
    @InterfaceStability.Uncommitted
    public N1qlParams rawParam(String name, Object value) {
        if (this.rawParams == null) {
            this.rawParams = new HashMap();
        }

        if (!JsonValue.checkType(value)) {
            throw new IllegalArgumentException("Only JSON types are supported.");
        }

        rawParams.put(name, value);
        return this;
    }

    /**
     * Helper method to check if a custom server side timeout has been applied on the params.
     *
     * @return true if it has, false otherwise.
     */
    public boolean hasServerSideTimeout() {
        return serverSideTimeout != null;
    }

    /**
     * True if this query is adhoc, false otherwise.
     *
     * @return true if adhoc false otherwise.
     */
    public boolean isAdhoc() {
        return adhoc;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        N1qlParams that = (N1qlParams) o;

        if (disableMetrics != that.disableMetrics) return false;
        if (adhoc != that.adhoc) return false;
        if (serverSideTimeout != null ? !serverSideTimeout.equals(that.serverSideTimeout) : that.serverSideTimeout != null)
            return false;
        if (consistency != that.consistency) return false;
        if (scanWait != null ? !scanWait.equals(that.scanWait) : that.scanWait != null) return false;
        if (clientContextId != null ? !clientContextId.equals(that.clientContextId) : that.clientContextId != null)
            return false;
        if (maxParallelism != null ? !maxParallelism.equals(that.maxParallelism) : that.maxParallelism != null)
            return false;
        if (mutationState != null ? !mutationState.equals(that.mutationState) : that.mutationState != null)
            return false;
        return rawParams != null ? rawParams.equals(that.rawParams) : that.rawParams == null;

    }

    @Override
    public int hashCode() {
        int result = serverSideTimeout != null ? serverSideTimeout.hashCode() : 0;
        result = 31 * result + (consistency != null ? consistency.hashCode() : 0);
        result = 31 * result + (scanWait != null ? scanWait.hashCode() : 0);
        result = 31 * result + (clientContextId != null ? clientContextId.hashCode() : 0);
        result = 31 * result + (maxParallelism != null ? maxParallelism.hashCode() : 0);
        result = 31 * result + (disableMetrics ? 1 : 0);
        result = 31 * result + (mutationState != null ? mutationState.hashCode() : 0);
        result = 31 * result + (rawParams != null ? rawParams.hashCode() : 0);
        result = 31 * result + (adhoc ? 1 : 0);
        return result;
    }

    @Override
    public String toString() {
        final StringBuilder sb = new StringBuilder("N1qlParams{");
        sb.append("serverSideTimeout='").append(serverSideTimeout).append('\'');
        sb.append(", consistency=").append(consistency);
        sb.append(", scanWait='").append(scanWait).append('\'');
        sb.append(", clientContextId='").append(clientContextId).append('\'');
        sb.append(", maxParallelism=").append(maxParallelism);
        sb.append(", adhoc=").append(adhoc);
        sb.append(", disableMetrics=").append(disableMetrics);
        sb.append(", rawParams=").append(rawParams);
        sb.append('}');
        return sb.toString();
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy