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

org.jnosql.diana.couchbase.document.QueryConverter Maven / Gradle / Ivy

/*
 *  Copyright (c) 2017 Otávio Santana and others
 *   All rights reserved. This program and the accompanying materials
 *   are made available under the terms of the Eclipse Public License v1.0
 *   and Apache License v2.0 which accompanies this distribution.
 *   The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html
 *   and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php.
 *
 *   You may elect to redistribute this code under either of these licenses.
 *
 *   Contributors:
 *
 *   Otavio Santana
 */
package org.jnosql.diana.couchbase.document;

import com.couchbase.client.java.document.json.JsonObject;
import com.couchbase.client.java.query.Delete;
import com.couchbase.client.java.query.Statement;
import com.couchbase.client.java.query.dsl.Expression;
import com.couchbase.client.java.query.dsl.path.MutateLimitPath;
import org.jnosql.diana.api.Condition;
import org.jnosql.diana.api.Sort;
import org.jnosql.diana.api.TypeReference;
import org.jnosql.diana.api.document.Document;
import org.jnosql.diana.api.document.DocumentCondition;
import org.jnosql.diana.api.document.DocumentDeleteQuery;
import org.jnosql.diana.api.document.DocumentQuery;
import org.jnosql.diana.driver.ValueUtil;

import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.function.Function;

import static com.couchbase.client.java.query.dsl.Expression.x;
import static java.util.Objects.nonNull;
import static org.jnosql.diana.api.Condition.EQUALS;
import static org.jnosql.diana.api.Condition.IN;
import static org.jnosql.diana.couchbase.document.EntityConverter.ID_FIELD;
import static org.jnosql.diana.couchbase.document.EntityConverter.KEY_FIELD;
import static org.jnosql.diana.couchbase.document.StatementFactory.create;

final class QueryConverter {

    private static final Set NOT_APPENDABLE = EnumSet.of(IN, Condition.AND, Condition.OR);

    private static final char PARAM_PREFIX = '$';
    private static final String[] ALL_SELECT = {"*"};
    private static final Function SORT_MAP = s -> {
        if (Sort.SortType.ASC.equals(s.getType())) {
            return com.couchbase.client.java.query.dsl.Sort.asc(s.getName());
        } else {
            return com.couchbase.client.java.query.dsl.Sort.desc(s.getName());
        }
    };

    private QueryConverter() {
    }

    static QueryConverterResult select(DocumentQuery query, String bucket) {
        JsonObject params = JsonObject.create();
        List keys = new ArrayList<>();
        String[] documents = query.getDocuments().stream().toArray(String[]::new);
        if (documents.length == 0) {
            documents = ALL_SELECT;
        }

        Statement statement = null;
        int skip = (int) query.getSkip();
        int limit = (int) query.getLimit();

        com.couchbase.client.java.query.dsl.Sort[] sorts = query.getSorts().stream().map(SORT_MAP).
                toArray(com.couchbase.client.java.query.dsl.Sort[]::new);

        if (query.getCondition().isPresent()) {
            Expression condition = getCondition(query.getCondition().get(), params, keys, query.getDocumentCollection());
            if (nonNull(condition)) {
                statement = create(bucket, documents, skip, limit, sorts, condition);
            } else {
                statement = null;
            }
        } else {
            statement = create(bucket, documents, skip, limit, sorts);
        }
        return new QueryConverterResult(params, statement, keys);
    }


    static QueryConverterResult delete(DocumentDeleteQuery query, String bucket) {
        JsonObject params = JsonObject.create();
        List ids = new ArrayList<>();
        Expression condition = getCondition(query.getCondition()
                .orElseThrow(() -> new IllegalArgumentException("Condigtion is required")), params, ids,
                query.getDocumentCollection());
        MutateLimitPath statement = null;
        if (nonNull(condition)) {
            statement = Delete.deleteFrom(bucket).where(condition);
        }

        return new QueryConverterResult(params, statement, ids);
    }

    private static Expression getCondition(DocumentCondition condition, JsonObject params
            , List keys, String documentCollection) {
        Document document = condition.getDocument();

        if (!NOT_APPENDABLE.contains(condition.getCondition()) && isKeyField(document)) {
            if (IN.equals(condition.getCondition())) {
                inKeys(keys, documentCollection, document);
            } else if (EQUALS.equals(condition.getCondition())) {
                eqKeys(keys, documentCollection, document);

            }

            return null;
        }
        if (!NOT_APPENDABLE.contains(condition.getCondition())) {
            if(Condition.BETWEEN.equals(condition.getCondition()) || Condition.IN.equals(condition.getCondition())) {
                params.put(document.getName(), ValueUtil.convertToList(document.getValue()));
            } else {
                params.put(document.getName(), ValueUtil.convert(document.getValue()));
            }
        }

        switch (condition.getCondition()) {
            case EQUALS:
                return x(document.getName()).eq(x(PARAM_PREFIX + document.getName()));
            case LESSER_THAN:
                return x(document.getName()).lt(x(PARAM_PREFIX + document.getName()));
            case LESSER_EQUALS_THAN:
                return x(document.getName()).lte(x(PARAM_PREFIX + document.getName()));
            case GREATER_THAN:
                return x(document.getName()).gt(x(PARAM_PREFIX + document.getName()));
            case GREATER_EQUALS_THAN:
                return x(document.getName()).gte(x(PARAM_PREFIX + document.getName()));
            case LIKE:
                return x(document.getName()).like(x(PARAM_PREFIX + document.getName()));
            case IN:
                return x(document.getName()).in(x(PARAM_PREFIX + document.getName()));
            case BETWEEN:
                return x(document.getName()).between(x(PARAM_PREFIX + document.getName()));
            case AND:
                return document.get(new TypeReference>() {
                })
                        .stream()
                        .map(d -> getCondition(d, params, keys, documentCollection))
                        .filter(Objects::nonNull)
                        .reduce(Expression::and)
                        .orElseThrow(() -> new IllegalStateException("An and condition cannot be empty"));


            case OR:
                return document.get(new TypeReference>() {
                })
                        .stream()
                        .map(d -> getCondition(d, params, keys, documentCollection))
                        .reduce(Expression::or)
                        .orElseThrow(() -> new IllegalStateException("An or condition cannot be empty"));
            case NOT:
                DocumentCondition dc = document.get(DocumentCondition.class);
                return getCondition(dc, params, keys, documentCollection).not();
            default:
                throw new IllegalStateException("This condition is not supported at coubhbase: " + condition.getCondition());
        }
    }

    private static void eqKeys(List keys, String documentCollection, Document document) {
        if(document.getName().equals(KEY_FIELD)){
            keys.add(document.get(String.class));
        } else {
            keys.add(EntityConverter.getPrefix(documentCollection, document.get(String.class)));
        }
    }

    private static void inKeys(List keys, String documentCollection, Document document) {
        if(document.getName().equals(KEY_FIELD)){
            keys.addAll(document.get(new TypeReference>() {
            }));
        } else {
            List ids = document.get(new TypeReference>() {});
            ids.stream().map(id -> EntityConverter.getPrefix(documentCollection, id))
                    .forEach(keys::add);
        }
    }

    private static boolean isKeyField(Document document) {
        return ID_FIELD.equals(document.getName()) || KEY_FIELD.equals(document.getName());
    }

    static class QueryConverterResult {

        private final JsonObject params;

        private final Statement statement;

        private final List keys;

        QueryConverterResult(JsonObject params, Statement statement, List keys) {
            this.params = params;
            this.statement = statement;
            this.keys = keys;
        }

        JsonObject getParams() {
            return params;
        }

        Statement getStatement() {
            return statement;
        }

        List getKeys() {
            return keys;
        }
    }


}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy