com.yahoo.bard.webservice.util.IntervalUtils Maven / Gradle / Ivy
Show all versions of fili-core Show documentation
// Copyright 2016 Yahoo Inc.
// Licensed under the terms of the Apache license. Please see LICENSE.md file distributed with this work for terms.
package com.yahoo.bard.webservice.util;
import com.yahoo.bard.webservice.config.SystemConfig;
import com.yahoo.bard.webservice.config.SystemConfigProvider;
import com.yahoo.bard.webservice.data.time.Granularity;
import com.yahoo.bard.webservice.data.time.StandardGranularityParser;
import com.yahoo.bard.webservice.data.time.TimeGrain;
import com.yahoo.bard.webservice.table.resolver.GranularityComparator;
import org.joda.time.DateTime;
import org.joda.time.Duration;
import org.joda.time.Interval;
import org.joda.time.base.AbstractInterval;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
/**
* Methods and Iterators which support interval slicing alignment and set operations.
*/
public class IntervalUtils {
private static final GranularityComparator COMPARATOR = GranularityComparator.getInstance();
public static final SystemConfig SYSTEM_CONFIG = SystemConfigProvider.getInstance();
public static final String ALIGNMENT_EPOCH_STRING = SYSTEM_CONFIG.getStringProperty(
SYSTEM_CONFIG.getPackageVariableName("alignment_epoch"),
"1970-01-01T00:00:00Z"
);
public static final DateTime SYSTEM_ALIGNMENT_EPOCH = new DateTime(ALIGNMENT_EPOCH_STRING);
/**
* For a given interval, return a stream of the subintervals of it that are shared with at least one interval
* in the collection of compare intervals.
*
* @param interval The interval being matched
* @param compareIntervals The collection of intervals being matched against
*
* @return Every pairwise overlap
*/
public static Stream getIntervalOverlaps(Interval interval, Collection compareIntervals) {
return compareIntervals.stream().map(interval::overlap).filter(Objects::nonNull);
}
/**
* Find all the interval overlaps between two collections of intervals.
*
* If the left set is null, return the right. This makes this usable in a reduce function.
*
* @param left The intervals being streamed over
* @param right The intervals being tested against
*
* @return A set of intervals describing the time common to both sets
*/
public static Set getOverlappingSubintervals(Collection left, Collection right) {
if (left == null) {
return new LinkedHashSet<>(right);
}
// Each interval in left is matched against all intervals in the right, collecting overlaps
return left.stream()
.flatMap(leftElement -> getIntervalOverlaps(leftElement, right))
.collect(Collectors.toSet());
}
/**
* Find all the interval overlaps between two collections of intervals.
*
* If the left set is null, return the right. This makes this usable in a reduce function.
*
* @param left The intervals being streamed over
* @param right The intervals being tested against
*
* @return A set of intervals describing the time common to both sets
*/
public static Set getOverlappingSubintervals(Set left, Set right) {
return getOverlappingSubintervals((Collection) left, (Collection) right);
}
/**
* Simplify raw intervals and return a map of intervals (dividing them by the grain) to the ordinal of the interval.
*
* @param rawIntervals A collection of intervals to be split
* @param grain The grain to split by.
*
* @return a map of simplified intervals split according to the grain to an integer that indicates their ordinal
*/
public static Map getSlicedIntervals(
Collection rawIntervals,
Granularity grain
) {
Iterable requestIterable = grain.intervalsIterable(rawIntervals);
AtomicInteger index = new AtomicInteger(0);
return StreamSupport
.stream(requestIterable.spliterator(), false)
.collect(
Collectors.toMap(
Function.identity(),
ignored -> new AtomicInteger(index.getAndIncrement()),
(x, y) -> { throw new AssertionError(); },
LinkedHashMap::new
)
);
}
/**
* Count the intervals after simplifying raw intervals and splitting by grain.
*
* @param rawIntervals A collection of intervals to be split
* @param grain The grain to split by.
*
* @return a count of intervals as split by grain
*/
public static long countSlicedIntervals(Collection rawIntervals, Granularity grain) {
Iterable requestIterable = grain.intervalsIterable(rawIntervals);
return StreamSupport.stream(requestIterable.spliterator(), false)
.count();
}
/**
* 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 bucketedIterable = granularity.intervalsIterable(bucketedIntervals);
// Predicate to find buckets which overlap
Predicate isIntersecting =
new SimplifiedIntervalList.SkippingIntervalPredicate(
supplyIntervals,
AbstractInterval::overlaps,
false
);
return StreamSupport.stream(bucketedIterable.spliterator(), false)
.filter(isIntersecting)
.collect(SimplifiedIntervalList.getCollector());
}
/**
* Sum the length of the intervals in this collection.
*
* @param intervals A collection of time intervals
*
* @return The total duration of all the intervals
*/
public static long getTotalDuration(Collection intervals) {
return new SimplifiedIntervalList(intervals).stream()
.map(Interval::toDuration)
.mapToLong(Duration::getMillis)
.sum();
}
/**
* Return the first date time instant in this set of intervals if available.
*
* @param intervalSets A collection of intervals sets
*
* @return Return the earliest date time (if any) across all the intervals contained
*/
public static Optional firstMoment(Collection extends Collection> intervalSets) {
return intervalSets.stream().flatMap(Collection::stream)
.map(Interval::getStart)
.reduce(Utils::getMinValue);
}
/**
* Find a valid timegrain for the interval based on the start and end date of the interval.
*
* @param interval The interval to find a timegrain for.
* @return the valid timegrain spanned by the interval.
*/
public static Optional getTimeGrain(Interval interval) {
return StandardGranularityParser.getDefaultGrainMap()
.values()
.stream()
.filter(granularity -> granularity instanceof TimeGrain)
.map(granularity -> (TimeGrain) granularity)
.filter(timeGrain -> timeGrain.aligns(interval))
.filter(timeGrain -> interval.getStart().plus(timeGrain.getPeriod()).equals(interval.getEnd()))
.findFirst();
}
}