/** * Takes one or more lists of intervals, and combines them into a single, sorted list with the minimum number of * intervals needed to capture exactly the same instants as the original intervals. * <p> * If any subintervals of the input collection abut or overlap they will be replaced with a single, combined * interval. * <p> * Examples: * <ul> * <li>['2014/2017', '2015/2020'] will combine into ['2014/2020'] * <li>['2015/2016', '2016/2017'] will combine into ['2015/2017] * <li>['2015/2016', '2013/2014'] will sort into ['2013/2014', '2015/2016'] * <li>['2015/2015', '2015/2016', '2012/2013'] will sort and combine to ['2012/2013', '2015/2016'] * </ul> * @param intervals The collection(s) of intervals being collated * * @return A single list of sorted intervals simplified to the smallest number of intervals able to describe the * duration */ @SafeVarargs public static SimplifiedIntervalList simplifyIntervals(Collection<Interval>... intervals) { Stream<Interval> allIntervals = Stream.empty(); for (Collection<Interval> intervalCollection : intervals) { allIntervals = Stream.concat(allIntervals, intervalCollection.stream()); } return allIntervals .sorted(IntervalStartComparator.INSTANCE::compare) .collect(getCollector()); }
@Override public SimplifiedIntervalList apply(DruidAggregationQuery<?> druidAggregationQuery) { LookbackQuery castQuery = (LookbackQuery) druidAggregationQuery; return Stream.concat( castQuery.getIntervals().stream(), castQuery.getIntervals().stream() .flatMap(interval -> castQuery.getLookbackOffsets().stream() .map(lookbackOffset -> getCohortInterval(interval, lookbackOffset)) ) ).collect(SimplifiedIntervalList.getCollector()); }
/** * Collect all subintervals of an interval list of a grain bucketed size which are subintervals of another supply * list of intervals. * * @param supplyIntervals The interval collection to match bucketedIntervals against * @param bucketedIntervals The grain bucketed intervals to collect if they overlap the supply * @param granularity Grain at which to split the bucketingIntervals * * @return a simplified list of subintervals of the bucketedIntervals list */ public static SimplifiedIntervalList collectBucketedIntervalsIntersectingIntervalList( SimplifiedIntervalList supplyIntervals, SimplifiedIntervalList bucketedIntervals, Granularity granularity ) { // Stream the from intervals, split by grain Iterable<Interval> bucketedIterable = granularity.intervalsIterable(bucketedIntervals); // Predicate to find buckets which overlap Predicate<Interval> isIntersecting = new SimplifiedIntervalList.SkippingIntervalPredicate( supplyIntervals, AbstractInterval::overlaps, false ); return StreamSupport.stream(bucketedIterable.spliterator(), false) .filter(isIntersecting) .collect(SimplifiedIntervalList.getCollector()); } /**
/** * Collect all subintervals from a bucketed collection that are not subintervals of a supply. * <p> * The bucketed list of intervals are split by grain before being tested as subintervals of the supply list. * * @param supplyIntervals The intervals which bucketed intervals are being tested against * @param bucketedIntervals The grain bucketed intervals to collect if not in the supply * @param granularity The grain at which to split the bucketingIntervals * * @return a simplified list of intervals reflecting the intervals in the fromSet which do not appear in the * remove set */ protected static SimplifiedIntervalList collectBucketedIntervalsNotInIntervalList( SimplifiedIntervalList supplyIntervals, SimplifiedIntervalList bucketedIntervals, Granularity granularity ) { // Stream the from intervals, split by grain Iterable<Interval> bucketIterable = granularity.intervalsIterable(bucketedIntervals); // Not in returns true if any part of the stream interval is not 'covered' by the remove intervals. Predicate<Interval> notIn = new SimplifiedIntervalList.IsSubinterval(supplyIntervals).negate(); return StreamSupport.stream(bucketIterable.spliterator(), false) .filter(notIn) .collect(SimplifiedIntervalList.getCollector()); } }