Search in sources :

Example 16 with Filter

use of org.apache.druid.query.filter.Filter in project druid by druid-io.

the class JoinableFactoryWrapper method convertJoinsToFilters.

/**
 * Converts any join clauses to filters that can be converted, and returns the rest as-is.
 *
 * See {@link #convertJoinToFilter} for details on the logic.
 */
@VisibleForTesting
static Pair<List<Filter>, List<JoinableClause>> convertJoinsToFilters(final List<JoinableClause> clauses, final Set<String> requiredColumns, final int maxNumFilterValues) {
    final List<Filter> filterList = new ArrayList<>();
    final List<JoinableClause> clausesToUse = new ArrayList<>();
    // Join clauses may depend on other, earlier join clauses.
    // We track that using a Multiset, because we'll need to remove required columns one by one as we convert clauses,
    // and multiple clauses may refer to the same column.
    final Multiset<String> columnsRequiredByJoinClauses = HashMultiset.create();
    for (JoinableClause clause : clauses) {
        for (String column : clause.getCondition().getRequiredColumns()) {
            columnsRequiredByJoinClauses.add(column, 1);
        }
    }
    // Walk through the list of clauses, picking off any from the start of the list that can be converted to filters.
    boolean atStart = true;
    for (JoinableClause clause : clauses) {
        if (atStart) {
            // Remove this clause from columnsRequiredByJoinClauses. It's ok if it relies on itself.
            for (String column : clause.getCondition().getRequiredColumns()) {
                columnsRequiredByJoinClauses.remove(column, 1);
            }
            final Optional<Filter> filter = convertJoinToFilter(clause, Sets.union(requiredColumns, columnsRequiredByJoinClauses.elementSet()), maxNumFilterValues);
            if (filter.isPresent()) {
                filterList.add(filter.get());
            } else {
                clausesToUse.add(clause);
                atStart = false;
            }
        } else {
            clausesToUse.add(clause);
        }
    }
    // Sanity check. If this exception is ever thrown, it's a bug.
    if (filterList.size() + clausesToUse.size() != clauses.size()) {
        throw new ISE("Lost a join clause during planning");
    }
    return Pair.of(filterList, clausesToUse);
}
Also used : InDimFilter(org.apache.druid.query.filter.InDimFilter) Filter(org.apache.druid.query.filter.Filter) ArrayList(java.util.ArrayList) ISE(org.apache.druid.java.util.common.ISE) PreJoinableClause(org.apache.druid.query.planning.PreJoinableClause) VisibleForTesting(com.google.common.annotations.VisibleForTesting)

Example 17 with Filter

use of org.apache.druid.query.filter.Filter in project druid by druid-io.

the class JoinFilterAnalyzer method splitFilter.

/**
 * @param joinFilterPreAnalysis The pre-analysis computed by {@link #computeJoinFilterPreAnalysis)}
 * @param baseFilter - Filter on base table that was specified in the query itself
 *
 * @return A JoinFilterSplit indicating what parts of the filter should be applied pre-join and post-join
 */
public static JoinFilterSplit splitFilter(JoinFilterPreAnalysis joinFilterPreAnalysis, @Nullable Filter baseFilter) {
    if (joinFilterPreAnalysis.getOriginalFilter() == null || !joinFilterPreAnalysis.isEnableFilterPushDown()) {
        return new JoinFilterSplit(baseFilter, joinFilterPreAnalysis.getOriginalFilter(), ImmutableSet.of());
    }
    // Pushdown filters, rewriting if necessary
    List<Filter> leftFilters = new ArrayList<>();
    List<Filter> rightFilters = new ArrayList<>();
    Map<Expr, VirtualColumn> pushDownVirtualColumnsForLhsExprs = new HashMap<>();
    if (null != baseFilter) {
        leftFilters.add(baseFilter);
    }
    for (Filter baseTableFilter : joinFilterPreAnalysis.getNormalizedBaseTableClauses()) {
        if (!Filters.filterMatchesNull(baseTableFilter)) {
            leftFilters.add(baseTableFilter);
        } else {
            rightFilters.add(baseTableFilter);
        }
    }
    for (Filter orClause : joinFilterPreAnalysis.getNormalizedJoinTableClauses()) {
        JoinFilterAnalysis joinFilterAnalysis = analyzeJoinFilterClause(orClause, joinFilterPreAnalysis, pushDownVirtualColumnsForLhsExprs);
        if (joinFilterAnalysis.isCanPushDown()) {
            // noinspection OptionalGetWithoutIsPresent isCanPushDown checks isPresent
            leftFilters.add(joinFilterAnalysis.getPushDownFilter().get());
        }
        if (joinFilterAnalysis.isRetainAfterJoin()) {
            rightFilters.add(joinFilterAnalysis.getOriginalFilter());
        }
    }
    return new JoinFilterSplit(Filters.maybeAnd(leftFilters).orElse(null), Filters.maybeAnd(rightFilters).orElse(null), new HashSet<>(pushDownVirtualColumnsForLhsExprs.values()));
}
Also used : Expr(org.apache.druid.math.expr.Expr) SelectorFilter(org.apache.druid.segment.filter.SelectorFilter) FalseFilter(org.apache.druid.segment.filter.FalseFilter) OrFilter(org.apache.druid.segment.filter.OrFilter) InDimFilter(org.apache.druid.query.filter.InDimFilter) Filter(org.apache.druid.query.filter.Filter) HashMap(java.util.HashMap) ArrayList(java.util.ArrayList) VirtualColumn(org.apache.druid.segment.VirtualColumn) ExpressionVirtualColumn(org.apache.druid.segment.virtual.ExpressionVirtualColumn)

Example 18 with Filter

use of org.apache.druid.query.filter.Filter in project druid by druid-io.

the class JoinFilterAnalyzer method rewriteFilterDirect.

private static JoinFilterAnalysis rewriteFilterDirect(Filter filterClause, JoinFilterPreAnalysis joinFilterPreAnalysis, Map<Expr, VirtualColumn> pushDownVirtualColumnsForLhsExprs) {
    if (!filterClause.supportsRequiredColumnRewrite()) {
        return JoinFilterAnalysis.createNoPushdownFilterAnalysis(filterClause);
    }
    List<Filter> newFilters = new ArrayList<>();
    // we only support direct rewrites of filters that reference a single column
    String reqColumn = filterClause.getRequiredColumns().iterator().next();
    List<JoinFilterColumnCorrelationAnalysis> correlationAnalyses = joinFilterPreAnalysis.getCorrelationsByDirectFilteringColumn().get(reqColumn);
    if (correlationAnalyses == null) {
        return JoinFilterAnalysis.createNoPushdownFilterAnalysis(filterClause);
    }
    for (JoinFilterColumnCorrelationAnalysis correlationAnalysis : correlationAnalyses) {
        if (correlationAnalysis.supportsPushDown()) {
            for (String correlatedBaseColumn : correlationAnalysis.getBaseColumns()) {
                Filter rewrittenFilter = filterClause.rewriteRequiredColumns(ImmutableMap.of(reqColumn, correlatedBaseColumn));
                newFilters.add(rewrittenFilter);
            }
            for (Expr correlatedBaseExpr : correlationAnalysis.getBaseExpressions()) {
                // We need to create a virtual column for the expressions when pushing down
                VirtualColumn pushDownVirtualColumn = pushDownVirtualColumnsForLhsExprs.computeIfAbsent(correlatedBaseExpr, (expr) -> {
                    String vcName = getCorrelatedBaseExprVirtualColumnName(pushDownVirtualColumnsForLhsExprs.size());
                    return new ExpressionVirtualColumn(vcName, correlatedBaseExpr, ColumnType.STRING);
                });
                Filter rewrittenFilter = filterClause.rewriteRequiredColumns(ImmutableMap.of(reqColumn, pushDownVirtualColumn.getOutputName()));
                newFilters.add(rewrittenFilter);
            }
        }
    }
    if (newFilters.isEmpty()) {
        return JoinFilterAnalysis.createNoPushdownFilterAnalysis(filterClause);
    }
    return new JoinFilterAnalysis(false, filterClause, Filters.maybeAnd(newFilters).orElse(null));
}
Also used : ExpressionVirtualColumn(org.apache.druid.segment.virtual.ExpressionVirtualColumn) Expr(org.apache.druid.math.expr.Expr) SelectorFilter(org.apache.druid.segment.filter.SelectorFilter) FalseFilter(org.apache.druid.segment.filter.FalseFilter) OrFilter(org.apache.druid.segment.filter.OrFilter) InDimFilter(org.apache.druid.query.filter.InDimFilter) Filter(org.apache.druid.query.filter.Filter) ArrayList(java.util.ArrayList) VirtualColumn(org.apache.druid.segment.VirtualColumn) ExpressionVirtualColumn(org.apache.druid.segment.virtual.ExpressionVirtualColumn)

Example 19 with Filter

use of org.apache.druid.query.filter.Filter in project druid by druid-io.

the class JoinFilterAnalyzer method rewriteOrFilter.

/**
 * Potentially rewrite the subfilters of an OR filter so that the whole OR filter can be pushed down to
 * the base table.
 *
 * @param orFilter                          OrFilter to be rewritten
 * @param joinFilterPreAnalysis             The pre-analysis computed by {@link #computeJoinFilterPreAnalysis)}
 * @param pushDownVirtualColumnsForLhsExprs See comments on {@link #analyzeJoinFilterClause}
 *
 * @return A JoinFilterAnalysis indicating how to handle the potentially rewritten filter
 */
private static JoinFilterAnalysis rewriteOrFilter(OrFilter orFilter, JoinFilterPreAnalysis joinFilterPreAnalysis, Map<Expr, VirtualColumn> pushDownVirtualColumnsForLhsExprs) {
    List<Filter> newFilters = new ArrayList<>();
    boolean retainRhs = false;
    for (Filter filter : orFilter.getFilters()) {
        if (!joinFilterPreAnalysis.getJoinableClauses().areSomeColumnsFromJoin(filter.getRequiredColumns())) {
            newFilters.add(filter);
            continue;
        }
        JoinFilterAnalysis rewritten = null;
        if (joinFilterPreAnalysis.getEquiconditions().doesFilterSupportDirectJoinFilterRewrite(filter)) {
            rewritten = rewriteFilterDirect(filter, joinFilterPreAnalysis, pushDownVirtualColumnsForLhsExprs);
        } else if (filter instanceof SelectorFilter) {
            retainRhs = true;
            // We could optimize retainRhs handling further by introducing a "filter to retain" property to the
            // analysis, and only keeping the subfilters that need to be retained
            rewritten = rewriteSelectorFilter((SelectorFilter) filter, joinFilterPreAnalysis, pushDownVirtualColumnsForLhsExprs);
        }
        if (rewritten == null || !rewritten.isCanPushDown()) {
            return JoinFilterAnalysis.createNoPushdownFilterAnalysis(orFilter);
        } else {
            // noinspection OptionalGetWithoutIsPresent isCanPushDown checks isPresent
            newFilters.add(rewritten.getPushDownFilter().get());
        }
    }
    return new JoinFilterAnalysis(retainRhs, orFilter, Filters.maybeOr(newFilters).orElse(null));
}
Also used : SelectorFilter(org.apache.druid.segment.filter.SelectorFilter) SelectorFilter(org.apache.druid.segment.filter.SelectorFilter) FalseFilter(org.apache.druid.segment.filter.FalseFilter) OrFilter(org.apache.druid.segment.filter.OrFilter) InDimFilter(org.apache.druid.query.filter.InDimFilter) Filter(org.apache.druid.query.filter.Filter) ArrayList(java.util.ArrayList)

Example 20 with Filter

use of org.apache.druid.query.filter.Filter in project druid by druid-io.

the class RhsRewriteCandidates method getRhsRewriteCandidates.

/**
 * Determine candidates for filter rewrites.
 * A candidate is an RHS column that appears in a filter, along with the value being filtered on, plus
 * the joinable clause associated with the table that the RHS column is from.
 *
 * These candidates are redued to filter rewrite correlations.
 *
 * @param normalizedJoinTableClauses
 * @param equiconditions
 * @param joinableClauses
 * @return A set of candidates for filter rewrites.
 */
public static RhsRewriteCandidates getRhsRewriteCandidates(List<Filter> normalizedJoinTableClauses, Equiconditions equiconditions, JoinableClauses joinableClauses) {
    Set<RhsRewriteCandidate> rhsRewriteCandidates = new LinkedHashSet<>();
    for (Filter orClause : normalizedJoinTableClauses) {
        if (Filters.filterMatchesNull(orClause)) {
            continue;
        }
        if (orClause instanceof OrFilter) {
            for (Filter subFilter : ((OrFilter) orClause).getFilters()) {
                Optional<RhsRewriteCandidate> rhsRewriteCandidate = determineRhsRewriteCandidatesForSingleFilter(subFilter, equiconditions, joinableClauses);
                rhsRewriteCandidate.ifPresent(rhsRewriteCandidates::add);
            }
            continue;
        }
        Optional<RhsRewriteCandidate> rhsRewriteCandidate = determineRhsRewriteCandidatesForSingleFilter(orClause, equiconditions, joinableClauses);
        rhsRewriteCandidate.ifPresent(rhsRewriteCandidates::add);
    }
    return new RhsRewriteCandidates(rhsRewriteCandidates);
}
Also used : LinkedHashSet(java.util.LinkedHashSet) SelectorFilter(org.apache.druid.segment.filter.SelectorFilter) OrFilter(org.apache.druid.segment.filter.OrFilter) Filter(org.apache.druid.query.filter.Filter) OrFilter(org.apache.druid.segment.filter.OrFilter)

Aggregations

Filter (org.apache.druid.query.filter.Filter)127 Test (org.junit.Test)88 SelectorFilter (org.apache.druid.segment.filter.SelectorFilter)74 ExpressionDimFilter (org.apache.druid.query.filter.ExpressionDimFilter)64 JoinFilterPreAnalysis (org.apache.druid.segment.join.filter.JoinFilterPreAnalysis)63 OrFilter (org.apache.druid.segment.filter.OrFilter)52 AndFilter (org.apache.druid.segment.filter.AndFilter)47 BoundDimFilter (org.apache.druid.query.filter.BoundDimFilter)43 InDimFilter (org.apache.druid.query.filter.InDimFilter)43 BoundFilter (org.apache.druid.segment.filter.BoundFilter)42 FalseFilter (org.apache.druid.segment.filter.FalseFilter)41 SelectorDimFilter (org.apache.druid.query.filter.SelectorDimFilter)39 OrDimFilter (org.apache.druid.query.filter.OrDimFilter)36 JoinFilterSplit (org.apache.druid.segment.join.filter.JoinFilterSplit)35 ArrayList (java.util.ArrayList)19 DimFilter (org.apache.druid.query.filter.DimFilter)15 IndexedTableJoinable (org.apache.druid.segment.join.table.IndexedTableJoinable)13 AndDimFilter (org.apache.druid.query.filter.AndDimFilter)10 BooleanFilter (org.apache.druid.query.filter.BooleanFilter)10 Cursor (org.apache.druid.segment.Cursor)10