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

com.kenshoo.pl.entity.internal.fetch.QueryBuilder Maven / Gradle / Ivy

Go to download

A Java persistence layer based on JOOQ for high performance and business flow support.

There is a newer version: 0.1.121-jooq-3.16.3
Show newest version
package com.kenshoo.pl.entity.internal.fetch;

import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.kenshoo.jooq.*;
import com.kenshoo.pl.entity.*;
import org.jooq.*;

import java.util.*;
import java.util.function.Consumer;
import java.util.function.Predicate;

import static org.jooq.impl.DSL.trueCondition;
import static org.jooq.lambda.function.Functions.not;


public class QueryBuilder> {

    private final QueryExtender DONT_EXTEND_WITH_IDS = this::dontExtend;

    private DSLContext dslContext;
    private List> selectedFields;
    private DataTable startingTable;
    private List paths = Collections.emptyList();
    private Set oneToOneTableRelations = Collections.emptySet();
    private Condition condition = trueCondition();
    private Partitioner partitioner = this::addPartitionToCondition;
    private QueryExtender queryExtender = DONT_EXTEND_WITH_IDS;


    public QueryBuilder(DSLContext dslContext) {
        this.dslContext = dslContext;
    }

    public QueryBuilder selecting(List> selectedFields) {
        this.selectedFields = selectedFields;
        return this;
    }

    public QueryBuilder from(DataTable primaryTable) {
        this.startingTable = primaryTable;
        return this;
    }

    public QueryBuilder innerJoin(List paths) {
        this.paths = paths;
        return this;
    }

    public QueryBuilder innerJoin(TreeEdge path) {
        this.paths = Lists.newArrayList(path);
        return this;
    }

    public QueryBuilder leftJoin(Set oneToOneTableRelations) {
        this.oneToOneTableRelations = oneToOneTableRelations;
        return this;
    }

    public QueryBuilder whereIdsIn(Collection>> ids) {
        if (queryExtender != DONT_EXTEND_WITH_IDS) {
            throw new IllegalStateException("We currently support only a single query extension");
        }
        this.queryExtender = query -> addIdCondition(query, (Collection)ids);
        return this;
    }

    public QueryBuilder withCondition(Condition condition) {
        this.condition = condition;
        return this;
    }

    public QueryBuilder withoutPartitions() {
        this.partitioner = NO_PARTITION;
        return this;
    }

    public QueryExtension> build() {
        final SelectJoinStep query = dslContext.select(selectedFields).from(startingTable);
        final Set joinedTables = Sets.newHashSet(startingTable);
        paths.forEach(edge -> joinTables(query, joinedTables, edge));
        joinSecondaryTables(query, joinedTables, oneToOneTableRelations);
        condition = partitioner.transform(startingTable, condition);
        query.where(condition);

        return queryExtender.transform(query);
    }

    static void joinTables(SelectJoinStep query, Set alreadyJoinedTables, TreeEdge edgeInThePath) {
        // The joins must be composed in the order of traversal, so we have to "unwind" the path traveled from the root
        // Using a stack for that
        LinkedList joins = new LinkedList<>();
        joins.push(edgeInThePath);
        // Push onto the stack until we reach a table already joined (or the starting table)
        while (!alreadyJoinedTables.contains(edgeInThePath.source.table)) {
            edgeInThePath = edgeInThePath.source.parent;
            joins.push(edgeInThePath);
        }
        // Perform the joins
        for (TreeEdge join : joins) {
            DataTable rhs = join.target.table;
            query.join(rhs).on(getJoinCondition(join.source.table, join.target.table));
            alreadyJoinedTables.add(rhs);
        }
    }

    static void joinSecondaryTables(SelectJoinStep query, Set> alreadyJoinedTables, Set targetOneToOneRelations) {
        targetOneToOneRelations.stream()
                .filter(not(secondaryTableIn(alreadyJoinedTables)))
                .forEach(addLeftJoinTo(query));
    }

    private static Condition getJoinCondition(Table fromTable, Table toTable) {
        List> foreignKeys = toTable.getReferencesTo(fromTable);
        if (foreignKeys.isEmpty()) {
            foreignKeys = fromTable.getReferencesTo(toTable);
        }
        if (foreignKeys.isEmpty()) {
            return null;
        }
        Condition joinCondition = trueCondition();
        ForeignKey foreignKey = foreignKeys.get(0);
        org.jooq.UniqueKey key = foreignKey.getKey();
        List> otherTableFields = key.getFields();
        for (int i = 0; i < foreignKey.getFields().size(); i++) {
            TableField tableField = foreignKey.getFields().get(i);
            //noinspection unchecked
            joinCondition = joinCondition.and(tableField.eq((TableField) otherTableFields.get(i)));
        }
        return joinCondition;
    }

    private Condition addPartitionToCondition(DataTable table, final Condition inputJooqCondition) {
        return table.getVirtualPartition().stream()
                .map(fv -> (FieldAndValue) fv)
                .map(fieldAndValue -> fieldAndValue.getField().eq(fieldAndValue.getValue()))
                .reduce(inputJooqCondition, Condition::and);
    }

    private static Predicate secondaryTableIn(Set> joinedTables) {
        return relation -> joinedTables.contains(relation.getSecondary());
    }

    private static Consumer addLeftJoinTo(SelectJoinStep query) {
        return relation -> query.leftOuterJoin(relation.getSecondary()).on(getJoinCondition(relation));
    }

    private static Condition getJoinCondition(final OneToOneTableRelation relation) {
        return getJoinCondition(relation.getSecondary(), relation.getPrimary());
    }

    private interface Partitioner {
        Condition transform(DataTable table, Condition condition);
    }

    private final Partitioner NO_PARTITION = (table, cond) -> cond;

    private interface QueryExtender {
        QueryExtension> transform(SelectFinalStep query);
    }

    private QueryExtension> dontExtend(SelectFinalStep query){
        return new QueryExtension>() {
            @Override
            public SelectFinalStep getQuery() {
                return query;
            }

            @Override
            public void close() {
            }
        };
    }

    private , ID extends Identifier> QueryExtension> addIdCondition(SelectFinalStep query, Collection ids) {
        return SelectQueryExtender.of(this.dslContext, query, Identifier.groupValuesByFields(ids));
    }

}