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

io.yawp.repository.query.QueryBuilder Maven / Gradle / Ivy

There is a newer version: 2.08alpha
Show newest version
package io.yawp.repository.query;

import io.yawp.repository.IdRef;
import io.yawp.repository.Repository;
import io.yawp.repository.models.ObjectModel;
import io.yawp.repository.query.condition.BaseCondition;
import io.yawp.repository.query.condition.Condition;
import io.yawp.repository.query.condition.SimpleCondition;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

public class QueryBuilder {

    private Class clazz;

    private ObjectModel model;

    private Repository r;

    private IdRef parentId;

    private BaseCondition condition;

    private List preOrders = new ArrayList<>();

    private List postOrders = new ArrayList<>();

    private Integer limit;

    private String cursor;

    private QueryBuilder(Class clazz, Repository r) {
        this.clazz = clazz;
        this.r = r;
        this.model = new ObjectModel(clazz);
    }

    public static  QueryBuilder q(Class clazz, Repository r) {
        return new QueryBuilder<>(clazz, r);
    }

    public  QueryTransformer transform(String transformName) {
        return new QueryTransformer<>(this, transformName);
    }

    public QueryBuilder and(String field, String operator, Object value) {
        return where(field, operator, value);
    }

    public QueryBuilder where(String field, String operator, Object value) {
        return where(Condition.c(field, operator, value));
    }

    public QueryBuilder where(BaseCondition c) {
        if (condition == null) {
            condition = c;
        } else {
            condition = Condition.and(condition, c);
        }

        condition.init(r, clazz);
        return this;
    }

    public QueryBuilder and(BaseCondition c) {
        return where(c);
    }

    public QueryBuilder from(IdRef parentId) {
        if (parentId == null) {
            this.parentId = null;
            return this;
        }

        this.parentId = parentId;
        return this;
    }

    public QueryBuilder order(String property) {
        order(property, null);
        return this;
    }

    public QueryBuilder order(String property, String direction) {
        preOrders.add(new QueryOrder(null, property, direction));
        return this;
    }

    public QueryBuilder sort(String property) {
        sort(property, null);
        return this;
    }

    public QueryBuilder sort(String property, String direction) {
        sort(null, property, direction);
        return this;
    }

    public QueryBuilder sort(String entity, String property, String direction) {
        postOrders.add(new QueryOrder(entity, property, direction));
        return this;
    }

    public QueryBuilder limit(int limit) {
        this.limit = limit;
        return this;
    }

    public QueryBuilder cursor(String cursor) {
        this.cursor = cursor;
        return this;
    }

    public IdRef getParentId() {
        return parentId;
    }

    public String getCursor() {
        return this.cursor;
    }

    public void setCursor(String cursor) {
        this.cursor = cursor;
    }

    public QueryBuilder options(QueryOptions options) {
        if (options.getCondition() != null) {
            where(options.getCondition());
        }

        if (options.getPreOrders() != null) {
            preOrders.addAll(options.getPreOrders());
        }

        if (options.getPostOrders() != null) {
            postOrders.addAll(options.getPostOrders());
        }

        if (options.getLimit() != null) {
            limit(options.getLimit());
        }

        if (options.getCursor() != null) {
            cursor(options.getCursor());
        }

        return this;
    }

    public Integer getLimit() {
        return limit;
    }

    public List getPreOrders() {
        return preOrders;
    }

    public BaseCondition getCondition() {
        return condition;
    }

    public Repository getRepository() {
        return this.r;
    }

    public ObjectModel getModel() {
        return model;
    }

    public List executeQueryList() {
        r.namespace().set(getClazz());
        try {
            return executeQuery();
        } finally {
            r.namespace().reset();
        }
    }

    public List list() {
        List list = executeQueryList();
        sortList(list);
        return list;
    }

    public T first() {
        r.namespace().set(getClazz());
        try {
            if (isQueryById()) {
                return executeQueryById();
            }
            return executeQueryFirst();
        } finally {
            r.namespace().reset();
        }
    }

    private T executeQueryFirst() {
        limit(1);

        List list = executeQuery();
        if (list.size() == 0) {
            return null;
        }
        return list.get(0);
    }

    public T only() throws NoResultException, MoreThanOneResultException {
        r.namespace().set(getClazz());
        try {
            T object;

            if (isQueryById()) {
                object = executeQueryById();
            } else {
                object = executeQueryOnly();
            }

            if (object == null) {
                throw new NoResultException();
            }

            return object;
        } finally {
            r.namespace().reset();
        }
    }

    private T executeQueryOnly() throws MoreThanOneResultException {
        List list = executeQuery();
        if (list.size() == 0) {
            throw new NoResultException();
        }
        if (list.size() == 1) {
            return list.get(0);
        }
        throw new MoreThanOneResultException();
    }

    private List executeQuery() {
        List objects = r.driver().query().objects(this);
        return postFilter(objects);
    }

    private List postFilter(List objects) {
        if (!hasPostFilter()) {
            return objects;
        }
        return condition.applyPostFilter(objects);

    }

    private boolean hasPostFilter() {
        return condition != null && condition.hasPostFilter();
    }

    @SuppressWarnings("unchecked")
    private T executeQueryById() {
        SimpleCondition c = (SimpleCondition) condition;
        IdRef id = (IdRef) c.getWhereValue();

        return r.driver().query().fetch(id);
    }

    private boolean isQueryById() {
        if (condition == null || !(condition instanceof SimpleCondition)) {
            return false;
        }

        SimpleCondition c = (SimpleCondition) condition;
        return c.isIdField() && c.isEqualOperator();
    }

    public void sortList(List objects) {
        if (!hasPostOrder()) {
            return;
        }

        Collections.sort(objects, new Comparator() {
            @Override
            public int compare(Object o1, Object o2) {
                for (QueryOrder order : postOrders) {
                    int compare = order.compare(o1, o2);

                    if (compare == 0) {
                        continue;
                    }

                    return compare;
                }
                return 0;
            }
        });
    }

    public boolean hasPreOrder() {
        return preOrders.size() != 0;
    }

    private boolean hasPostOrder() {
        return postOrders.size() != 0;
    }

    protected Class getClazz() {
        return clazz;
    }

    public QueryBuilder whereById(String operator, IdRef id) {
        return from(id.getParentId()).where(model.getIdField().getName(), operator, id);
    }

    public T fetch(IdRef idRef) {
        return whereById("=", idRef).only();
    }

    public T fetch(Long id) {
        IdRef idRef = IdRef.create(r, clazz, id);
        return fetch(idRef);
    }

    public T fetch(String name) {
        IdRef idRef = IdRef.create(r, clazz, name);
        return fetch(idRef);
    }

    public List> ids() {
        if (hasPostFilter() || hasPostOrder()) {
            throw new RuntimeException("ids() cannot be used with post query filter or order. You may need to add @Index to your model attributes.");
        }

        r.namespace().set(getClazz());
        try {
            List> ids = r.driver().query().ids(this);


            return ids;
        } finally {
            r.namespace().reset();
        }
    }

    public IdRef onlyId() throws NoResultException, MoreThanOneResultException {

        List> ids = ids();

        if (ids.size() == 0) {
            throw new NoResultException();
        }

        if (ids.size() > 1) {
            throw new MoreThanOneResultException();
        }

        return ids.get(0);
    }
}