use of com.apple.foundationdb.record.query.predicates.ValueComparisonRangePredicate.Placeholder in project fdb-record-layer by FoundationDB.
the class ValueIndexLikeExpansionVisitor method visitExpression.
@Nonnull
@Override
public GraphExpansion visitExpression(@Nonnull FieldKeyExpression fieldKeyExpression) {
final String fieldName = fieldKeyExpression.getFieldName();
final KeyExpression.FanType fanType = fieldKeyExpression.getFanType();
final VisitorState state = getCurrentState();
final List<String> fieldNamePrefix = state.getFieldNamePrefix();
final CorrelationIdentifier baseAlias = state.getBaseAlias();
final List<String> fieldNames = ImmutableList.<String>builder().addAll(fieldNamePrefix).add(fieldName).build();
final Value value;
final Placeholder predicate;
switch(fanType) {
case FanOut:
// explode this field and prefixes of this field
final Quantifier childBase = fieldKeyExpression.explodeField(baseAlias, fieldNamePrefix);
value = state.registerValue(QuantifiedObjectValue.of(childBase.getAlias()));
final GraphExpansion childExpansion;
if (state.isKey()) {
predicate = value.asPlaceholder(newParameterAlias());
childExpansion = GraphExpansion.ofPlaceholder(value, predicate);
} else {
childExpansion = GraphExpansion.ofResultValue(value);
}
final SelectExpression selectExpression = childExpansion.buildSelectWithBase(childBase);
final Quantifier childQuantifier = Quantifier.forEach(GroupExpressionRef.of(selectExpression));
final GraphExpansion.Sealed sealedChildExpansion = childExpansion.seal();
return sealedChildExpansion.derivedWithQuantifier(childQuantifier);
case None:
value = state.registerValue(new FieldValue(QuantifiedColumnValue.of(baseAlias, 0), fieldNames));
if (state.isKey()) {
predicate = value.asPlaceholder(newParameterAlias());
return GraphExpansion.ofPlaceholder(value, predicate);
}
return GraphExpansion.ofResultValue(value);
// TODO collect/concatenate function
case Concatenate:
default:
}
throw new UnsupportedOperationException();
}
use of com.apple.foundationdb.record.query.predicates.ValueComparisonRangePredicate.Placeholder in project fdb-record-layer by FoundationDB.
the class GraphExpansion method seal.
/**
* Method to <em>seal</em> a graph expansion in an instance of {@link Sealed}. A sealed graph expansion is immutable
* and can only be used to (repeatedly) build actual expressions.
* A graph expansion object may contain duplicate information that have been added to it by callers. That is allowed
* and supported. In fact, in most cases, duplicates among e.g. {@link QueryPredicate}s come from merging individual
* {@link GraphExpansion}s into more complex ones. This method normalizes all elements beforehand in order to
* eventually return a sealed version of itself (which is not allowed to contain duplicates).
*
* @return a sealed graph expansion
*/
@Nonnull
public Sealed seal() {
final GraphExpansion graphExpansion;
if (!placeholders.isEmpty()) {
// There may be placeholders in the current (local) expansion step that are equivalent to each other but we
// don't know that yet.
final ImmutableSet<QueryPredicate> localPredicates = ImmutableSet.copyOf(getPredicates());
final List<Placeholder> resultPlaceHolders = Lists.newArrayList(placeholders);
final List<Pair<Placeholder, Integer>> localPlaceHolderPairs = IntStream.range(0, placeholders.size()).mapToObj(i -> Pair.of(placeholders.get(i), i)).filter(p -> localPredicates.contains(p.getKey())).collect(Collectors.toList());
final List<QueryPredicate> resultPredicates = Lists.newArrayList();
for (final QueryPredicate queryPredicate : getPredicates()) {
if (queryPredicate instanceof Placeholder) {
final Placeholder localPlaceHolder = (Placeholder) queryPredicate;
final AliasMap identities = AliasMap.identitiesFor(localPlaceHolder.getCorrelatedTo());
final Iterator<Pair<Placeholder, Integer>> iterator = localPlaceHolderPairs.iterator();
int foundAtOrdinal = -1;
while (iterator.hasNext()) {
final Pair<Placeholder, Integer> currentPlaceholderPair = iterator.next();
final Placeholder currentPlaceHolder = currentPlaceholderPair.getKey();
if (localPlaceHolder.semanticEqualsWithoutParameterAlias(currentPlaceHolder, identities)) {
if (foundAtOrdinal < 0) {
foundAtOrdinal = currentPlaceholderPair.getRight();
resultPredicates.add(currentPlaceHolder);
} else {
resultPlaceHolders.set(currentPlaceholderPair.getRight(), resultPlaceHolders.get(foundAtOrdinal));
}
iterator.remove();
}
}
} else {
resultPredicates.add(queryPredicate);
}
}
graphExpansion = new GraphExpansion(resultValues, resultPredicates, getQuantifiers(), resultPlaceHolders);
} else {
graphExpansion = new GraphExpansion(resultValues, getPredicates(), getQuantifiers(), ImmutableList.of());
}
return graphExpansion.new Sealed();
}
use of com.apple.foundationdb.record.query.predicates.ValueComparisonRangePredicate.Placeholder in project fdb-record-layer by FoundationDB.
the class SelectExpression method subsumedBy.
@Nonnull
@Override
public Iterable<MatchInfo> subsumedBy(@Nonnull final RelationalExpression candidateExpression, @Nonnull final AliasMap aliasMap, @Nonnull final IdentityBiMap<Quantifier, PartialMatch> partialMatchMap) {
// TODO This method should be simplified by adding some structure to it.
final Collection<MatchInfo> matchInfos = PartialMatch.matchesFromMap(partialMatchMap);
Verify.verify(this != candidateExpression);
if (getClass() != candidateExpression.getClass()) {
return ImmutableList.of();
}
final SelectExpression otherSelectExpression = (SelectExpression) candidateExpression;
// merge parameter maps -- early out if a binding clashes
final ImmutableList<Map<CorrelationIdentifier, ComparisonRange>> parameterBindingMaps = matchInfos.stream().map(MatchInfo::getParameterBindingMap).collect(ImmutableList.toImmutableList());
final Optional<Map<CorrelationIdentifier, ComparisonRange>> mergedParameterBindingMapOptional = MatchInfo.tryMergeParameterBindings(parameterBindingMaps);
if (!mergedParameterBindingMapOptional.isPresent()) {
return ImmutableList.of();
}
final Map<CorrelationIdentifier, ComparisonRange> mergedParameterBindingMap = mergedParameterBindingMapOptional.get();
final ImmutableSet.Builder<CorrelationIdentifier> matchedCorrelatedToBuilder = ImmutableSet.builder();
// for-each quantifiers. Also keep track of all aliases the matched quantifiers are correlated to.
for (final Quantifier quantifier : getQuantifiers()) {
if (partialMatchMap.containsKeyUnwrapped(quantifier)) {
if (quantifier instanceof Quantifier.ForEach) {
// current quantifier is matched
final PartialMatch childPartialMatch = Objects.requireNonNull(partialMatchMap.getUnwrapped(quantifier));
if (!childPartialMatch.getQueryExpression().computeUnmatchedForEachQuantifiers(childPartialMatch).isEmpty()) {
return ImmutableList.of();
}
}
matchedCorrelatedToBuilder.addAll(quantifier.getCorrelatedTo());
}
}
for (final Value resultValue : getResultValues()) {
matchedCorrelatedToBuilder.addAll(resultValue.getCorrelatedTo());
}
final ImmutableSet<CorrelationIdentifier> matchedCorrelatedTo = matchedCorrelatedToBuilder.build();
if (getQuantifiers().stream().anyMatch(quantifier -> quantifier instanceof Quantifier.ForEach && !partialMatchMap.containsKeyUnwrapped(quantifier))) {
return ImmutableList.of();
}
final boolean allNonMatchedQuantifiersIndependent = getQuantifiers().stream().filter(quantifier -> !partialMatchMap.containsKeyUnwrapped(quantifier)).noneMatch(quantifier -> matchedCorrelatedTo.contains(quantifier.getAlias()));
if (!allNonMatchedQuantifiersIndependent) {
return ImmutableList.of();
}
// Loop through all for each quantifiers on the other side to ensure that they are all matched.
// If any are not matched we cannot establish a match at all.
final boolean allOtherForEachQuantifiersMatched = otherSelectExpression.getQuantifiers().stream().filter(quantifier -> quantifier instanceof Quantifier.ForEach).allMatch(quantifier -> aliasMap.containsTarget(quantifier.getAlias()));
// would help us here to make sure the additional non-matched quantifier is not eliminating records.
if (!allOtherForEachQuantifiersMatched) {
return ImmutableList.of();
}
//
// Map predicates on the query side to predicates on the candidate side. Record parameter bindings and/or
// compensations for each mapped predicate.
// A predicate on this side (the query side) can cause us to filter out rows, a mapped predicate (for that
// predicate) can only filter out fewer rows which is correct and can be compensated for. The important part
// is that we must not have predicates on the other (candidate) side at the end of this mapping process which
// would mean that the candidate eliminates records that the query side may not eliminate. If we detect that
// case we MUST not create a match.
//
final ImmutableList.Builder<Iterable<PredicateMapping>> predicateMappingsBuilder = ImmutableList.builder();
//
if (getPredicates().isEmpty()) {
final boolean allNonFiltering = otherSelectExpression.getPredicates().stream().allMatch(queryPredicate -> queryPredicate instanceof Placeholder || queryPredicate.isTautology());
if (allNonFiltering) {
return MatchInfo.tryMerge(partialMatchMap, mergedParameterBindingMap, PredicateMap.empty()).map(ImmutableList::of).orElse(ImmutableList.of());
} else {
return ImmutableList.of();
}
}
for (final QueryPredicate predicate : getPredicates()) {
final Set<PredicateMapping> impliedMappingsForPredicate = predicate.findImpliedMappings(aliasMap, otherSelectExpression.getPredicates());
predicateMappingsBuilder.add(impliedMappingsForPredicate);
}
//
// We now have a multimap from predicates on the query side to predicates on the candidate side. In the trivial
// case this multimap only contains singular mappings for a query predicate. If it doesn't we need to enumerate
// through their cross product exhaustively. Each complete and non-contradictory element of that cross product
// can lead to a match.
//
final EnumeratingIterable<PredicateMapping> crossedMappings = CrossProduct.crossProduct(predicateMappingsBuilder.build());
return IterableHelpers.flatMap(crossedMappings, predicateMappings -> {
final Set<QueryPredicate> unmappedOtherPredicates = Sets.newIdentityHashSet();
unmappedOtherPredicates.addAll(otherSelectExpression.getPredicates());
final Map<CorrelationIdentifier, ComparisonRange> parameterBindingMap = Maps.newHashMap();
final PredicateMap.Builder predicateMapBuilder = PredicateMap.builder();
for (final PredicateMapping predicateMapping : predicateMappings) {
predicateMapBuilder.put(predicateMapping.getQueryPredicate(), predicateMapping);
unmappedOtherPredicates.remove(predicateMapping.getCandidatePredicate());
final Optional<CorrelationIdentifier> parameterAliasOptional = predicateMapping.getParameterAliasOptional();
final Optional<ComparisonRange> comparisonRangeOptional = predicateMapping.getComparisonRangeOptional();
if (parameterAliasOptional.isPresent() && comparisonRangeOptional.isPresent()) {
parameterBindingMap.put(parameterAliasOptional.get(), comparisonRangeOptional.get());
}
}
//
// Last chance for unmapped predicates - if there is a placeholder or a tautology on the other side that is still
// unmapped, we can (and should) remove it from the unmapped other set now. The reasoning is that this predicate is
// not filtering so it does not cause records to be filtered that are not filtered on the query side.
//
unmappedOtherPredicates.removeIf(queryPredicate -> queryPredicate instanceof Placeholder || queryPredicate.isTautology());
if (!unmappedOtherPredicates.isEmpty()) {
return ImmutableList.of();
}
final Optional<? extends PredicateMap> predicateMapOptional = predicateMapBuilder.buildMaybe();
return predicateMapOptional.map(predicateMap -> {
final Optional<Map<CorrelationIdentifier, ComparisonRange>> allParameterBindingMapOptional = MatchInfo.tryMergeParameterBindings(ImmutableList.of(mergedParameterBindingMap, parameterBindingMap));
return allParameterBindingMapOptional.flatMap(allParameterBindingMap -> MatchInfo.tryMerge(partialMatchMap, allParameterBindingMap, predicateMap)).map(ImmutableList::of).orElse(ImmutableList.of());
}).orElse(ImmutableList.of());
});
}
use of com.apple.foundationdb.record.query.predicates.ValueComparisonRangePredicate.Placeholder in project fdb-record-layer by FoundationDB.
the class ValueIndexLikeExpansionVisitor method visitExpression.
@Nonnull
@Override
public GraphExpansion visitExpression(@Nonnull final KeyExpressionWithValue keyExpressionWithValue) {
final VisitorState state = getCurrentState();
final Value value = state.registerValue(keyExpressionWithValue.toValue(state.getBaseAlias(), state.getFieldNamePrefix()));
if (state.isKey()) {
final Placeholder predicate = value.asPlaceholder(newParameterAlias());
return GraphExpansion.ofPlaceholder(value, predicate);
}
return GraphExpansion.ofResultValue(value);
}
use of com.apple.foundationdb.record.query.predicates.ValueComparisonRangePredicate.Placeholder 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