package com.appmetr.hercules.partition.dated; import com.appmetr.hercules.partition.TopKeyPartiotionProvider; import com.appmetr.hercules.wide.SliceDataSpecificator; import com.appmetr.hercules.wide.SliceDataSpecificatorByCF; import org.joda.time.*; import org.joda.time.format.DateTimeFormat; import org.joda.time.format.DateTimeFormatter; import java.util.*; public class DatePartiotionProvider<T extends DatedColumn> extends TopKeyPartiotionProvider<T> { private DatePartsConfig partsConfig; public DatePartiotionProvider(DatePartsConfig partsConfig) { this.partsConfig = partsConfig; } @Override public String getPartition(T topKey) { return getDatePartition(topKey.getDate(), partsConfig); } @Override public List<SliceDataSpecificatorByCF<T>> getPartitionedQueries(SliceDataSpecificator<T> sliceDataSpecificator) { List<SliceDataSpecificatorByCF<T>> queries = getDatePartitionedQueries(sliceDataSpecificator, partsConfig); List<SliceDataSpecificatorByCF<T>> rowQueries = new ArrayList<SliceDataSpecificatorByCF<T>>(); for (SliceDataSpecificatorByCF<T> query : queries) { rowQueries.add(new SliceDataSpecificatorByCF<T>(query.getPartitionName(), query.getSliceDataSpecificator())); } return rowQueries; } @Override public List<String> getPartitionsForCreation() { List<String> foundPartitions = new ArrayList<String>(); List<DatePartition> partitions = getPartitionsList(partsConfig); for (DatePartition partition : partitions) { long middlePoint = partition.middlePoint(); String cfPartition = getDatePartition(middlePoint, partsConfig); foundPartitions.add(cfPartition); } return foundPartitions; } private <T extends DatedColumn<T>> List<SliceDataSpecificatorByCF<T>> getDatePartitionedQueries( SliceDataSpecificator<T> sliceDataSpecificator, DatePartsConfig partsConfig) { List<SliceDataSpecificatorByCF<T>> parts = new ArrayList<SliceDataSpecificatorByCF<T>>(); List<DatePartition> partitions = getPartitionsList(partsConfig); if (sliceDataSpecificator.getType() == SliceDataSpecificator.SliceDataSpecificatorType.RANGE) { DateSegment<T> sliceSegment = new DateSegment<T>(sliceDataSpecificator); for (DatePartition partition : partitions) { DateSegment<T> partitionSegment = new DateSegment<T>(partition.getFrom().getMillis(), partition.getTo().getMillis()); DateSegment<T> intersectSegment = partitionSegment.intersection(sliceSegment); if (intersectSegment.isValid()) { SliceDataSpecificator<T> intersectSlice = intersectSegment.toSliceDataSpecificator(sliceDataSpecificator.isOrderDesc(), 0); parts.add(new SliceDataSpecificatorByCF<T>( getPartitionName(partition), intersectSlice )); } } if (sliceDataSpecificator.isOrderDesc()) { List<SliceDataSpecificatorByCF<T>> reverseParts = new ArrayList<SliceDataSpecificatorByCF<T>>(parts.size()); for (int i = parts.size() - 1; i >= 0; i--) { reverseParts.add(parts.get(i)); } parts = reverseParts; } } else if (sliceDataSpecificator.getType() == SliceDataSpecificator.SliceDataSpecificatorType.COLUMNS) { TreeSet<T> columns = sliceDataSpecificator.getColumnsArray() != null ? new TreeSet<T>(Arrays.asList(sliceDataSpecificator.getColumnsArray())) : new TreeSet<T>(sliceDataSpecificator.getColumnsCollection()); for (DatePartition partition : partitions) { if (columns.size() == 0) { break; } Set<T> partColumns = new TreeSet<T>(); for (T column : columns) { if (column.getDate() >= partition.getFrom().getMillis() && column.getDate() <= partition.getTo().getMillis()) { partColumns.add(column); } } if (partColumns.size() > 0) { columns.removeAll(partColumns); parts.add(new SliceDataSpecificatorByCF<T>( getPartitionName(partition), new SliceDataSpecificator<T>(partColumns) )); } } } else { throw new IllegalStateException("Invalid type: " + sliceDataSpecificator.getType()); } return parts; } private List<DatePartition> getPartitionsList(DatePartsConfig partsConfig) { DateTime endByCurrTime = ceilDateToPeriod(new DateTime(DateTimeZone.UTC), partsConfig.getPartsField(), 1); endByCurrTime = endByCurrTime.property(partsConfig.getPartsField()).addToCopy(partsConfig.getPartsForward()); DateTime from = new DateTime(0, DateTimeZone.UTC); DateTime to = partsConfig.getPartsStart().withZone(DateTimeZone.UTC); List<DatePartition> partitions = new ArrayList<DatePartition>(); while (!to.isAfter(endByCurrTime)) { partitions.add(new DatePartition(from, to.minusMillis(1))); from = to; to = ceilDateToPeriodPlus(to, partsConfig.getPartsField(), 1); } return partitions; } private String getDatePartition(long column, DatePartsConfig partsConfig) { List<DatePartition> partitions = getPartitionsList(partsConfig); for (DatePartition partition : partitions) { DateSegment segment = new DateSegment(partition.getFrom().getMillis(), partition.getTo().getMillis()); try { if (segment.contains(column)) { return getPartitionName(partition); } } catch (RuntimeException e) { throw new RuntimeException("" + column + " >> " + segment, e); } } throw new IllegalArgumentException("Column is out of partitions range: " + new DateTime(column, DateTimeZone.UTC)); } protected String getPartitionName(DatePartition partition) { if (partition.getFrom().withZone(DateTimeZone.UTC).getMillis() == 0) { return ""; } return dateTimeToStr(partition.getFrom()) + dateTimeToStr(partition.getTo().plusMillis(1)); } protected String dateTimeToStr(DateTime dateTime) { DateTimeFormatter formatter = DateTimeFormat.forPattern("yyMMdd"); return formatter.print(dateTime.withZone(DateTimeZone.UTC)); } public static DateTime ceilDateToPeriod(ReadableDateTime time, DateTimeFieldType fieldType, int count) { MutableDateTime roundUp = new MutableDateTime(time).property(fieldType).roundCeiling(); int mod = time.get(fieldType) % count; if (mod > 0) { roundUp.add(fieldType.getDurationType(), count - mod); } return roundUp.toDateTime(); } public static DateTime ceilDateToPeriodPlus(ReadableDateTime time, DateTimeFieldType fieldType, int count) { MutableDateTime roundUp = new MutableDateTime(time).property(fieldType).roundCeiling(); int mod = time.get(fieldType) % count; roundUp.add(fieldType.getDurationType(), count - mod); return roundUp.toDateTime(); } }