use of com.apple.foundationdb.record.query.expressions.QueryComponent in project fdb-record-layer by FoundationDB.
the class RecordQueryPlanner method plan.
/**
* Create a plan to get the results of the provided query.
*
* @param query a query for records on this planner's metadata
* @param parameterRelationshipGraph a set of bindings and their relationships that provide additional information
* to the planner that may improve plan quality but may also tighten requirements imposed on the parameter
* bindings that are used to execute the query
* @return a plan that will return the results of the provided query when executed
* @throws com.apple.foundationdb.record.RecordCoreException if there is no index that matches the sort in the provided query
*/
@Nonnull
@Override
public RecordQueryPlan plan(@Nonnull RecordQuery query, @Nonnull ParameterRelationshipGraph parameterRelationshipGraph) {
query.validate(metaData);
final PlanContext planContext = getPlanContext(query);
final BooleanNormalizer normalizer = BooleanNormalizer.forConfiguration(configuration);
final QueryComponent queryFilter = query.getFilter();
final QueryComponent filter = normalizer.normalizeIfPossible(queryFilter == null ? null : queryFilter.withParameterRelationshipMap(parameterRelationshipGraph));
final KeyExpression sort = query.getSort();
final boolean sortReverse = query.isSortReverse();
RecordQueryPlan plan = plan(planContext, filter, sort, sortReverse);
if (plan == null) {
if (sort == null) {
throw new RecordCoreException("Unexpected failure to plan without sort");
}
final RecordQueryPlannerSortConfiguration sortConfiguration = configuration.getSortConfiguration();
if (sortConfiguration != null && sortConfiguration.shouldAllowNonIndexSort(query)) {
final PlanContext withoutSort = new PlanContext(query.toBuilder().setSort(null).build(), planContext.indexes, planContext.commonPrimaryKey);
plan = plan(withoutSort, filter, null, false);
if (plan == null) {
throw new RecordCoreException("Unexpected failure to plan without sort");
}
plan = new RecordQuerySortPlan(plan, sortConfiguration.getSortKey(sort, sortReverse));
} else {
throw new RecordCoreException("Cannot sort without appropriate index: " + sort);
}
}
if (query.getRequiredResults() != null) {
plan = tryToConvertToCoveringPlan(planContext, plan);
}
if (timer != null) {
plan.logPlanStructure(timer);
}
if (plan.getComplexity() > configuration.getComplexityThreshold()) {
throw new RecordQueryPlanComplexityException(plan);
}
if (logger.isTraceEnabled()) {
logger.trace(KeyValueLogMessage.of("explain of plan", "explain", PlannerGraphProperty.explain(plan)));
}
return plan;
}
use of com.apple.foundationdb.record.query.expressions.QueryComponent in project fdb-record-layer by FoundationDB.
the class RecordQueryPlanner method normalizeAndOr.
@Nonnull
private // Distribute it across a disjunction so that we can union complex index lookups.
QueryComponent normalizeAndOr(AndComponent and) {
if (and.getChildren().size() == 2) {
QueryComponent child1 = and.getChildren().get(0);
QueryComponent child2 = and.getChildren().get(1);
if (child1 instanceof OrComponent && Query.isSingleFieldComparison(child2)) {
return OrComponent.from(distributeAnd(Collections.singletonList(child2), ((OrComponent) child1).getChildren()));
}
if (child2 instanceof OrComponent && Query.isSingleFieldComparison(child1)) {
return OrComponent.from(distributeAnd(Collections.singletonList(child1), ((OrComponent) child2).getChildren()));
}
}
return and;
}
use of com.apple.foundationdb.record.query.expressions.QueryComponent in project fdb-record-layer by FoundationDB.
the class RecordQueryPlanner method distributeAnd.
private List<QueryComponent> distributeAnd(List<QueryComponent> predicatesToDistribute, List<QueryComponent> children) {
List<QueryComponent> distributed = new ArrayList<>();
for (QueryComponent child : children) {
List<QueryComponent> conjuncts = new ArrayList<>(2);
conjuncts.addAll(predicatesToDistribute);
if (child instanceof AndComponent) {
conjuncts.addAll(((AndComponent) child).getChildren());
} else {
conjuncts.add(child);
}
child = AndComponent.from(conjuncts);
distributed.add(child);
}
return distributed;
}
use of com.apple.foundationdb.record.query.expressions.QueryComponent in project fdb-record-layer by FoundationDB.
the class RecordQueryPlanner method planOr.
@Nullable
private ScoredPlan planOr(@Nonnull PlanContext planContext, @Nonnull OrComponent filter) {
if (filter.getChildren().isEmpty()) {
return null;
}
List<ScoredPlan> subplans = new ArrayList<>(filter.getChildren().size());
boolean allHaveOrderingKey = true;
RecordQueryPlan commonFilteredBasePlan = null;
boolean allHaveSameBasePlan = true;
for (QueryComponent subfilter : filter.getChildren()) {
ScoredPlan subplan = planFilter(planContext, subfilter, true);
if (subplan == null) {
return null;
}
if (subplan.planOrderingKey == null) {
allHaveOrderingKey = false;
}
RecordQueryPlan filteredBasePlan;
if (subplan.plan instanceof RecordQueryFilterPlan) {
filteredBasePlan = ((RecordQueryFilterPlan) subplan.plan).getInnerPlan();
} else {
filteredBasePlan = null;
}
if (subplans.isEmpty()) {
commonFilteredBasePlan = filteredBasePlan;
allHaveSameBasePlan = filteredBasePlan != null;
} else if (allHaveSameBasePlan && !Objects.equals(filteredBasePlan, commonFilteredBasePlan)) {
allHaveSameBasePlan = false;
}
subplans.add(subplan);
}
// out there over the equivalent OR(EQUALS) filters.
if (allHaveSameBasePlan) {
final RecordQueryPlan combinedOrFilter = new RecordQueryFilterPlan(commonFilteredBasePlan, new OrComponent(subplans.stream().map(subplan -> ((RecordQueryFilterPlan) subplan.plan).getConjunctedFilter()).collect(Collectors.toList())));
ScoredPlan firstSubPlan = subplans.get(0);
return new ScoredPlan(combinedOrFilter, Collections.emptyList(), Collections.emptyList(), firstSubPlan.score, firstSubPlan.createsDuplicates, firstSubPlan.includedRankComparisons);
}
// neither removes duplicates nor requires the children be in order.
if (allHaveOrderingKey) {
final ScoredPlan orderedUnionPlan = planOrderedUnion(planContext, subplans);
if (orderedUnionPlan != null) {
return orderedUnionPlan;
}
}
final ScoredPlan unorderedUnionPlan = planUnorderedUnion(planContext, subplans);
if (unorderedUnionPlan != null) {
return planRemoveDuplicates(planContext, unorderedUnionPlan);
}
return null;
}
use of com.apple.foundationdb.record.query.expressions.QueryComponent in project fdb-record-layer by FoundationDB.
the class RankComparisons method findComparison.
private void findComparison(@Nonnull QueryRecordFunctionWithComparison comparison, @Nonnull List<Index> indexes, @Nonnull List<QueryComponent> potentialGroupFilters, @Nonnull AtomicInteger counter) {
RecordFunction<?> recordFunction = comparison.getFunction();
// TODO: Should share with indexMaintainerForAggregateFunction
// TODO: Move index-specific query planning behavior outside of planner (https://github.com/FoundationDB/fdb-record-layer/issues/17)
List<String> requiredIndexTypes;
if (recordFunction.getName().equals(FunctionNames.RANK)) {
requiredIndexTypes = Arrays.asList(IndexTypes.RANK, IndexTypes.TIME_WINDOW_LEADERBOARD);
} else if (recordFunction.getName().equals(FunctionNames.TIME_WINDOW_RANK)) {
requiredIndexTypes = Collections.singletonList(IndexTypes.TIME_WINDOW_LEADERBOARD);
} else {
requiredIndexTypes = null;
}
if (requiredIndexTypes != null) {
final GroupingKeyExpression operand = ((IndexRecordFunction) recordFunction).getOperand();
Optional<Index> matchingIndex = indexes.stream().filter(index -> requiredIndexTypes.contains(index.getType()) && index.getRootExpression().equals(operand)).min(Comparator.comparing(Index::getColumnSize));
if (matchingIndex.isPresent()) {
final KeyExpression groupBy = operand.getGroupingSubKey();
final List<QueryComponent> groupFilters = new ArrayList<>();
final List<Comparisons.Comparison> groupComparisons = new ArrayList<>();
if (!GroupingValidator.findGroupKeyFilters(potentialGroupFilters, groupBy, groupFilters, groupComparisons)) {
return;
}
QueryComponent substitute = null;
String bindingName = null;
final Comparisons.Type comparisonType = comparison.getComparison().getType();
if (!operand.createsDuplicates() && !comparisonType.isUnary()) {
bindingName = Bindings.Internal.RANK.bindingName(Integer.toString(counter.getAndIncrement()));
Comparisons.Comparison substituteComparison = new Comparisons.ParameterComparison(comparisonType, bindingName, Bindings.Internal.RANK);
final KeyExpression grouped = operand.getGroupedSubKey();
if (grouped instanceof FieldKeyExpression) {
substitute = new FieldWithComparison(((FieldKeyExpression) grouped).getFieldName(), substituteComparison);
} else if (grouped instanceof NestingKeyExpression) {
NestingKeyExpression nesting = (NestingKeyExpression) grouped;
if (nesting.getChild() instanceof FieldKeyExpression) {
substitute = new NestedField(nesting.getParent().getFieldName(), new FieldWithComparison(((FieldKeyExpression) nesting.getChild()).getFieldName(), substituteComparison));
}
}
if (substitute == null) {
bindingName = null;
}
}
comparisons.put(comparison, new RankComparison(comparison, matchingIndex.get(), groupFilters, groupComparisons, substitute, bindingName));
}
}
}
Aggregations