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

io.vertx.up.unity.JooqCond Maven / Gradle / Ivy

There is a newer version: 0.9.0
Show newest version
package io.vertx.up.unity;

import io.vertx.core.json.JsonObject;
import io.vertx.up.atom.query.Criteria;
import io.vertx.up.atom.query.Inquiry;
import io.vertx.up.log.Annal;
import io.vertx.up.eon.Strings;
import io.vertx.up.eon.Values;
import io.vertx.up.exception.zero.JooqArgumentException;
import io.vertx.up.util.Ut;
import io.vertx.up.fn.Fn;
import org.jooq.Condition;
import org.jooq.Field;
import org.jooq.Operator;
import org.jooq.impl.DSL;

import java.time.Instant;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.function.BiFunction;
import java.util.function.Function;

class JooqCond {

    private static final Annal LOGGER = Annal.get(JooqCond.class);
    private static final ConcurrentMap> OPS =
            new ConcurrentHashMap>() {
                {
                    this.put(Inquiry.Op.LT, (field, value) -> DSL.field(field).lt(value));
                    this.put(Inquiry.Op.GT, (field, value) -> DSL.field(field).gt(value));
                    this.put(Inquiry.Op.LE, (field, value) -> DSL.field(field).le(value));
                    this.put(Inquiry.Op.GE, (field, value) -> DSL.field(field).ge(value));
                    this.put(Inquiry.Op.EQ, (field, value) -> DSL.field(field).eq(value));
                    this.put(Inquiry.Op.NEQ, (field, value) -> DSL.field(field).ne(value));
                    this.put(Inquiry.Op.NOT_NULL, (field, value) -> DSL.field(field).isNotNull());
                    this.put(Inquiry.Op.NULL, (field, value) -> DSL.field(field).isNull());
                    this.put(Inquiry.Op.TRUE, (field, value) -> DSL.field(field).isTrue());
                    this.put(Inquiry.Op.FALSE, (field, value) -> DSL.field(field).isFalse());
                    this.put(Inquiry.Op.IN, (field, value) -> {
                        final Collection values = Ut.toCollection(value);
                        return DSL.field(field).in(values);
                    });
                    this.put(Inquiry.Op.NOT_IN, (field, value) -> {
                        final Collection values = Ut.toCollection(value);
                        return DSL.field(field).notIn(values);
                    });
                    this.put(Inquiry.Op.START, (field, value) -> DSL.field(field).startsWith(value));
                    this.put(Inquiry.Op.END, (field, value) -> DSL.field(field).endsWith(value));
                    this.put(Inquiry.Op.CONTAIN, (field, value) -> DSL.field(field).contains(value));
                }
            };
    private static final ConcurrentMap> DOPS =
            new ConcurrentHashMap>() {
                {
                    this.put(Inquiry.Instant.DAY, (field, value) -> {
                        // Time for locale
                        final LocalDate date = Ut.toDate(value);
                        return DSL.field(field).between(date.atStartOfDay(), date.plusDays(1).atStartOfDay());
                    });
                    this.put(Inquiry.Instant.DATE, (field, value) -> {
                        final LocalDate date = Ut.toDate(value);
                        final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
                        return DSL.field(field).eq(date.format(formatter));
                    });
                }
            };

    private static String applyField(final String field) {
        final Set keywords = new HashSet() {
            {
                this.add("KEY"); // MYSQL, KEY is keyword
            }
        };
        return keywords.contains(field) ? "`" + field + "`" : field;
    }

    // Condition ---------------------------------------------------------
    static Condition transform(final JsonObject filters,
                               final Function fnAnalyze) {
        return transform(filters, null, fnAnalyze);
    }

    static Condition transform(final JsonObject filters,
                               Operator operator,
                               final Function fnAnalyze) {
        final Condition condition;
        final Criteria criteria = Criteria.create(filters);
        /*
         * The mode has been selected by criteria, the condition is as following:
         * When filters contains the key = value ( value = JsonObject ), TREE
         * Otherwise it's LINEAR.
         */
        if (!Ut.isNil(filters)) {
            LOGGER.info("[ ZERO ] ( Query ) Mode selected {0}, filters raw = {1}",
                    criteria.getMode(), filters);
        }
        if (Inquiry.Mode.LINEAR == criteria.getMode()) {
            JsonObject inputFilters = filters;
            if (null == operator) {
                /*
                 * When the mode is linear, the system will be sure filters contains
                 * no value with JsonObject, remove all JsonObject value to switch
                 * LINEAR mode.
                 */
                inputFilters = transformLinear(filters);
                /*
                 * Re-calculate the operator AND / OR
                 * For complex normalize linear query tree.
                 */
                if (inputFilters.containsKey(Strings.EMPTY)) {
                    if (inputFilters.getBoolean(Strings.EMPTY)) {
                        operator = Operator.AND;
                    } else {
                        operator = Operator.OR;
                    }
                    inputFilters.remove(Strings.EMPTY);
                }
            } else {
                /*
                 * When LINEAR mode, operator is hight priority, the query engine will
                 * ignore the flag key = value. ( key = "", value = true )
                 * It's defined by zero.
                 */
                inputFilters.remove(Strings.EMPTY);
            }
            condition = transformLinear(inputFilters, operator, fnAnalyze);
        } else {
            /*
             * When the mode is Tree, you mustn't set operator, because the operator will
             * be parsed by query tree engine, this operation is unsupported and it will
             * throw out exception JooqModeConflictException,
             * Ignore operator information here, because the next analyzing will ignore automatically.
             */
            /*Fn.outUp(null != operator, LOGGER, JooqModeConflictException.class,
                    JooqCond.class, Inquiry.Mode.LINEAR, filters);*/
            condition = transformTree(filters, fnAnalyze);
        }
        if (null != condition) {
            LOGGER.info(Info.JOOQ_PARSE, condition);
        }
        return condition;
    }

    private static Condition transformTree(final JsonObject filters,
                                           final Function fnAnalyze) {
        Condition condition;
        // Calc operator in this level
        final Operator operator = calcOperator(filters);
        // Calc liner
        final JsonObject cloned = filters.copy();
        cloned.remove(Strings.EMPTY);
        // Operator has been calculated, remove "" to set linear of current tree.
        final Condition linear = transformLinear(transformLinear(cloned), operator, fnAnalyze);
        // Calc All Tree
        final List tree = transformTreeSet(filters, fnAnalyze);
        // Merge the same level
        if (null != linear) {
            tree.add(linear);
        }
        if (1 == tree.size()) {
            condition = tree.get(Values.IDX);
        } else {
            condition = tree.get(Values.IDX);
            for (int idx = Values.ONE; idx < tree.size(); idx++) {
                final Condition right = tree.get(idx);
                condition = opCond(condition, right, operator);
            }
        }
        return condition;
    }

    private static List transformTreeSet(
            final JsonObject filters,
            final Function fnAnalyze) {
        final List conditions = new ArrayList<>();
        final JsonObject tree = filters.copy();
        if (!tree.isEmpty()) {
            for (final String field : filters.fieldNames()) {
                if (Ut.isJObject(tree.getValue(field))) {
                    conditions.add(transformTree(tree.getJsonObject(field), fnAnalyze));
                }
            }
        }
        return conditions;
    }

    private static JsonObject transformLinear(final JsonObject filters) {
        final JsonObject linear = filters.copy();
        for (final String field : filters.fieldNames()) {
            if (Ut.isJObject(linear.getValue(field))) {
                linear.remove(field);
            }
        }
        return linear;
    }

    private static Operator calcOperator(final JsonObject data) {
        final Operator operator;
        if (!data.containsKey(Strings.EMPTY)) {
            operator = Operator.OR;
        } else {
            final Boolean isAnd = Boolean.valueOf(data.getValue(Strings.EMPTY).toString());
            operator = isAnd ? Operator.AND : Operator.OR;
        }
        return operator;
    }

    private static Condition transformLinear(
            final JsonObject filters,
            final Operator operator,
            final Function fnAnalyze) {
        Condition condition = null;
        for (final String field : filters.fieldNames()) {
            final String key = getKey(field);
            final String[] fields = field.split(",");
            String targetField = field.split(",")[Values.IDX];
            // TargetField re-do
            if (null != fnAnalyze) {
                targetField = fnAnalyze.apply(targetField).getName();
            }
            // Date, DateTime, Time
            final Object value = filters.getValue(field);
            if (3 > fields.length) {
                // Function
                final BiFunction fun = OPS.get(key);
                // JsonArray to List, fix vert.x and jooq connect issue.
                /**if (Ut.isJArray(value)) {
                 value = ((JsonArray) value).getList().toArray();
                 }**/
                final Condition item = fun.apply(applyField(targetField.trim()), value);
                condition = opCond(condition, item, operator);
                // Function condition inject

            } else if (3 == fields.length) {
                Fn.outUp(null == value, LOGGER,
                        JooqArgumentException.class, UxJooq.class, value);
                final Instant instant = filters.getInstant(field);
                Fn.outUp(Instant.class != instant.getClass(), LOGGER,
                        JooqArgumentException.class, UxJooq.class, instant.getClass());
                final String mode = fields[Values.TWO];
                final BiFunction fun = DOPS.get(mode);
                final Condition item = fun.apply(applyField(targetField.trim()), instant);
                condition = opCond(condition, item, operator);
            }
        }
        return condition;
    }

    private static String getKey(final String field) {
        if (!field.contains(",")) {
            return Inquiry.Op.EQ;
        } else {
            final String opStr = field.split(",")[Values.ONE];
            return Ut.isNil(opStr) ? Inquiry.Op.EQ : opStr.trim().toLowerCase();
        }
    }

    private static Condition opCond(final Condition left,
                                    final Condition right,
                                    final Operator operator) {
        if (null == left || null == right) {
            if (null == left && null != right) {
                return right;
            } else {
                return null;
            }
        } else {
            if (Operator.AND == operator) {
                return left.and(right);
            } else {
                return left.or(right);
            }
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy