@Override public Granularity apply(DataApiRequest request, TemplateDruidQuery templateDruidQuery) { return resolveAcceptingGrain(request, templateDruidQuery); } }
@Override public List<PhysicalTableMatcher> getMatchers(QueryPlanningConstraint requestConstraint) { return Arrays.asList( new SchemaPhysicalTableMatcher(requestConstraint), new AggregatableDimensionsMatcher(requestConstraint), new TimeAlignmentPhysicalTableMatcher(requestConstraint) ); }
/** * Copy Constructor. * * @param dataSourceConstraint The data source constraint to copy from */ protected DataSourceConstraint(DataSourceConstraint dataSourceConstraint) { this.requestDimensions = dataSourceConstraint.getRequestDimensions(); this.filterDimensions = dataSourceConstraint.getFilterDimensions(); this.metricDimensions = dataSourceConstraint.getMetricDimensions(); this.metricNames = dataSourceConstraint.getMetricNames(); this.apiFilters = dataSourceConstraint.getApiFilters(); this.allDimensions = dataSourceConstraint.getAllDimensions(); this.allDimensionNames = dataSourceConstraint.getAllDimensionNames(); this.allColumnNames = dataSourceConstraint.getAllColumnNames(); }
@Override public NoMatchFoundException noneFoundException() { String logicalTableName = requestConstraint.getLogicalTable().getName(); Set<String> logicalMetrics = requestConstraint.getLogicalMetricNames(); Set<String> dimensions = requestConstraint.getAllDimensionNames(); String grainName = requestConstraint.getMinimumGranularity().getName(); LOG.error(MESSAGE_FORMAT.logFormat(logicalTableName, dimensions, logicalMetrics, grainName)); return new NoMatchFoundException( MESSAGE_FORMAT.format(logicalTableName, dimensions, logicalMetrics, grainName)); } }
/** * Create a new <tt>PhysicalDataSourceConstraint</tt> instance with a new subset of metric names. * <p> * The new set of metric names will be an intersection between old metric names and * a user provided set of metric names * * @param metricNames The set of metric columns that are to be intersected with metric names in * <tt>this DataSourceConstraint</tt> * * @return the new <tt>PhysicalDataSourceConstraint</tt> instance with a new subset of metric names */ @Override public PhysicalDataSourceConstraint withMetricIntersection(Set<String> metricNames) { Set<String> nonIntersectingMetric = getMetricNames().stream() .filter(metricName -> !metricNames.contains(metricName)) .collect(Collectors.toSet()); Set<String> resultColumnNames = this.allColumnPhysicalNames.stream() .filter(name -> !nonIntersectingMetric.contains(name)) .collect(Collectors.toSet()); return new PhysicalDataSourceConstraint(super.withMetricIntersection(metricNames), resultColumnNames); } }
/** * Constructor. * * @param dataApiRequest <b>Data API request</b> containing the constraints information * @param templateDruidQuery Query containing metric constraint information */ public QueryPlanningConstraint( @NotNull DataApiRequest dataApiRequest, @NotNull TemplateDruidQuery templateDruidQuery ) { super(dataApiRequest, templateDruidQuery); this.logicalTable = dataApiRequest.getTable(); this.intervals = Collections.unmodifiableList(dataApiRequest.getIntervals()); this.logicalMetrics = Collections.unmodifiableSet(dataApiRequest.getLogicalMetrics()); this.minimumGranularity = new RequestQueryGranularityResolver().apply(dataApiRequest, templateDruidQuery); this.requestGranularity = dataApiRequest.getGranularity(); this.logicalMetricNames = generateLogicalMetricNames(); }
/** * Filter to a set of tables matching the rules of this resolver. * * @param candidateTables The physical tables being filtered * @param requestConstraint contains the request constraints extracted from DataApiRequest and TemplateDruidQuery * * @return a set of physical tables which all match the criteria of a request and partial query * * @throws NoMatchFoundException if any of the filters reduce the filter set to empty */ public Set<PhysicalTable> filter( Collection<PhysicalTable> candidateTables, QueryPlanningConstraint requestConstraint ) throws NoMatchFoundException { return filter(candidateTables, getMatchers(requestConstraint)); }
@Override public Boolean apply(DataSourceConstraint constraint) { Map<Dimension, Set<ApiFilter>> constraintMap = constraint.getApiFilters(); return dimensionKeySelectFilters.keySet() .stream() .allMatch(dimension -> emptyConstraintOrAnyRows(dimension, constraintMap)); }
@Override public NoMatchFoundException noneFoundException() { Set<String> aggDimensions = requestConstraint.getRequestDimensions().stream() .filter(Dimension::isAggregatable) .map(Dimension::getApiName) .collect(Collectors.toSet()); Set<String> nonAggDimensions = requestConstraint.getRequestDimensions().stream() .filter(StreamUtils.not(Dimension::isAggregatable)) .map(Dimension::getApiName) .collect(Collectors.toSet()); LOG.error(MESSAGE_FORMAT.logFormat(nonAggDimensions, aggDimensions)); return new NoMatchFoundException(MESSAGE_FORMAT.format(nonAggDimensions, aggDimensions)); } }
@Override public BinaryOperator<PhysicalTable> getBetterTableOperator(QueryPlanningConstraint requestConstraint) { List<Comparator<PhysicalTable>> comparators = new ArrayList<>(); if (BardFeatureFlag.PARTIAL_DATA.isOn()) { comparators.add( new PartialTimeComparator(requestConstraint, partialDataHandler)); comparators.add( new VolatileTimeComparator(requestConstraint, partialDataHandler, volatileIntervalsService)); } comparators.add(COMPARE_GRANULARITY); comparators.add(CARDINALITY_COMPARATOR); ChainingComparator<PhysicalTable> tableComparator = new ChainingComparator<>(comparators); return BinaryOperator.minBy(tableComparator); } }
@Override public boolean equals(final Object obj) { if (this == obj) { return true; } if (obj instanceof QueryPlanningConstraint) { QueryPlanningConstraint that = (QueryPlanningConstraint) obj; return super.equals(that) && Objects.equals(this.logicalTable, that.logicalTable) && Objects.equals(this.intervals, that.intervals) && Objects.equals(this.logicalMetrics, that.logicalMetrics) && Objects.equals(this.minimumGranularity, that.minimumGranularity) && Objects.equals(this.requestGranularity, that.requestGranularity) && Objects.equals(this.logicalMetricNames, that.logicalMetricNames); } return false; }
/** * Create a constrained copy of this table. * * @param constraint The dataSourceConstraint which narrows the view of the underlying availability * * @return a constrained table whose availability and serialization are narrowed by this constraint */ @Override public ConstrainedTable withConstraint(DataSourceConstraint constraint) { validateConstraintSchema(constraint); return new ConstrainedTable(this, new PhysicalDataSourceConstraint(constraint, getSchema())); }
@Override public int hashCode() { return Objects.hash( super.hashCode(), logicalTable, intervals, logicalMetrics, minimumGranularity, requestGranularity, logicalMetricNames ); }
/** * Performs a comparison based on how much data each table has within their volatile intervals. * * @param left The first table * @param right The second table * * @return negative if left has more data in its volatile segment than right */ @Override public int compare(PhysicalTable left, PhysicalTable right) { long leftVolatileDataDuration = getAvailableVolatileDataDuration(left); long rightVolatileDataDuration = getAvailableVolatileDataDuration(right); long mostCompleteVolatile = rightVolatileDataDuration - leftVolatileDataDuration; // A saturated cast that allows us to turn a long into an int such that any long within the range of valid // integers is preserved, but anything outside the range is rounded to Integer.MIN_VALUE or Integer.MAX_VALUE. return (int) Math.max(Math.min(Integer.MAX_VALUE, mostCompleteVolatile), Integer.MIN_VALUE); }
/** * Test whether constraints for a particular dimension are missing, empty or match rows. * * @param dimension The dimension for filtering * @param constraintMap The map of filters from the constraint object * * @return true if the constraintMap doesn't constraint this dimension or if the constraint returns rows */ private boolean emptyConstraintOrAnyRows(Dimension dimension, Map<Dimension, Set<ApiFilter>> constraintMap) { return !constraintMap.containsKey(dimension) || constraintMap.get(dimension).isEmpty() || anyRowsMatch(dimension, constraintMap.get(dimension)); }
/** * Return a stream of the partition parts, filtered by the associated DataSourceFilter. * * @param constraint A constraint which filters the partitions * * @return A stream of availabilities which participate given the constraint */ private Stream<Availability> filteredAvailabilities(PhysicalDataSourceConstraint constraint) { return availabilityFilters.entrySet().stream() .filter(entry -> entry.getValue().apply(constraint)) .map(Map.Entry::getKey); }
@Override public NoMatchFoundException noneFoundException() { LOG.error(MESSAGE_FORMAT.logFormat(logicalTableName, requestIntervals)); return new NoMatchFoundException(MESSAGE_FORMAT.format(logicalTableName, requestIntervals)); } }
/** * Return a view of the available intervals for the original source table given a constraint. * * @param constraint The constraint which limits available intervals * * @return The intervals that the table can report on */ @Override public SimplifiedIntervalList getAvailableIntervals(DataSourceConstraint constraint) { if (getConstraint().equals(constraint)) { return getAvailableIntervals(); } return getSourceTable().getAvailableIntervals(constraint); }
@Override public SimplifiedIntervalList getAvailableIntervals(DataSourceConstraint constraint) { validateConstraintSchema(constraint); return getAvailability().getAvailableIntervals(new PhysicalDataSourceConstraint(constraint, getSchema())); }
/** * Return the {@link TableName} of the dataSources which back the original source table given a constraint. * * @param constraint A constraint which may narrow the data sources participating. * * @return A set of tablenames for backing dataSources */ @Override public Set<DataSourceName> getDataSourceNames(DataSourceConstraint constraint) { if (getConstraint().equals(constraint)) { return getDataSourceNames(); } return getSourceTable().getDataSourceNames(constraint); }