Package ru.dreamteam.couch.query

Source Code of ru.dreamteam.couch.query.Query

package ru.dreamteam.couch.query;

import static ru.dreamteam.couch.CouchConstants.DESCENDING;
import static ru.dreamteam.couch.CouchConstants.END_KEY;
import static ru.dreamteam.couch.CouchConstants.GROUP;
import static ru.dreamteam.couch.CouchConstants.INCLUDE_DOCS;
import static ru.dreamteam.couch.CouchConstants.KEY;
import static ru.dreamteam.couch.CouchConstants.LIMIT;
import static ru.dreamteam.couch.CouchConstants.SKIP;
import static ru.dreamteam.couch.CouchConstants.START_DOCID;
import static ru.dreamteam.couch.CouchConstants.START_KEY;

import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;

import javax.xml.crypto.MarshalException;

import org.apache.http.HttpRequest;
import org.apache.http.HttpResponse;
import org.apache.http.StatusLine;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.utils.URIBuilder;
import static org.apache.http.HttpStatus.*;

import ru.dreamteam.couch.Couch;
import ru.dreamteam.couch.Db;
import ru.dreamteam.couch.HttpCall;
import ru.dreamteam.couch.MarshallingException;
import ru.dreamteam.couch.PagingResult;
import ru.dreamteam.couch.util.JSONUtils;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectReader;
import com.fasterxml.jackson.databind.ObjectWriter;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.node.ArrayNode;

/**
* Base class for a couchdb query. Use {@link Db#query(String, String, Class)} to create a new query.
* All options represent couch db query parameters - http://wiki.apache.org/couchdb/HTTP_view_API#Querying_Options
*
* User: DPokidov
* Date: 02.01.14
*/
public class Query<T> {
    private final Logger log = Logger.getLogger(Query.class.getName());

    private Class<T> clazz;
    private Couch dbInstance;
    private String uriPath;
    private ObjectMapper mapper;
    private ObjectWriter jsonWriter;
    private ObjectReader jsonReader;
    private Map<String, String> queryParams;

    /**
     * Create a new query to a database.
     * Common usage case is to using {@link Db#query(String, String, Class)} method to
     * create query.
     * @param dbInstance database instance.
     * @param uriPath path to view.
     * @param clazz result class.
     */
    public Query(Couch dbInstance, String uriPath, Class<T> clazz) {
        this.clazz = clazz;
        this.dbInstance = dbInstance;
        this.uriPath = uriPath;
        this.mapper = JSONUtils.createMapper();
        this.jsonWriter = mapper.writer(SerializationFeature.WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED);
        this.jsonReader = mapper.reader(clazz);
        this.queryParams = new HashMap<>();
    }

    /**
     * Sets a start key for a query. Accepts array if a key in a view is complex.
     * Note: if you need to sort result descending then you must swap your start key and end key.<br/>
     * HINT: If you need to add empty key({}) then you can use {@code new Object()} as a key
     * @param startKey start key for the query.
     * @return current query.
     */
    public Query<T> startKey(Object... startKey) {
        putArrayQueryParam(START_KEY, startKey);
        return this;
    }

    /**
     * Sets a start key for a query. Accepts array if a key in a view is complex.
     * Note: if you need to sort result descending then you must swap your start key and end key.<br/>
     * HINT: If you need to add empty key({}) then you can use {@code new Object()} as a key
     * @param startKey end key for the query.
     * @return current query.
     */
    public Query<T> endKey(Object... endKey) {
        putArrayQueryParam(END_KEY, endKey);
        return this;
    }

    /**
     * Sets a key for a query. Accepts array if a key in a view is complex.
     * @param key key for the query.
     * @return current query.
     */
    public Query<T> key(Object... key) {
        putArrayQueryParam(KEY, key);
        return this;
    }

    private void putArrayQueryParam(String paramName, Object... values) {
        if (values != null && values.length > 0) {
            try {
                queryParams.put(paramName, jsonWriter.writeValueAsString(values));
            } catch (JsonProcessingException e) {
                throw new MarshallingException("Error while serialize [" + values.toString() + "]", e);
            }
        }
    }

    /**
     * Sets a number of documents to skip from beginning of result.
     * @param skip number of records to skip. Must be greater then 0.
     * @return current query.
     */
    public Query<T> skip(int skip) {
        if (skip > 0) {
            queryParams.put(SKIP, Integer.toString(skip));
        }
        return this;
    }

    /**
     * Sets a limit for a number of documents that will be returned.
     * @param limit maximum number of document.
     * @return current query.
     */
    public Query<T> limit(int limit) {
        if (limit > 0) {
            queryParams.put(LIMIT, Integer.toString(limit));
        }
        return this;
    }

    /**
     * Sets a document id from which result will begin.
     * All documents before this will be ignored.
     * @param startKeyDocId Start document id. Must not be {@code null}
     * @return current query.
     */
    public Query<T> startKeyDocId(String startKeyDocId) {
        if (startKeyDocId != null) {
            queryParams.put(START_DOCID, startKeyDocId);
        }
        return this;
    }

    /**
     * Sets a direction of sorting documents.<br/>
     * By default documents sorting ascending by change key.
     * @param descending New direction of sorting. {@code true} for descending, {@code false} for ascending.
     * @return current query.
     */
    public Query<T> descending(boolean descending) {
        queryParams.put(DESCENDING, Boolean.toString(descending));
        return this;
    }

    /**
     * If group param is true, then CouchDb will call reduce function in view.
     * @param group Group param
     * @return current query
     */
    public Query<T> group(boolean group) {
        queryParams.put(GROUP, Boolean.toString(group));
        return this;
    }

    /**
     * If includeDocs param set to {@code true} then CouchDb will include documents in response.<br/>
     * By default set to {@code true}
     * @param includeDocs IncludeDocs param
     * @return current query
     */
    public Query<T> includeDocs(boolean includeDocs) {
        queryParams.put(INCLUDE_DOCS, Boolean.toString(includeDocs));
        return this;
    }

    /**
     * Executes query and returns a list of result documents.
     * @return result documents
     */
    public PagingResult<T> list() {
        return dbInstance.execute(new HttpCall<PagingResult<T>>() {
            @Override
            public HttpRequest getRequest() throws URISyntaxException {
                return new HttpGet(buildQueryUri());
            }

            @Override
            public PagingResult<T> doWithResponse(HttpResponse response) throws IOException {
                if (response.getStatusLine().getStatusCode() == SC_NOT_FOUND) {
                    throw new CouchQueryException("View [" + uriPath + "] does not exists");
                }

                PagingResult<T> result = new PagingResult<>();
                JsonNode node = mapper.readTree(response.getEntity().getContent());
                if (node.get("total_rows") != null) {
                    result.setTotalRows(node.get("total_rows").asInt());
                }
                ArrayNode rowsNode = (ArrayNode) node.get("rows");
                for (JsonNode aRowsNode : rowsNode) {
                    JsonNode valueNode = aRowsNode.has("doc") ? aRowsNode.get("doc") : aRowsNode.get("value");
                    if (!clazz.getName().equals(String.class.getName())) {
                        result.add((T) jsonReader.readValue(valueNode));
                    } else {
                        result.add((T) valueNode.toString());
                    }
                }
                return result;
            }
        }, SC_OK, SC_NOT_FOUND);
    }

    /**
     * Return number of documents for current query.
     * @return number of documents
     */
    public int count() {
        return dbInstance.execute(new HttpCall<Integer>() {
            @Override
            public HttpRequest getRequest() throws URISyntaxException {
                return new HttpGet(buildQueryUri());
            }

            @Override
            public Integer doWithResponse(HttpResponse response) throws IOException {
                if (response.getStatusLine().getStatusCode() == SC_NOT_FOUND) {
                    throw new CouchQueryException("View [" + uriPath + "] does not exists");
                }

                PagingResult<T> result = new PagingResult<>();
                JsonNode node = mapper.readTree(response.getEntity().getContent());
                if (node.get("total_rows") != null) {
                    result.setTotalRows(node.get("total_rows").asInt());
                }
                ArrayNode rowsNode = (ArrayNode) node.get("rows");
                return rowsNode.size();
            }
        }, SC_OK, SC_NOT_FOUND);
    }

    /**
     * Expects only one object in a query result
     * Shorthand for {@code list().get(0)}
     * @return result document or {@code null} if object not found
     * @throws CouchQueryException if more then one object in result
     */
    public T one() throws CouchQueryException {
        List<T> list = list();
        if (list.size() > 1) {
            throw new CouchQueryException("Expected 1 object in result, actually [" + list.size() + "]");
        }
        return list.isEmpty() ? null : list.get(0);
    }

    private URI buildQueryUri() throws URISyntaxException {
        URIBuilder uriBuilder = new URIBuilder();
        uriBuilder.setPath(uriPath);
        for (Map.Entry<String, String> opt : queryParams.entrySet()) {
            uriBuilder.setParameter(opt.getKey(), opt.getValue());
        }
        return uriBuilder.build();
    }
}
TOP

Related Classes of ru.dreamteam.couch.query.Query

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.