Search in sources :

Example 11 with Ordering

use of com.apple.foundationdb.record.query.plan.temp.Ordering in project fdb-record-layer by FoundationDB.

the class AbstractDataAccessRule method onMatch.

/**
 * Method that does the leg work to create the appropriate expression dag for data access using value indexes or
 * value index-like scans (primary scans).
 *
 * Conceptually we do the following work:
 *
 * <ul>
 * <li> This method yields a scan plan for each matching primary candidate ({@link PrimaryScanMatchCandidate}).
 *      There is only ever going to be exactly one {@link PrimaryScanMatchCandidate} for a primary key. Due to the
 *      candidate being solely based upon a primary key, the match structure is somewhat limited. In essence, there
 *      is an implicit guarantee that we can always create a primary scan for a data source.
 * </li>
 * <li> This method yields an index scan plan for each matching value index candidate
 *      ({@link ValueIndexScanMatchCandidate}).
 * </li>
 * <li> This method yields the combinatorial expansion of intersections of distinct-ed index scan plans.
 * </li>
 * </ul>
 *
 * The work described above is semantically correct in a sense that it creates a search space that can be explored
 * and pruned in suitable ways that will eventually converge into an optimal data access plan.
 *
 * We can choose to create an index scan for every index that is available regardless what the coverage
 * of an index is. The coverage of an index is a measurement that tells us how well an index can answer what a
 * filter (or by extension a query) asks for. For instance, a high number of search arguments used in the index scan
 * can be associated with high coverage (as in the index scan covers more of the query) and vice versa.
 *
 * Similarly, we can choose to create the intersection of all possible combinations of suitable scans over indexes
 * (that we have matches for). Since we create a logical intersection of these access plans we can leave it up to
 * the respective implementation rules (e.g., {@link ImplementIntersectionRule}) to do the right thing and implement
 * the physical plan for the intersection if possible (e.g. ensuring compatibly ordered legs, etc.).
 *
 * In fact, the two before-mentioned approaches are completely valid with respect to correctness of the plan and
 * the guaranteed creation of the optimal plan. However, in reality using this approach, although valid and probably
 * the conceptually better and more orthogonal approach, will result in a ballooning of the search space very quickly.
 * While that may be acceptable for group-by engines and only few index access paths, in an OLTP world where there
 * are potentially dozens of indexes, memory footprint and the sheer number of tasks that would be created for
 * subsequent exploration and implementation of all these alternatives make the purist approach to planning these
 * indexes infeasible.
 *
 * Thus we would like to eliminate unnecessary exploration by avoiding variations we know can never be successful
 * either in creating a successful executable plan (e.g. logical expression may not ever be able to produce a
 * compatible ordering) or cannot ever create an optimal plan. In a nutshell, we try to utilize additional
 * information that is available in addition to the matching partition in order to make decisions about which
 * expression variation to create and which to avoid:
 *
 * <ul>
 * <li> For a matching primary scan candidate ({@link PrimaryScanMatchCandidate})
 *      we will not create a primary scan if the scan is incompatible with an interesting order that has been
 *      communicated downwards in the graph.
 * </li>
 * <li> For a matching index scan candidate ({@link ValueIndexScanMatchCandidate})
 *      we will not create an index scan if the scan is incompatible with an interesting order that has been
 *      communicated downwards in the graph.
 * </li>
 * <li> We will only create a scan if there is no other index scan with a greater coverage (think of coverage
 *      as the assumed amount of filtering or currently the number of bound predicates) for the search arguments
 *      which are bound by the query.
 *      For instance, an index scan {@code INDEX SCAN(i1, a = [5, 5], b = [10, 10])} is still planned along
 *      {@code INDEX SCAN(i2, x = ["hello", "hello"], y = ["world", "world"], z = [10, inf])} even though
 *      the latter utilizes three search arguments while the former one only uses two. However, an index scan
 *      {@code INDEX SCAN(i1, a = [5, 5], b = [10, 10])} is not created (and yielded) if there we also
 *      have a choice to plan {@code INDEX SCAN(i2, b = [10, 10], a = [5, 5], c = ["Guten", "Morgen"])} as that
 *      index {@code i2} has a higher coverage compared to {@code i1} <em>and</em> all bound arguments in the scan
 *      over {@code i2} are also bound in the scan over {@code i1}.
 * <li>
 *      We will only create intersections of scans if we can already establish that the logical intersection
 *      can be implemented by a {@link com.apple.foundationdb.record.query.plan.plans.RecordQueryIntersectionPlan}.
 *      That requires that the legs of the intersection are compatibly ordered <em>and</em> that that ordering follows
 *      a potentially required ordering.
 * </li>
 * </ul>
 *
 * @param call the call associated with this planner rule execution
 */
@Override
@SuppressWarnings("java:S135")
public void onMatch(@Nonnull PlannerRuleCall call) {
    final PlannerBindings bindings = call.getBindings();
    final List<? extends PartialMatch> completeMatches = bindings.getAll(getCompleteMatchMatcher());
    final R expression = bindings.get(getExpressionMatcher());
    // 
    if (completeMatches.isEmpty()) {
        return;
    }
    // 
    // return if there is no pre-determined interesting ordering
    // 
    final Optional<Set<RequestedOrdering>> requestedOrderingsOptional = call.getInterestingProperty(OrderingAttribute.ORDERING);
    if (requestedOrderingsOptional.isEmpty()) {
        return;
    }
    final Set<RequestedOrdering> requestedOrderings = requestedOrderingsOptional.get();
    // 
    // group matches by candidates
    // 
    final LinkedHashMap<MatchCandidate, ? extends ImmutableList<? extends PartialMatch>> completeMatchMap = completeMatches.stream().collect(Collectors.groupingBy(PartialMatch::getMatchCandidate, LinkedHashMap::new, ImmutableList.toImmutableList()));
    // find the best match for a candidate as there may be more than one due to partial matching
    final ImmutableSet<PartialMatch> maximumCoverageMatchPerCandidate = completeMatchMap.entrySet().stream().flatMap(entry -> {
        final List<? extends PartialMatch> completeMatchesForCandidate = entry.getValue();
        final Optional<? extends PartialMatch> bestMatchForCandidateOptional = completeMatchesForCandidate.stream().max(Comparator.comparing(PartialMatch::getNumBoundParameterPrefix));
        return bestMatchForCandidateOptional.map(Stream::of).orElse(Stream.empty());
    }).collect(ImmutableSet.toImmutableSet());
    final List<PartialMatch> bestMaximumCoverageMatches = maximumCoverageMatches(maximumCoverageMatchPerCandidate, requestedOrderings);
    if (bestMaximumCoverageMatches.isEmpty()) {
        return;
    }
    // create scans for all best matches
    final Map<PartialMatch, RelationalExpression> bestMatchToExpressionMap = createScansForMatches(bestMaximumCoverageMatches);
    final ExpressionRef<RelationalExpression> toBeInjectedReference = GroupExpressionRef.empty();
    // create single scan accesses
    for (final PartialMatch bestMatch : bestMaximumCoverageMatches) {
        final RelationalExpression dataAccessAndCompensationExpression = compensateSingleDataAccess(bestMatch, bestMatchToExpressionMap.get(bestMatch));
        toBeInjectedReference.insert(dataAccessAndCompensationExpression);
    }
    final Map<PartialMatch, RelationalExpression> bestMatchToDistinctExpressionMap = distinctMatchToScanMap(bestMatchToExpressionMap);
    @Nullable final KeyExpression commonPrimaryKey = call.getContext().getCommonPrimaryKey();
    if (commonPrimaryKey != null) {
        final var commonPrimaryKeyParts = commonPrimaryKey.normalizeKeyForPositions();
        final var boundPartitions = Lists.<List<PartialMatch>>newArrayList();
        // create intersections for all n choose k partitions from k = 2 .. n
        IntStream.range(2, bestMaximumCoverageMatches.size() + 1).mapToObj(k -> ChooseK.chooseK(bestMaximumCoverageMatches, k)).flatMap(iterable -> StreamSupport.stream(iterable.spliterator(), false)).forEach(boundPartitions::add);
        boundPartitions.stream().flatMap(partition -> createIntersectionAndCompensation(commonPrimaryKeyParts, bestMatchToDistinctExpressionMap, partition, requestedOrderings).stream()).forEach(toBeInjectedReference::insert);
    }
    call.yield(inject(expression, completeMatches, toBeInjectedReference));
}
Also used : PlannerRuleCall(com.apple.foundationdb.record.query.plan.temp.PlannerRuleCall) OrderingAttribute(com.apple.foundationdb.record.query.plan.temp.OrderingAttribute) CascadesPlanner(com.apple.foundationdb.record.query.plan.temp.CascadesPlanner) LinkedIdentitySet(com.apple.foundationdb.record.query.plan.temp.LinkedIdentitySet) GroupExpressionRef(com.apple.foundationdb.record.query.plan.temp.GroupExpressionRef) PartialMatch(com.apple.foundationdb.record.query.plan.temp.PartialMatch) Pair(org.apache.commons.lang3.tuple.Pair) RecordCoreException(com.apple.foundationdb.record.RecordCoreException) ComparisonRange(com.apple.foundationdb.record.query.plan.temp.ComparisonRange) Map(java.util.Map) IndexScanExpression(com.apple.foundationdb.record.query.plan.temp.expressions.IndexScanExpression) MatchCandidate(com.apple.foundationdb.record.query.plan.temp.MatchCandidate) PrimaryScanExpression(com.apple.foundationdb.record.query.plan.temp.expressions.PrimaryScanExpression) RequestedOrdering(com.apple.foundationdb.record.query.plan.temp.RequestedOrdering) KeyExpression(com.apple.foundationdb.record.metadata.expressions.KeyExpression) ImmutableSet(com.google.common.collect.ImmutableSet) ImmutableMap(com.google.common.collect.ImmutableMap) Collection(java.util.Collection) MatchPartition(com.apple.foundationdb.record.query.plan.temp.MatchPartition) Set(java.util.Set) PlannerBindings(com.apple.foundationdb.record.query.plan.temp.matchers.PlannerBindings) ReferencedFieldsAttribute(com.apple.foundationdb.record.query.plan.temp.ReferencedFieldsAttribute) Collectors(java.util.stream.Collectors) Objects(java.util.Objects) BoundKeyPart(com.apple.foundationdb.record.query.plan.temp.BoundKeyPart) List(java.util.List) Stream(java.util.stream.Stream) CorrelationIdentifier(com.apple.foundationdb.record.query.plan.temp.CorrelationIdentifier) MatchInfo(com.apple.foundationdb.record.query.plan.temp.MatchInfo) Optional(java.util.Optional) API(com.apple.foundationdb.annotation.API) IntStream(java.util.stream.IntStream) Iterables(com.google.common.collect.Iterables) PlannerRule(com.apple.foundationdb.record.query.plan.temp.PlannerRule) Quantifier(com.apple.foundationdb.record.query.plan.temp.Quantifier) Ordering(com.apple.foundationdb.record.query.plan.temp.Ordering) Function(java.util.function.Function) Key(com.apple.foundationdb.record.metadata.Key) LinkedHashMap(java.util.LinkedHashMap) Lists(com.google.common.collect.Lists) ImmutableList(com.google.common.collect.ImmutableList) Compensation(com.apple.foundationdb.record.query.plan.temp.Compensation) StreamSupport(java.util.stream.StreamSupport) ExpressionRef(com.apple.foundationdb.record.query.plan.temp.ExpressionRef) Nonnull(javax.annotation.Nonnull) Nullable(javax.annotation.Nullable) ChooseK(com.apple.foundationdb.record.query.combinatorics.ChooseK) LogicalIntersectionExpression(com.apple.foundationdb.record.query.plan.temp.expressions.LogicalIntersectionExpression) Iterator(java.util.Iterator) LogicalDistinctExpression(com.apple.foundationdb.record.query.plan.temp.expressions.LogicalDistinctExpression) PartialOrder(com.apple.foundationdb.record.query.combinatorics.PartialOrder) QueryPredicate(com.apple.foundationdb.record.query.predicates.QueryPredicate) KeyPart(com.apple.foundationdb.record.query.plan.temp.KeyPart) Maps(com.google.common.collect.Maps) RelationalExpression(com.apple.foundationdb.record.query.plan.temp.RelationalExpression) BindingMatcher(com.apple.foundationdb.record.query.plan.temp.matchers.BindingMatcher) PrimaryScanMatchCandidate(com.apple.foundationdb.record.query.plan.temp.PrimaryScanMatchCandidate) Comparator(java.util.Comparator) ValueIndexScanMatchCandidate(com.apple.foundationdb.record.query.plan.temp.ValueIndexScanMatchCandidate) RelationalExpression(com.apple.foundationdb.record.query.plan.temp.RelationalExpression) LinkedIdentitySet(com.apple.foundationdb.record.query.plan.temp.LinkedIdentitySet) ImmutableSet(com.google.common.collect.ImmutableSet) Set(java.util.Set) Optional(java.util.Optional) KeyExpression(com.apple.foundationdb.record.metadata.expressions.KeyExpression) RequestedOrdering(com.apple.foundationdb.record.query.plan.temp.RequestedOrdering) PartialMatch(com.apple.foundationdb.record.query.plan.temp.PartialMatch) PlannerBindings(com.apple.foundationdb.record.query.plan.temp.matchers.PlannerBindings) MatchCandidate(com.apple.foundationdb.record.query.plan.temp.MatchCandidate) PrimaryScanMatchCandidate(com.apple.foundationdb.record.query.plan.temp.PrimaryScanMatchCandidate) ValueIndexScanMatchCandidate(com.apple.foundationdb.record.query.plan.temp.ValueIndexScanMatchCandidate) List(java.util.List) ImmutableList(com.google.common.collect.ImmutableList) Stream(java.util.stream.Stream) IntStream(java.util.stream.IntStream) Nullable(javax.annotation.Nullable)

Example 12 with Ordering

use of com.apple.foundationdb.record.query.plan.temp.Ordering in project fdb-record-layer by FoundationDB.

the class AbstractDataAccessRule method createIntersectionAndCompensation.

/**
 * Private helper method to plan an intersection and subsequently compensate it using the partial match structures
 * kept for all participating data accesses.
 * Planning the data access and its compensation for a given match is a two-step approach as we compute
 * the compensation for intersections by intersecting the {@link Compensation} for the single data accesses first
 * before using the resulting {@link Compensation} to compute the compensating expression for the entire
 * intersection.
 * @param commonPrimaryKeyParts normalized common primary key
 * @param matchToExpressionMap a map from match to single data access expression
 * @param partition a partition (i.e. a list of {@link PartialMatch}es that the caller would like to compute
 *        and intersected data access for
 * @param requestedOrderings a set of ordering that have been requested by consuming expressions/plan operators
 * @return a optional containing a new {@link RelationalExpression} that represents the data access and its
 *         compensation, {@code Optional.empty()} if this method was unable to compute the intersection expression
 */
@Nonnull
private static List<RelationalExpression> createIntersectionAndCompensation(@Nonnull final List<KeyExpression> commonPrimaryKeyParts, @Nonnull final Map<PartialMatch, RelationalExpression> matchToExpressionMap, @Nonnull final List<PartialMatch> partition, @Nonnull final Set<RequestedOrdering> requestedOrderings) {
    final var expressionsBuilder = ImmutableList.<RelationalExpression>builder();
    final var orderingPartialOrder = intersectionOrdering(partition);
    final ImmutableSet<BoundKeyPart> equalityBoundKeyParts = partition.stream().map(partialMatch -> partialMatch.getMatchInfo().getBoundKeyParts()).flatMap(boundOrderingKeyParts -> boundOrderingKeyParts.stream().filter(boundOrderingKey -> boundOrderingKey.getComparisonRangeType() == ComparisonRange.Type.EQUALITY)).collect(ImmutableSet.toImmutableSet());
    for (final var requestedOrdering : requestedOrderings) {
        final var satisfyingOrderingPartsOptional = Ordering.satisfiesKeyPartsOrdering(orderingPartialOrder, requestedOrdering.getOrderingKeyParts(), BoundKeyPart::getKeyPart);
        final var comparisonKeyOptional = satisfyingOrderingPartsOptional.map(parts -> parts.stream().filter(part -> !equalityBoundKeyParts.contains(part)).collect(ImmutableList.toImmutableList())).flatMap(parts -> comparisonKey(commonPrimaryKeyParts, equalityBoundKeyParts, parts));
        if (comparisonKeyOptional.isEmpty()) {
            continue;
        }
        final KeyExpression comparisonKey = comparisonKeyOptional.get();
        final var compensation = partition.stream().map(partialMatch -> partialMatch.compensate(partialMatch.getBoundParameterPrefixMap())).reduce(Compensation.impossibleCompensation(), Compensation::intersect);
        final ImmutableList<RelationalExpression> scans = partition.stream().map(partialMatch -> Objects.requireNonNull(matchToExpressionMap.get(partialMatch))).collect(ImmutableList.toImmutableList());
        final var logicalIntersectionExpression = LogicalIntersectionExpression.from(scans, comparisonKey);
        final var compensatedIntersection = compensation.isNeeded() ? compensation.apply(GroupExpressionRef.of(logicalIntersectionExpression)) : logicalIntersectionExpression;
        expressionsBuilder.add(compensatedIntersection);
    }
    return expressionsBuilder.build();
}
Also used : PlannerRuleCall(com.apple.foundationdb.record.query.plan.temp.PlannerRuleCall) OrderingAttribute(com.apple.foundationdb.record.query.plan.temp.OrderingAttribute) CascadesPlanner(com.apple.foundationdb.record.query.plan.temp.CascadesPlanner) LinkedIdentitySet(com.apple.foundationdb.record.query.plan.temp.LinkedIdentitySet) GroupExpressionRef(com.apple.foundationdb.record.query.plan.temp.GroupExpressionRef) PartialMatch(com.apple.foundationdb.record.query.plan.temp.PartialMatch) Pair(org.apache.commons.lang3.tuple.Pair) RecordCoreException(com.apple.foundationdb.record.RecordCoreException) ComparisonRange(com.apple.foundationdb.record.query.plan.temp.ComparisonRange) Map(java.util.Map) IndexScanExpression(com.apple.foundationdb.record.query.plan.temp.expressions.IndexScanExpression) MatchCandidate(com.apple.foundationdb.record.query.plan.temp.MatchCandidate) PrimaryScanExpression(com.apple.foundationdb.record.query.plan.temp.expressions.PrimaryScanExpression) RequestedOrdering(com.apple.foundationdb.record.query.plan.temp.RequestedOrdering) KeyExpression(com.apple.foundationdb.record.metadata.expressions.KeyExpression) ImmutableSet(com.google.common.collect.ImmutableSet) ImmutableMap(com.google.common.collect.ImmutableMap) Collection(java.util.Collection) MatchPartition(com.apple.foundationdb.record.query.plan.temp.MatchPartition) Set(java.util.Set) PlannerBindings(com.apple.foundationdb.record.query.plan.temp.matchers.PlannerBindings) ReferencedFieldsAttribute(com.apple.foundationdb.record.query.plan.temp.ReferencedFieldsAttribute) Collectors(java.util.stream.Collectors) Objects(java.util.Objects) BoundKeyPart(com.apple.foundationdb.record.query.plan.temp.BoundKeyPart) List(java.util.List) Stream(java.util.stream.Stream) CorrelationIdentifier(com.apple.foundationdb.record.query.plan.temp.CorrelationIdentifier) MatchInfo(com.apple.foundationdb.record.query.plan.temp.MatchInfo) Optional(java.util.Optional) API(com.apple.foundationdb.annotation.API) IntStream(java.util.stream.IntStream) Iterables(com.google.common.collect.Iterables) PlannerRule(com.apple.foundationdb.record.query.plan.temp.PlannerRule) Quantifier(com.apple.foundationdb.record.query.plan.temp.Quantifier) Ordering(com.apple.foundationdb.record.query.plan.temp.Ordering) Function(java.util.function.Function) Key(com.apple.foundationdb.record.metadata.Key) LinkedHashMap(java.util.LinkedHashMap) Lists(com.google.common.collect.Lists) ImmutableList(com.google.common.collect.ImmutableList) Compensation(com.apple.foundationdb.record.query.plan.temp.Compensation) StreamSupport(java.util.stream.StreamSupport) ExpressionRef(com.apple.foundationdb.record.query.plan.temp.ExpressionRef) Nonnull(javax.annotation.Nonnull) Nullable(javax.annotation.Nullable) ChooseK(com.apple.foundationdb.record.query.combinatorics.ChooseK) LogicalIntersectionExpression(com.apple.foundationdb.record.query.plan.temp.expressions.LogicalIntersectionExpression) Iterator(java.util.Iterator) LogicalDistinctExpression(com.apple.foundationdb.record.query.plan.temp.expressions.LogicalDistinctExpression) PartialOrder(com.apple.foundationdb.record.query.combinatorics.PartialOrder) QueryPredicate(com.apple.foundationdb.record.query.predicates.QueryPredicate) KeyPart(com.apple.foundationdb.record.query.plan.temp.KeyPart) Maps(com.google.common.collect.Maps) RelationalExpression(com.apple.foundationdb.record.query.plan.temp.RelationalExpression) BindingMatcher(com.apple.foundationdb.record.query.plan.temp.matchers.BindingMatcher) PrimaryScanMatchCandidate(com.apple.foundationdb.record.query.plan.temp.PrimaryScanMatchCandidate) Comparator(java.util.Comparator) ValueIndexScanMatchCandidate(com.apple.foundationdb.record.query.plan.temp.ValueIndexScanMatchCandidate) RelationalExpression(com.apple.foundationdb.record.query.plan.temp.RelationalExpression) Compensation(com.apple.foundationdb.record.query.plan.temp.Compensation) KeyExpression(com.apple.foundationdb.record.metadata.expressions.KeyExpression) BoundKeyPart(com.apple.foundationdb.record.query.plan.temp.BoundKeyPart) Nonnull(javax.annotation.Nonnull)

Example 13 with Ordering

use of com.apple.foundationdb.record.query.plan.temp.Ordering in project fdb-record-layer by FoundationDB.

the class OrderingProperty method deriveForInJoinFromOrderings.

@SuppressWarnings("java:S135")
public static Optional<Ordering> deriveForInJoinFromOrderings(@Nonnull final List<Optional<Ordering>> orderingOptionals, @Nonnull final RecordQueryInJoinPlan inJoinPlan) {
    final Optional<Ordering> childOrderingOptional = Iterables.getOnlyElement(orderingOptionals);
    if (childOrderingOptional.isPresent()) {
        final var childOrdering = childOrderingOptional.get();
        final var equalityBoundKeyMap = childOrdering.getEqualityBoundKeyMap();
        final var inSource = inJoinPlan.getInSource();
        final CorrelationIdentifier inAlias = inJoinPlan.getInAlias();
        final SetMultimap<KeyExpression, Comparisons.Comparison> resultEqualityBoundKeyMap = HashMultimap.create(equalityBoundKeyMap);
        KeyExpression inKeyExpression = null;
        for (final var entry : equalityBoundKeyMap.entries()) {
            // TODO we only look for the first entry that matches. That is enough for the in-to-join case,
            // however, it is possible that more than one different key expressions are equality-bound
            // by this in. That would constitute to more than one concurrent order which we cannot
            // express at the moment (we need the PartialOrder approach for that).
            final var comparison = entry.getValue();
            final var correlatedTo = comparison.getCorrelatedTo();
            if (correlatedTo.size() != 1) {
                continue;
            }
            if (inAlias.equals(Iterables.getOnlyElement(correlatedTo))) {
                inKeyExpression = entry.getKey();
                resultEqualityBoundKeyMap.removeAll(inKeyExpression);
                break;
            }
        }
        if (inKeyExpression == null || !inSource.isSorted()) {
            // 
            return Optional.of(new Ordering(resultEqualityBoundKeyMap, ImmutableList.of(), false));
        }
        // 
        // Prepend the existing order with the key expression we just found.
        // 
        final var resultOrderingKeyPartsBuilder = ImmutableList.<KeyPart>builder();
        resultOrderingKeyPartsBuilder.add(KeyPart.of(inKeyExpression, inSource.isReverse()));
        resultOrderingKeyPartsBuilder.addAll(childOrdering.getOrderingKeyParts());
        return Optional.of(new Ordering(resultEqualityBoundKeyMap, resultOrderingKeyPartsBuilder.build(), childOrdering.isDistinct()));
    } else {
        return Optional.empty();
    }
}
Also used : CorrelationIdentifier(com.apple.foundationdb.record.query.plan.temp.CorrelationIdentifier) RequestedOrdering(com.apple.foundationdb.record.query.plan.temp.RequestedOrdering) Ordering(com.apple.foundationdb.record.query.plan.temp.Ordering) KeyExpression(com.apple.foundationdb.record.metadata.expressions.KeyExpression) KeyPart(com.apple.foundationdb.record.query.plan.temp.KeyPart)

Example 14 with Ordering

use of com.apple.foundationdb.record.query.plan.temp.Ordering in project fdb-record-layer by FoundationDB.

the class OrderingProperty method fromKeyAndScanComparisons.

@Nonnull
private static Optional<Ordering> fromKeyAndScanComparisons(@Nullable final KeyExpression keyExpression, @Nonnull final ScanComparisons scanComparisons, final boolean isReverse, final boolean isDistinct) {
    if (keyExpression == null) {
        return Optional.empty();
    }
    final ImmutableSetMultimap.Builder<KeyExpression, Comparisons.Comparison> equalityBoundKeyMapBuilder = ImmutableSetMultimap.builder();
    final List<KeyExpression> normalizedKeyExpressions = keyExpression.normalizeKeyForPositions();
    final List<Comparisons.Comparison> equalityComparisons = scanComparisons.getEqualityComparisons();
    for (int i = 0; i < equalityComparisons.size(); i++) {
        final KeyExpression normalizedKeyExpression = normalizedKeyExpressions.get(i);
        final Comparisons.Comparison comparison = equalityComparisons.get(i);
        equalityBoundKeyMapBuilder.put(normalizedKeyExpression, comparison);
    }
    final ImmutableList.Builder<KeyPart> result = ImmutableList.builder();
    for (int i = scanComparisons.getEqualitySize(); i < normalizedKeyExpressions.size(); i++) {
        final KeyExpression currentKeyExpression = normalizedKeyExpressions.get(i);
        // 
        // Note that it is not really important here if the keyExpression can be normalized in a lossless way
        // or not. A key expression containing repeated fields is sort-compatible with its normalized key
        // expression. We used to refuse to compute the sort order in the presence of repeats, however,
        // I think that restriction can be relaxed.
        // 
        result.add(KeyPart.of(currentKeyExpression, isReverse));
    }
    return Optional.of(new Ordering(equalityBoundKeyMapBuilder.build(), result.build(), isDistinct));
}
Also used : ImmutableSetMultimap(com.google.common.collect.ImmutableSetMultimap) ImmutableList(com.google.common.collect.ImmutableList) KeyExpression(com.apple.foundationdb.record.metadata.expressions.KeyExpression) KeyPart(com.apple.foundationdb.record.query.plan.temp.KeyPart) ScanComparisons(com.apple.foundationdb.record.query.plan.ScanComparisons) Comparisons(com.apple.foundationdb.record.query.expressions.Comparisons) RequestedOrdering(com.apple.foundationdb.record.query.plan.temp.RequestedOrdering) Ordering(com.apple.foundationdb.record.query.plan.temp.Ordering) Nonnull(javax.annotation.Nonnull)

Example 15 with Ordering

use of com.apple.foundationdb.record.query.plan.temp.Ordering in project fdb-record-layer by FoundationDB.

the class OrderingProperty method deriveForInUnionFromOrderings.

public static Optional<Ordering> deriveForInUnionFromOrderings(@Nonnull final List<Optional<Ordering>> orderingOptionals, @Nonnull final RecordQueryInUnionPlan inUnionPlan) {
    final Optional<Ordering> childOrderingOptional = Iterables.getOnlyElement(orderingOptionals);
    if (childOrderingOptional.isPresent()) {
        final var childOrdering = childOrderingOptional.get();
        final SetMultimap<KeyExpression, Comparisons.Comparison> equalityBoundKeyMap = childOrdering.getEqualityBoundKeyMap();
        final KeyExpression comparisonKey = inUnionPlan.getComparisonKey();
        final SetMultimap<KeyExpression, Comparisons.Comparison> resultEqualityBoundKeyMap = HashMultimap.create(equalityBoundKeyMap);
        final ImmutableList.Builder<KeyPart> resultKeyPartBuilder = ImmutableList.builder();
        final List<KeyExpression> normalizedComparisonKeys = comparisonKey.normalizeKeyForPositions();
        for (final KeyExpression normalizedKeyExpression : normalizedComparisonKeys) {
            resultKeyPartBuilder.add(KeyPart.of(normalizedKeyExpression, inUnionPlan.isReverse()));
        }
        final var sourceAliases = inUnionPlan.getInSources().stream().map(inSource -> CorrelationIdentifier.of(CORRELATION.identifier(inSource.getBindingName()))).collect(ImmutableSet.toImmutableSet());
        for (final var entry : equalityBoundKeyMap.entries()) {
            final var correlatedTo = entry.getValue().getCorrelatedTo();
            if (correlatedTo.stream().anyMatch(sourceAliases::contains)) {
                resultEqualityBoundKeyMap.removeAll(entry.getKey());
            }
        }
        return Optional.of(new Ordering(resultEqualityBoundKeyMap, resultKeyPartBuilder.build(), childOrdering.isDistinct()));
    } else {
        return Optional.empty();
    }
}
Also used : RecordQueryIntersectionPlan(com.apple.foundationdb.record.query.plan.plans.RecordQueryIntersectionPlan) PlannerProperty(com.apple.foundationdb.record.query.plan.temp.PlannerProperty) HashMultimap(com.google.common.collect.HashMultimap) Pair(org.apache.commons.lang3.tuple.Pair) RecordQueryScanPlan(com.apple.foundationdb.record.query.plan.plans.RecordQueryScanPlan) RecordQueryUnionPlan(com.apple.foundationdb.record.query.plan.plans.RecordQueryUnionPlan) RecordQueryUnorderedUnionPlan(com.apple.foundationdb.record.query.plan.plans.RecordQueryUnorderedUnionPlan) PrimaryScanExpression(com.apple.foundationdb.record.query.plan.temp.expressions.PrimaryScanExpression) ImmutableSetMultimap(com.google.common.collect.ImmutableSetMultimap) RequestedOrdering(com.apple.foundationdb.record.query.plan.temp.RequestedOrdering) KeyExpression(com.apple.foundationdb.record.metadata.expressions.KeyExpression) ImmutableSet(com.google.common.collect.ImmutableSet) Collection(java.util.Collection) RecordQueryPredicatesFilterPlan(com.apple.foundationdb.record.query.plan.plans.RecordQueryPredicatesFilterPlan) Collectors(java.util.stream.Collectors) BinaryOperator(java.util.function.BinaryOperator) RecordQueryCoveringIndexPlan(com.apple.foundationdb.record.query.plan.plans.RecordQueryCoveringIndexPlan) List(java.util.List) Stream(java.util.stream.Stream) RecordQueryInUnionPlan(com.apple.foundationdb.record.query.plan.plans.RecordQueryInUnionPlan) CorrelationIdentifier(com.apple.foundationdb.record.query.plan.temp.CorrelationIdentifier) RecordQueryInJoinPlan(com.apple.foundationdb.record.query.plan.plans.RecordQueryInJoinPlan) PlanContext(com.apple.foundationdb.record.query.plan.temp.PlanContext) Optional(java.util.Optional) API(com.apple.foundationdb.annotation.API) CORRELATION(com.apple.foundationdb.record.Bindings.Internal.CORRELATION) RecordMetaData(com.apple.foundationdb.record.RecordMetaData) Iterables(com.google.common.collect.Iterables) ValuePredicate(com.apple.foundationdb.record.query.predicates.ValuePredicate) Quantifier(com.apple.foundationdb.record.query.plan.temp.Quantifier) Ordering(com.apple.foundationdb.record.query.plan.temp.Ordering) RecordQueryPlan(com.apple.foundationdb.record.query.plan.plans.RecordQueryPlan) LogicalSortExpression(com.apple.foundationdb.record.query.plan.temp.expressions.LogicalSortExpression) Key(com.apple.foundationdb.record.metadata.Key) ImmutableList(com.google.common.collect.ImmutableList) ExpressionRef(com.apple.foundationdb.record.query.plan.temp.ExpressionRef) FieldValue(com.apple.foundationdb.record.query.predicates.FieldValue) Nonnull(javax.annotation.Nonnull) Nullable(javax.annotation.Nullable) RecordQueryIndexPlan(com.apple.foundationdb.record.query.plan.plans.RecordQueryIndexPlan) RecordQueryPlanWithIndex(com.apple.foundationdb.record.query.plan.plans.RecordQueryPlanWithIndex) ScanComparisons(com.apple.foundationdb.record.query.plan.ScanComparisons) KeyPart(com.apple.foundationdb.record.query.plan.temp.KeyPart) SetMultimap(com.google.common.collect.SetMultimap) RelationalExpression(com.apple.foundationdb.record.query.plan.temp.RelationalExpression) ValueIndexExpansionVisitor(com.apple.foundationdb.record.query.plan.temp.ValueIndexExpansionVisitor) Comparisons(com.apple.foundationdb.record.query.expressions.Comparisons) RecordType(com.apple.foundationdb.record.metadata.RecordType) Index(com.apple.foundationdb.record.metadata.Index) ImmutableList(com.google.common.collect.ImmutableList) RequestedOrdering(com.apple.foundationdb.record.query.plan.temp.RequestedOrdering) Ordering(com.apple.foundationdb.record.query.plan.temp.Ordering) KeyExpression(com.apple.foundationdb.record.metadata.expressions.KeyExpression) KeyPart(com.apple.foundationdb.record.query.plan.temp.KeyPart)

Aggregations

KeyPart (com.apple.foundationdb.record.query.plan.temp.KeyPart)15 Ordering (com.apple.foundationdb.record.query.plan.temp.Ordering)15 RequestedOrdering (com.apple.foundationdb.record.query.plan.temp.RequestedOrdering)14 KeyExpression (com.apple.foundationdb.record.metadata.expressions.KeyExpression)13 ImmutableList (com.google.common.collect.ImmutableList)13 Nonnull (javax.annotation.Nonnull)12 API (com.apple.foundationdb.annotation.API)11 Quantifier (com.apple.foundationdb.record.query.plan.temp.Quantifier)11 ImmutableSet (com.google.common.collect.ImmutableSet)11 Iterables (com.google.common.collect.Iterables)11 Collection (java.util.Collection)11 List (java.util.List)11 Optional (java.util.Optional)11 Collectors (java.util.stream.Collectors)11 Pair (org.apache.commons.lang3.tuple.Pair)11 RecordQueryPlan (com.apple.foundationdb.record.query.plan.plans.RecordQueryPlan)10 CorrelationIdentifier (com.apple.foundationdb.record.query.plan.temp.CorrelationIdentifier)10 Key (com.apple.foundationdb.record.metadata.Key)9 CORRELATION (com.apple.foundationdb.record.Bindings.Internal.CORRELATION)8 Comparisons (com.apple.foundationdb.record.query.expressions.Comparisons)7