Search in sources :

Example 1 with QueryComponent

use of com.apple.foundationdb.record.query.expressions.QueryComponent in project fdb-record-layer by FoundationDB.

the class RecordQueryPlanner method planIntersection.

@Nullable
private ScoredPlan planIntersection(@Nonnull List<ScoredPlan> intersectionCandidates, @Nonnull KeyExpression comparisonKey) {
    // Prefer plans that handle more filters (leave fewer unhandled), more index filters
    intersectionCandidates.sort(Comparator.comparingInt(ScoredPlan::getNumNonSargables).thenComparing(Comparator.comparingInt(ScoredPlan::getNumIndexFilters).reversed()));
    // Since we limited to isPrimaryKeyOrdered(), comparisonKey will always work.
    ScoredPlan plan1 = intersectionCandidates.get(0);
    List<QueryComponent> nonSargables = new ArrayList<>(plan1.combineNonSargables());
    Set<RankComparisons.RankComparison> includedRankComparisons = mergeRankComparisons(null, plan1.includedRankComparisons);
    RecordQueryPlan plan = plan1.plan;
    List<RecordQueryPlan> includedPlans = new ArrayList<>(intersectionCandidates.size());
    includedPlans.add(plan);
    // TODO optimize so that we don't do excessive intersections
    for (int i = 1; i < intersectionCandidates.size(); i++) {
        ScoredPlan nextPlan = intersectionCandidates.get(i);
        List<QueryComponent> nextNonSargables = new ArrayList<>(nextPlan.combineNonSargables());
        int oldCount = nonSargables.size();
        nonSargables.retainAll(nextNonSargables);
        if (nonSargables.size() < oldCount) {
            if (plan.isReverse() != nextPlan.plan.isReverse()) {
                // Cannot intersect plans with incompatible reverse settings.
                return null;
            }
            includedPlans.add(nextPlan.plan);
        }
        includedRankComparisons = mergeRankComparisons(includedRankComparisons, nextPlan.includedRankComparisons);
    }
    if (includedPlans.size() > 1) {
        // Calculating the new score would require more state, not doing, because we currently ignore the score
        // after this call.
        final RecordQueryPlan intersectionPlan = RecordQueryIntersectionPlan.from(includedPlans, comparisonKey);
        if (intersectionPlan.getComplexity() > configuration.getComplexityThreshold()) {
            throw new RecordQueryPlanComplexityException(intersectionPlan);
        }
        return new ScoredPlan(intersectionPlan, nonSargables, Collections.emptyList(), plan1.score, plan1.createsDuplicates, includedRankComparisons);
    } else {
        return null;
    }
}
Also used : RecordQueryPlan(com.apple.foundationdb.record.query.plan.plans.RecordQueryPlan) QueryComponent(com.apple.foundationdb.record.query.expressions.QueryComponent) ArrayList(java.util.ArrayList) Nullable(javax.annotation.Nullable)

Example 2 with QueryComponent

use of com.apple.foundationdb.record.query.expressions.QueryComponent in project fdb-record-layer by FoundationDB.

the class RecordQueryPlanner method planRankWithAnd.

@Nullable
private ScoredPlan planRankWithAnd(@Nonnull CandidateScan candidateScan, @Nonnull Index index, @Nonnull GroupingKeyExpression indexExpr, @Nonnull AndComponent and) {
    final List<QueryComponent> filters = and.getChildren();
    for (QueryComponent filter : filters) {
        if (filter instanceof QueryRecordFunctionWithComparison) {
            final QueryRecordFunctionWithComparison filterComparison = (QueryRecordFunctionWithComparison) filter;
            final RankComparisons.RankComparison rankComparison = candidateScan.planContext.rankComparisons.getPlanComparison(filterComparison);
            if (rankComparison != null && rankComparison.getIndex() == index && RankComparisons.matchesSort(indexExpr, candidateScan.planContext.query.getSort())) {
                ScanComparisons scanComparisons = rankComparison.getScanComparisons();
                final Set<RankComparisons.RankComparison> includedRankComparisons = new HashSet<>();
                includedRankComparisons.add(rankComparison);
                final List<QueryComponent> unsatisfiedFilters = new ArrayList<>(filters);
                unsatisfiedFilters.remove(filter);
                unsatisfiedFilters.removeAll(rankComparison.getGroupFilters());
                for (int i = 0; i < unsatisfiedFilters.size(); i++) {
                    final QueryComponent otherFilter = unsatisfiedFilters.get(i);
                    if (otherFilter instanceof QueryRecordFunctionWithComparison) {
                        final QueryRecordFunctionWithComparison otherComparison = (QueryRecordFunctionWithComparison) otherFilter;
                        final RankComparisons.RankComparison otherRank = candidateScan.planContext.rankComparisons.getPlanComparison(otherComparison);
                        if (otherRank != null) {
                            ScanComparisons mergedScanComparisons = scanComparisons.merge(otherRank.getScanComparisons());
                            if (mergedScanComparisons != null) {
                                scanComparisons = mergedScanComparisons;
                                includedRankComparisons.add(otherRank);
                                unsatisfiedFilters.remove(i--);
                            }
                        }
                    }
                }
                final RecordQueryPlan scan = rankScan(candidateScan, filterComparison, scanComparisons);
                final boolean createsDuplicates = RankComparisons.createsDuplicates(index, indexExpr);
                return new ScoredPlan(scan, unsatisfiedFilters, Collections.emptyList(), indexExpr.getColumnSize(), createsDuplicates, includedRankComparisons);
            }
        }
    }
    return null;
}
Also used : RecordQueryPlan(com.apple.foundationdb.record.query.plan.plans.RecordQueryPlan) QueryComponent(com.apple.foundationdb.record.query.expressions.QueryComponent) QueryRecordFunctionWithComparison(com.apple.foundationdb.record.query.expressions.QueryRecordFunctionWithComparison) RankComparisons(com.apple.foundationdb.record.query.plan.planning.RankComparisons) ArrayList(java.util.ArrayList) TimeWindowScanComparisons(com.apple.foundationdb.record.provider.foundationdb.leaderboard.TimeWindowScanComparisons) IndexScanComparisons(com.apple.foundationdb.record.provider.foundationdb.IndexScanComparisons) HashSet(java.util.HashSet) Nullable(javax.annotation.Nullable)

Example 3 with QueryComponent

use of com.apple.foundationdb.record.query.expressions.QueryComponent in project fdb-record-layer by FoundationDB.

the class RecordQueryPlanner method normalizeAndOrForInAsOr.

private QueryComponent normalizeAndOrForInAsOr(@Nonnull QueryComponent component) {
    if (!(component instanceof AndComponent)) {
        return component;
    }
    final AndComponent and = (AndComponent) component;
    OrComponent singleOrChild = null;
    final List<QueryComponent> otherChildren = new ArrayList<>();
    for (QueryComponent child : and.getChildren()) {
        if (child instanceof OrComponent) {
            if (singleOrChild == null) {
                singleOrChild = (OrComponent) child;
            } else {
                return and;
            }
        } else if (Query.isSingleFieldComparison(child)) {
            otherChildren.add(child);
        } else {
            return and;
        }
    }
    if (singleOrChild == null) {
        return and;
    }
    // We have exactly one OR child and the others are single field comparisons
    return OrComponent.from(distributeAnd(otherChildren, singleOrChild.getChildren()));
}
Also used : QueryComponent(com.apple.foundationdb.record.query.expressions.QueryComponent) OrComponent(com.apple.foundationdb.record.query.expressions.OrComponent) AndComponent(com.apple.foundationdb.record.query.expressions.AndComponent) ArrayList(java.util.ArrayList)

Example 4 with QueryComponent

use of com.apple.foundationdb.record.query.expressions.QueryComponent in project fdb-record-layer by FoundationDB.

the class RecordQueryPlanner method planAndWithNesting.

@Nullable
private ScoredPlan planAndWithNesting(@Nonnull CandidateScan candidateScan, @Nonnull NestingKeyExpression indexExpr, @Nonnull AndComponent filter, @Nullable KeyExpression sort) {
    final FieldKeyExpression parent = indexExpr.getParent();
    if (parent.getFanType() == FanType.None) {
        // For non-spread case, we can do a better job trying to match more than one of the filter children if
        // they have the same nesting.
        final List<QueryComponent> nestedFilters = new ArrayList<>();
        final List<QueryComponent> remainingFilters = new ArrayList<>();
        for (QueryComponent filterChild : filter.getChildren()) {
            QueryComponent filterComponent = candidateScan.planContext.rankComparisons.planComparisonSubstitute(filterChild);
            if (filterComponent instanceof NestedField) {
                final NestedField nestedField = (NestedField) filterComponent;
                if (parent.getFieldName().equals(nestedField.getFieldName())) {
                    nestedFilters.add(nestedField.getChild());
                    continue;
                }
            }
            remainingFilters.add(filterChild);
        }
        if (nestedFilters.size() > 1) {
            final NestedField nestedAnd = new NestedField(parent.getFieldName(), Query.and(nestedFilters));
            final ScoredPlan plan = planNestedField(candidateScan, indexExpr, nestedAnd, sort);
            if (plan != null) {
                if (remainingFilters.isEmpty()) {
                    return plan;
                } else {
                    return plan.withUnsatisfiedFilters(remainingFilters);
                }
            } else {
                return null;
            }
        }
    }
    List<QueryComponent> unsatisfiedFilters = new ArrayList<>(filter.getChildren());
    for (QueryComponent filterChild : filter.getChildren()) {
        QueryComponent filterComponent = candidateScan.planContext.rankComparisons.planComparisonSubstitute(filterChild);
        if (filterComponent instanceof NestedField) {
            NestedField nestedField = (NestedField) filterComponent;
            final ScoredPlan plan = planNestedField(candidateScan, indexExpr, nestedField, sort);
            if (plan != null) {
                unsatisfiedFilters.remove(filterChild);
                return plan.withUnsatisfiedFilters(unsatisfiedFilters);
            }
        }
    }
    return null;
}
Also used : NestedField(com.apple.foundationdb.record.query.expressions.NestedField) QueryComponent(com.apple.foundationdb.record.query.expressions.QueryComponent) FieldKeyExpression(com.apple.foundationdb.record.metadata.expressions.FieldKeyExpression) ArrayList(java.util.ArrayList) Nullable(javax.annotation.Nullable)

Example 5 with QueryComponent

use of com.apple.foundationdb.record.query.expressions.QueryComponent in project fdb-record-layer by FoundationDB.

the class RecordQueryPlanner method planIndex.

@Nullable
private ScoredPlan planIndex(@Nonnull PlanContext planContext, @Nonnull QueryComponent filter, @Nullable Index index, @Nonnull KeyExpression indexExpr, @Nonnull List<ScoredPlan> intersectionCandidates) {
    final KeyExpression sort = planContext.query.getSort();
    final boolean sortReverse = planContext.query.isSortReverse();
    final CandidateScan candidateScan = new CandidateScan(planContext, index, sortReverse);
    ScoredPlan p = null;
    if (index != null) {
        if (indexTypes.getRankTypes().contains(index.getType())) {
            GroupingKeyExpression grouping = (GroupingKeyExpression) indexExpr;
            p = planRank(candidateScan, index, grouping, filter);
            // Plan as just value index.
            indexExpr = grouping.getWholeKey();
        } else if (indexTypes.getTextTypes().contains(index.getType())) {
            p = planText(candidateScan, index, filter, sort);
            if (p != null) {
                p = planRemoveDuplicates(planContext, p);
            }
            if (p != null) {
                p = computeIndexFilters(planContext, p);
            }
            return p;
        } else if (indexTypes.getLuceneTypes().contains(index.getType())) {
            p = planLucene(candidateScan, index, filter, sort);
            if (p != null) {
                p = planRemoveDuplicates(planContext, p);
            }
            if (p != null) {
                p = computeIndexFilters(planContext, p);
            }
            return p;
        } else if (!indexTypes.getValueTypes().contains(index.getType())) {
            return null;
        }
    }
    if (p == null) {
        p = planCandidateScan(candidateScan, indexExpr, filter, sort);
    }
    if (p == null) {
        // we can't match the filter, but maybe the sort
        p = planSortOnly(candidateScan, indexExpr, sort);
        if (p != null) {
            final List<QueryComponent> unsatisfiedFilters = filter instanceof AndComponent ? ((AndComponent) filter).getChildren() : Collections.singletonList(filter);
            p = new ScoredPlan(0, p.plan, unsatisfiedFilters, p.createsDuplicates);
        }
    }
    if (p != null) {
        if (getConfiguration().shouldOptimizeForIndexFilters()) {
            // partition index filters
            if (index == null) {
                // if we scan without an index all filters become index filters as we don't need a fetch
                // to evaluate these filters
                p = p.withFilters(p.combineNonSargables(), Collections.emptyList());
            } else {
                p = computeIndexFilters(planContext, p);
            }
        }
    }
    if (p != null) {
        p = planRemoveDuplicates(planContext, p);
        if (p != null && p.getNumNonSargables() > 0) {
            PlanOrderingKey planOrderingKey = PlanOrderingKey.forPlan(metaData, p.plan, planContext.commonPrimaryKey);
            if (planOrderingKey != null && (sort != null || planOrderingKey.isPrimaryKeyOrdered())) {
                // If there is a sort, all chosen plans should be ordered by it and so compatible.
                // Otherwise, by requiring pkey order, we miss out on the possible intersection of
                // X < 10 AND X < 5, which should have been handled already. We gain simplicity
                // in not trying X < 10 AND Y = 5 AND Z = 'foo', where we would need to throw
                // some out as we fail to align them all.
                p.planOrderingKey = planOrderingKey;
                intersectionCandidates.add(p);
            }
        }
    }
    return p;
}
Also used : QueryComponent(com.apple.foundationdb.record.query.expressions.QueryComponent) GroupingKeyExpression(com.apple.foundationdb.record.metadata.expressions.GroupingKeyExpression) AndComponent(com.apple.foundationdb.record.query.expressions.AndComponent) RecordTypeKeyExpression(com.apple.foundationdb.record.metadata.expressions.RecordTypeKeyExpression) VersionKeyExpression(com.apple.foundationdb.record.metadata.expressions.VersionKeyExpression) GroupingKeyExpression(com.apple.foundationdb.record.metadata.expressions.GroupingKeyExpression) KeyExpression(com.apple.foundationdb.record.metadata.expressions.KeyExpression) FieldKeyExpression(com.apple.foundationdb.record.metadata.expressions.FieldKeyExpression) NestingKeyExpression(com.apple.foundationdb.record.metadata.expressions.NestingKeyExpression) EmptyKeyExpression(com.apple.foundationdb.record.metadata.expressions.EmptyKeyExpression) ThenKeyExpression(com.apple.foundationdb.record.metadata.expressions.ThenKeyExpression) Nullable(javax.annotation.Nullable)

Aggregations

QueryComponent (com.apple.foundationdb.record.query.expressions.QueryComponent)96 RecordQueryPlan (com.apple.foundationdb.record.query.plan.plans.RecordQueryPlan)58 RecordQuery (com.apple.foundationdb.record.query.RecordQuery)44 Test (org.junit.jupiter.api.Test)41 FDBRecordContext (com.apple.foundationdb.record.provider.foundationdb.FDBRecordContext)37 ArrayList (java.util.ArrayList)30 ParameterizedTest (org.junit.jupiter.params.ParameterizedTest)29 FDBQueriedRecord (com.apple.foundationdb.record.provider.foundationdb.FDBQueriedRecord)25 Index (com.apple.foundationdb.record.metadata.Index)24 Nonnull (javax.annotation.Nonnull)23 Comparisons (com.apple.foundationdb.record.query.expressions.Comparisons)22 Message (com.google.protobuf.Message)22 Query (com.apple.foundationdb.record.query.expressions.Query)20 RecordQueryPlanner (com.apple.foundationdb.record.query.plan.RecordQueryPlanner)20 List (java.util.List)20 Collections (java.util.Collections)19 Nullable (javax.annotation.Nullable)18 Assertions.assertEquals (org.junit.jupiter.api.Assertions.assertEquals)18 PlanHashable (com.apple.foundationdb.record.PlanHashable)17 Expressions.field (com.apple.foundationdb.record.metadata.Key.Expressions.field)17