use of com.apple.foundationdb.record.query.plan.temp.BoundKeyPart in project fdb-record-layer by FoundationDB.
the class AbstractDataAccessRule method maximumCoverageMatches.
/**
* Private helper method to eliminate {@link PartialMatch}es whose coverage is entirely contained in other matches
* (among the matches given).
* @param matches candidate matches
* @param interestedOrderings a set of interesting orderings
* @return a list of {@link PartialMatch}es that are the maximum coverage matches among the matches handed in
*/
@Nonnull
@SuppressWarnings({ "java:S1905", "java:S135" })
private static List<PartialMatch> maximumCoverageMatches(@Nonnull final Collection<PartialMatch> matches, @Nonnull final Set<RequestedOrdering> interestedOrderings) {
final ImmutableList<Pair<PartialMatch, Map<QueryPredicate, BoundKeyPart>>> boundKeyPartMapsForMatches = matches.stream().filter(partialMatch -> !satisfiedOrderings(partialMatch, interestedOrderings).isEmpty()).map(partialMatch -> Pair.of(partialMatch, computeBoundKeyPartMap(partialMatch))).sorted(Comparator.comparing((Function<Pair<PartialMatch, Map<QueryPredicate, BoundKeyPart>>, Integer>) p -> p.getValue().size()).reversed()).collect(ImmutableList.toImmutableList());
final ImmutableList.Builder<PartialMatch> maximumCoverageMatchesBuilder = ImmutableList.builder();
for (int i = 0; i < boundKeyPartMapsForMatches.size(); i++) {
final PartialMatch outerMatch = boundKeyPartMapsForMatches.get(i).getKey();
final Map<QueryPredicate, BoundKeyPart> outer = boundKeyPartMapsForMatches.get(i).getValue();
boolean foundContainingInner = false;
for (int j = 0; j < boundKeyPartMapsForMatches.size(); j++) {
final Map<QueryPredicate, BoundKeyPart> inner = boundKeyPartMapsForMatches.get(j).getValue();
// check if outer is completely contained in inner
if (outer.size() >= inner.size()) {
break;
}
if (i != j) {
final boolean allContained = outer.entrySet().stream().allMatch(outerEntry -> inner.containsKey(outerEntry.getKey()));
if (allContained) {
foundContainingInner = true;
break;
}
}
}
if (!foundContainingInner) {
//
// no other partial match completely contained this one
//
maximumCoverageMatchesBuilder.add(outerMatch);
}
}
return maximumCoverageMatchesBuilder.build();
}
use of com.apple.foundationdb.record.query.plan.temp.BoundKeyPart in project fdb-record-layer by FoundationDB.
the class AbstractDataAccessRule method computeBoundKeyPartMap.
@Nonnull
private static Map<QueryPredicate, BoundKeyPart> computeBoundKeyPartMap(final PartialMatch partialMatch) {
final MatchInfo matchInfo = partialMatch.getMatchInfo();
final Map<CorrelationIdentifier, ComparisonRange> boundParameterPrefixMap = partialMatch.getBoundParameterPrefixMap();
return matchInfo.getBoundKeyParts().stream().filter(boundKeyPart -> {
// make sure that matching actually bound the part and that it can be used in a scan
return matchInfo.getParameterAliasForBoundKeyPart(boundKeyPart).map(boundParameterPrefixMap::containsKey).orElse(false);
}).peek(// make sure we got a predicate mapping
boundKeyPart -> Objects.requireNonNull(boundKeyPart.getQueryPredicate())).collect(Collectors.toMap(BoundKeyPart::getQueryPredicate, Function.identity(), (a, b) -> {
if (matchInfo.getCandidatePredicateForBoundKeyPart(a) == matchInfo.getCandidatePredicateForBoundKeyPart(b) && a.getComparisonRangeType() == b.getComparisonRangeType()) {
return a;
}
throw new RecordCoreException("merge conflict");
}, Maps::newIdentityHashMap));
}
use of com.apple.foundationdb.record.query.plan.temp.BoundKeyPart 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();
}
use of com.apple.foundationdb.record.query.plan.temp.BoundKeyPart in project fdb-record-layer by FoundationDB.
the class MatchableSortExpression method forPartialMatch.
/**
* This synthesizes a list of {@link BoundKeyPart}s from the current partial match and the ordering information
* contained in this expression. Using the list of parameter ids, each {@link BoundKeyPart} links together the
* (1) normalized key expression that originally produced the key (from index, or common primary key)
* (2) a comparison range for this parameter which is contained in the already existent partial match
* (3) the predicate on the query part that participated and bound this parameter (and implicitly was used to
* synthesize the comparison range in (2)
* (4) the candidate predicate on the candidate side that is the {@link Placeholder} for the parameter
* @param partialMatch the pre-existing partial match on {@code (expression, this)} that the caller wants to adjust.
* @return a list of bound key parts that express the order of the outgoing data stream and their respective mappings
* between query and match candidate
*/
@Nonnull
private List<BoundKeyPart> forPartialMatch(@Nonnull PartialMatch partialMatch) {
final MatchCandidate matchCandidate = partialMatch.getMatchCandidate();
final MatchInfo matchInfo = partialMatch.getMatchInfo();
final Map<CorrelationIdentifier, ComparisonRange> parameterBindingMap = matchInfo.getParameterBindingMap();
final PredicateMap accumulatedPredicateMap = matchInfo.getAccumulatedPredicateMap();
final ImmutableMap<CorrelationIdentifier, QueryPredicate> parameterBindingPredicateMap = accumulatedPredicateMap.entries().stream().filter(entry -> {
final PredicateMapping predicateMapping = entry.getValue();
return predicateMapping.getParameterAliasOptional().isPresent();
}).collect(ImmutableMap.toImmutableMap(entry -> {
final PredicateMapping predicateMapping = entry.getValue();
return Objects.requireNonNull(predicateMapping.getParameterAliasOptional().orElseThrow(() -> new RecordCoreException("parameter alias should have been set")));
}, entry -> Objects.requireNonNull(entry.getKey())));
final List<KeyExpression> normalizedKeys = matchCandidate.getAlternativeKeyExpression().normalizeKeyForPositions();
final ImmutableList.Builder<BoundKeyPart> builder = ImmutableList.builder();
final List<CorrelationIdentifier> candidateParameterIds = matchCandidate.getParameters();
for (final CorrelationIdentifier parameterId : sortParameterIds) {
final int ordinalInCandidate = candidateParameterIds.indexOf(parameterId);
Verify.verify(ordinalInCandidate >= 0);
final KeyExpression normalizedKey = normalizedKeys.get(ordinalInCandidate);
Objects.requireNonNull(parameterId);
Objects.requireNonNull(normalizedKey);
@Nullable final ComparisonRange comparisonRange = parameterBindingMap.get(parameterId);
@Nullable final QueryPredicate queryPredicate = parameterBindingPredicateMap.get(parameterId);
Verify.verify(comparisonRange == null || comparisonRange.getRangeType() == ComparisonRange.Type.EMPTY || queryPredicate != null);
builder.add(BoundKeyPart.of(normalizedKey, comparisonRange == null ? ComparisonRange.Type.EMPTY : comparisonRange.getRangeType(), queryPredicate, isReverse));
}
return builder.build();
}
Aggregations