use of com.apple.foundationdb.record.query.predicates.Value 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.Value in project fdb-record-layer by FoundationDB.
the class ValueIndexScanMatchCandidate method tryFetchCoveringIndexScan.
@Nonnull
private Optional<RelationalExpression> tryFetchCoveringIndexScan(@Nonnull final PartialMatch partialMatch, @Nonnull final List<ComparisonRange> comparisonRanges, final boolean isReverse) {
if (recordTypes.size() > 1) {
return Optional.empty();
}
final RecordType recordType = Iterables.getOnlyElement(recordTypes);
final IndexKeyValueToPartialRecord.Builder builder = IndexKeyValueToPartialRecord.newBuilder(recordType);
for (int i = 0; i < indexKeyValues.size(); i++) {
final Value keyValue = indexKeyValues.get(i);
if (keyValue instanceof FieldValue && keyValue.isFunctionallyDependentOn(recordValue)) {
final AvailableFields.FieldData fieldData = AvailableFields.FieldData.of(IndexKeyValueToPartialRecord.TupleSource.KEY, i);
addCoveringField(builder, (FieldValue) keyValue, fieldData);
}
}
for (int i = 0; i < indexValueValues.size(); i++) {
final Value valueValue = indexValueValues.get(i);
if (valueValue instanceof FieldValue && valueValue.isFunctionallyDependentOn(recordValue)) {
final AvailableFields.FieldData fieldData = AvailableFields.FieldData.of(IndexKeyValueToPartialRecord.TupleSource.VALUE, i);
addCoveringField(builder, (FieldValue) valueValue, fieldData);
}
}
if (!builder.isValid()) {
return Optional.empty();
}
final IndexScanParameters scanParameters = IndexScanComparisons.byValue(toScanComparisons(comparisonRanges));
final RecordQueryPlanWithIndex indexPlan = new RecordQueryIndexPlan(index.getName(), scanParameters, isReverse, false, (ValueIndexScanMatchCandidate) partialMatch.getMatchCandidate());
final RecordQueryCoveringIndexPlan coveringIndexPlan = new RecordQueryCoveringIndexPlan(indexPlan, recordType.getName(), // not used except for old planner properties
AvailableFields.NO_FIELDS, builder.build());
return Optional.of(new RecordQueryFetchFromPartialRecordPlan(coveringIndexPlan, coveringIndexPlan::pushValueThroughFetch));
}
use of com.apple.foundationdb.record.query.predicates.Value in project fdb-record-layer by FoundationDB.
the class ValueIndexScanMatchCandidate method pushValueThroughFetch.
@Nonnull
@Override
public Optional<Value> pushValueThroughFetch(@Nonnull Value value, @Nonnull QuantifiedColumnValue indexRecordColumnValue) {
final Set<Value> quantifiedColumnValues = ImmutableSet.copyOf(value.filter(v -> v instanceof QuantifiedColumnValue));
// if this is a value that is referring to more than one value from its quantifier or two multiple quantifiers
if (quantifiedColumnValues.size() != 1) {
return Optional.empty();
}
final QuantifiedColumnValue quantifiedColumnValue = (QuantifiedColumnValue) Iterables.getOnlyElement(quantifiedColumnValues);
// replace the quantified column value inside the given value with the quantified value in the match candidate
final Optional<Value> translatedValueOptional = value.translate(ImmutableMap.of(quantifiedColumnValue, QuantifiedColumnValue.of(recordValue.getAlias(), 0)));
if (!translatedValueOptional.isPresent()) {
return Optional.empty();
}
final Value translatedValue = translatedValueOptional.get();
final AliasMap equivalenceMap = AliasMap.identitiesFor(ImmutableSet.of(recordValue.getAlias()));
for (final Value matchResultValue : Iterables.concat(ImmutableList.of(recordValue), indexKeyValues, indexValueValues)) {
final Set<CorrelationIdentifier> resultValueCorrelatedTo = matchResultValue.getCorrelatedTo();
if (resultValueCorrelatedTo.size() != 1) {
continue;
}
if (translatedValue.semanticEquals(matchResultValue, equivalenceMap)) {
return matchResultValue.translate(ImmutableMap.of(QuantifiedColumnValue.of(recordValue.getAlias(), 0), indexRecordColumnValue));
}
}
return Optional.empty();
}
use of com.apple.foundationdb.record.query.predicates.Value in project fdb-record-layer by FoundationDB.
the class PushSetOperationThroughFetchRule method onMatch.
@Override
@SuppressWarnings("java:S1905")
public void onMatch(@Nonnull PlannerRuleCall call) {
final PlannerBindings bindings = call.getBindings();
final RecordQuerySetPlan setOperationPlan = bindings.get(getMatcher());
final List<? extends Quantifier.Physical> quantifiersOverFetches = bindings.getAll(quantifierOverFetchMatcher);
// if set operation is dynamic all quantifiers must have fetches
if (setOperationPlan.isDynamic()) {
if (quantifiersOverFetches.size() < setOperationPlan.getQuantifiers().size()) {
return;
}
} else {
if (quantifiersOverFetches.size() <= 1) {
// pulling up the fetch is meaningless in this case
return;
}
}
final List<? extends RecordQueryFetchFromPartialRecordPlan> fetchPlans = bindings.getAll(fetchPlanMatcher);
final ImmutableList<TranslateValueFunction> dependentFunctions = fetchPlans.stream().map(RecordQueryFetchFromPartialRecordPlan::getPushValueFunction).collect(ImmutableList.toImmutableList());
Verify.verify(quantifiersOverFetches.size() == fetchPlans.size());
Verify.verify(fetchPlans.size() == dependentFunctions.size());
final List<? extends Value> requiredValues = setOperationPlan.getRequiredValues(CorrelationIdentifier.uniqueID());
final Set<CorrelationIdentifier> pushableAliases = setOperationPlan.tryPushValues(dependentFunctions, quantifiersOverFetches, requiredValues);
// if set operation is dynamic all aliases must be pushable
if (setOperationPlan.isDynamic()) {
if (pushableAliases.size() < setOperationPlan.getQuantifiers().size()) {
return;
}
} else {
if (pushableAliases.size() <= 1) {
// pulling up the fetch is meaningless in this case
return;
}
}
final ImmutableList.Builder<Quantifier.Physical> pushableQuantifiersBuilder = ImmutableList.builder();
final ImmutableList.Builder<RecordQueryFetchFromPartialRecordPlan> pushableFetchPlansBuilder = ImmutableList.builder();
final ImmutableList.Builder<TranslateValueFunction> pushableDependentFunctionsBuilder = ImmutableList.builder();
for (int i = 0; i < quantifiersOverFetches.size(); i++) {
final Quantifier.Physical quantifier = quantifiersOverFetches.get(i);
if (pushableAliases.contains(quantifier.getAlias())) {
pushableQuantifiersBuilder.add(quantifier);
pushableFetchPlansBuilder.add(fetchPlans.get(i));
pushableDependentFunctionsBuilder.add(dependentFunctions.get(i));
}
}
final ImmutableList<Quantifier.Physical> pushableQuantifiers = pushableQuantifiersBuilder.build();
final ImmutableList<RecordQueryFetchFromPartialRecordPlan> pushableFetchPlans = pushableFetchPlansBuilder.build();
final ImmutableList<TranslateValueFunction> pushableDependentFunctions = pushableDependentFunctionsBuilder.build();
final ImmutableList<Quantifier.Physical> nonPushableQuantifiers = setOperationPlan.getQuantifiers().stream().map(quantifier -> (Quantifier.Physical) quantifier).filter(quantifier -> !pushableAliases.contains(quantifier.getAlias())).collect(ImmutableList.toImmutableList());
final List<? extends ExpressionRef<RecordQueryPlan>> newPushedInnerPlans = pushableFetchPlans.stream().map(RecordQueryFetchFromPartialRecordPlan::getChild).map(GroupExpressionRef::of).collect(ImmutableList.toImmutableList());
Verify.verify(pushableQuantifiers.size() + nonPushableQuantifiers.size() == setOperationPlan.getQuantifiers().size());
final TranslateValueFunction combinedTranslateValueFunction = setOperationPlan.pushValueFunction(pushableDependentFunctions);
final RecordQuerySetPlan newSetOperationPlan = setOperationPlan.withChildrenReferences(newPushedInnerPlans);
final RecordQueryFetchFromPartialRecordPlan newFetchPlan = new RecordQueryFetchFromPartialRecordPlan(newSetOperationPlan, combinedTranslateValueFunction);
if (nonPushableQuantifiers.isEmpty()) {
call.yield(GroupExpressionRef.of(newFetchPlan));
} else {
final List<ExpressionRef<? extends RecordQueryPlan>> newFetchPlanAndResidualInners = Streams.concat(Stream.of(GroupExpressionRef.of(newFetchPlan)), nonPushableQuantifiers.stream().map(Quantifier.Physical::getRangesOver).map(RecordQueryPlan::narrowReference)).collect(ImmutableList.toImmutableList());
call.yield(GroupExpressionRef.of(setOperationPlan.withChildrenReferences(newFetchPlanAndResidualInners)));
}
}
use of com.apple.foundationdb.record.query.predicates.Value in project fdb-record-layer by FoundationDB.
the class SelectExpression method partitionPredicates.
private static List<? extends QueryPredicate> partitionPredicates(final List<? extends QueryPredicate> predicates) {
final ImmutableList<QueryPredicate> flattenedAndPredicates = predicates.stream().flatMap(predicate -> flattenAndPredicate(predicate).stream()).collect(ImmutableList.toImmutableList());
// partition predicates in value-based predicates and non-value-based predicates
final ImmutableList.Builder<PredicateWithValue> predicateWithValuesBuilder = ImmutableList.builder();
final ImmutableList.Builder<QueryPredicate> resultPredicatesBuilder = ImmutableList.builder();
for (final QueryPredicate flattenedAndPredicate : flattenedAndPredicates) {
if (flattenedAndPredicate instanceof PredicateWithValue) {
predicateWithValuesBuilder.add((PredicateWithValue) flattenedAndPredicate);
} else {
resultPredicatesBuilder.add(flattenedAndPredicate);
}
}
final ImmutableList<PredicateWithValue> predicateWithValues = predicateWithValuesBuilder.build();
final AliasMap boundIdentitiesMap = AliasMap.identitiesFor(flattenedAndPredicates.stream().flatMap(predicate -> predicate.getCorrelatedTo().stream()).collect(ImmutableSet.toImmutableSet()));
final BoundEquivalence boundEquivalence = new BoundEquivalence(boundIdentitiesMap);
final HashMultimap<Equivalence.Wrapper<Value>, PredicateWithValue> partitionedPredicatesWithValues = predicateWithValues.stream().collect(Multimaps.toMultimap(predicate -> boundEquivalence.wrap(predicate.getValue()), Function.identity(), HashMultimap::create));
partitionedPredicatesWithValues.asMap().forEach((valueWrapper, predicatesOnValue) -> {
final Value value = Objects.requireNonNull(valueWrapper.get());
ComparisonRange resultRange = ComparisonRange.EMPTY;
for (final PredicateWithValue predicateOnValue : predicatesOnValue) {
if (predicateOnValue instanceof ValuePredicate) {
final Comparisons.Comparison comparison = ((ValuePredicate) predicateOnValue).getComparison();
final ComparisonRange.MergeResult mergeResult = resultRange.merge(comparison);
resultRange = mergeResult.getComparisonRange();
mergeResult.getResidualComparisons().forEach(residualComparison -> resultPredicatesBuilder.add(value.withComparison(residualComparison)));
} else if (predicateOnValue instanceof Sargable) {
final Sargable valueComparisonRangePredicate = (Sargable) predicateOnValue;
final ComparisonRange comparisonRange = valueComparisonRangePredicate.getComparisonRange();
final ComparisonRange.MergeResult mergeResult = resultRange.merge(comparisonRange);
resultRange = mergeResult.getComparisonRange();
mergeResult.getResidualComparisons().forEach(residualComparison -> resultPredicatesBuilder.add(value.withComparison(residualComparison)));
} else {
resultPredicatesBuilder.add(predicateOnValue);
}
}
if (!resultRange.isEmpty()) {
resultPredicatesBuilder.add(ValueComparisonRangePredicate.sargable(value, resultRange));
}
});
return resultPredicatesBuilder.build();
}
Aggregations