blob: 58cda989643485e1b6df7df47cdce927ca92b2c7 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.iotdb.tsfile.read.filter.operator;
import org.apache.iotdb.tsfile.read.filter.basic.Filter;
import org.apache.iotdb.tsfile.read.filter.basic.OperatorType;
import org.apache.iotdb.tsfile.utils.ReadWriteIOUtils;
import org.apache.iotdb.tsfile.utils.TimeDuration;
import java.io.DataOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Objects;
import java.util.TimeZone;
import java.util.concurrent.TimeUnit;
import static org.apache.iotdb.tsfile.utils.TimeDuration.calcPositiveIntervalByMonth;
import static org.apache.iotdb.tsfile.utils.TimeDuration.getConsecutiveTimesIntervalByMonth;
/**
* GroupByMonthFilter is used to handle natural month slidingStep and interval by generating
* dynamically. Attention: it's only supported to access in ascending order now.
*/
public class GroupByMonthFilter extends GroupByFilter {
private final Calendar calendar = Calendar.getInstance();
private final TimeDuration originalSlidingStep;
private final TimeDuration originalInterval;
// These fields will be serialized to remote nodes, as other fields may be updated during process
private TimeZone timeZone;
private final long originalStartTime;
private final long originalEndTime;
private final TimeUnit currPrecision;
private long[] startTimes;
public GroupByMonthFilter(
long startTime,
long endTime,
TimeDuration interval,
TimeDuration slidingStep,
TimeZone timeZone,
TimeUnit currPrecision) {
super(startTime, endTime);
this.originalStartTime = startTime;
this.originalEndTime = endTime;
this.originalInterval = interval;
this.originalSlidingStep = slidingStep;
if (!interval.containsMonth()) {
this.interval = interval.nonMonthDuration;
}
if (!slidingStep.containsMonth()) {
this.slidingStep = slidingStep.nonMonthDuration;
}
this.currPrecision = currPrecision;
initMonthGroupByParameters(timeZone);
}
public GroupByMonthFilter(ByteBuffer buffer) {
this(
ReadWriteIOUtils.readLong(buffer),
ReadWriteIOUtils.readLong(buffer),
TimeDuration.deserialize(buffer),
TimeDuration.deserialize(buffer),
TimeZone.getTimeZone(ReadWriteIOUtils.readString(buffer)),
TimeUnit.values()[ReadWriteIOUtils.readInt(buffer)]);
}
@Override
public boolean timeSatisfy(long time) {
if (time < originalStartTime || time >= endTime) {
return false;
} else if (time >= startTime && time < startTime + slidingStep) {
return time - startTime < interval;
} else {
int count = getTimePointPosition(time);
getNthTimeInterval(count);
return time - startTime < interval;
}
}
@Override
public boolean satisfyStartEndTime(long startTime, long endTime) {
if (satisfyCurrentInterval(startTime, endTime)) {
return true;
} else {
// get the interval which contains the start time
int count = getTimePointPosition(startTime);
getNthTimeInterval(count);
// judge two adjacent intervals
if (satisfyCurrentInterval(startTime, endTime)) {
return true;
} else {
getNthTimeInterval(count + 1);
return satisfyCurrentInterval(startTime, endTime);
}
}
}
private boolean satisfyCurrentInterval(long startTime, long endTime) {
if (endTime < this.startTime || startTime >= this.endTime) {
return false;
} else {
return startTime - this.startTime < interval;
}
}
@Override
public boolean containStartEndTime(long startTime, long endTime) {
if (isContainedByCurrentInterval(startTime, endTime)) {
return true;
} else {
// get the interval which contains the start time
int count = getTimePointPosition(startTime);
getNthTimeInterval(count);
// judge single interval that contains start time
return isContainedByCurrentInterval(startTime, endTime);
}
}
private boolean isContainedByCurrentInterval(long startTime, long endTime) {
if (startTime < this.startTime || endTime > this.endTime) {
return false;
} else {
return startTime - this.startTime < interval && endTime - this.startTime < interval;
}
}
private void initMonthGroupByParameters(TimeZone timeZone) {
calendar.setTimeZone(timeZone);
this.timeZone = timeZone;
calendar.setTimeInMillis(startTime);
if (originalSlidingStep.containsMonth()) {
startTimes =
getConsecutiveTimesIntervalByMonth(
startTime,
originalSlidingStep,
(int)
Math.ceil(
((originalEndTime - originalStartTime)
/ (double) originalSlidingStep.getMinTotalDuration(currPrecision))),
timeZone,
currPrecision);
}
getNthTimeInterval(0);
}
/** Get the interval that @param time belongs to. */
private int getTimePointPosition(long time) {
if (originalSlidingStep.containsMonth()) {
int searchResult = Arrays.binarySearch(startTimes, time);
return searchResult >= 0 ? searchResult : Math.max(0, Math.abs(searchResult) - 2);
} else {
return (int) ((time - originalStartTime) / slidingStep);
}
}
/** get the Nth time interval. */
private void getNthTimeInterval(int n) {
// get interval and sliding step
if (originalSlidingStep.containsMonth()) {
if (n < 0 || n > startTimes.length - 1) {
this.interval = -1;
return;
}
this.startTime = startTimes[n];
this.slidingStep =
calcPositiveIntervalByMonth(
originalStartTime, originalSlidingStep.multiple(n + 1), timeZone, currPrecision)
- startTime;
} else {
this.startTime = originalStartTime + n * slidingStep;
}
if (originalInterval.containsMonth()) {
this.interval =
calcPositiveIntervalByMonth(
originalStartTime,
originalSlidingStep.multiple(n).merge(originalInterval),
timeZone,
currPrecision)
- startTime;
}
}
@Override
public OperatorType getOperatorType() {
return OperatorType.GROUP_BY_MONTH;
}
@Override
public void serialize(DataOutputStream outputStream) throws IOException {
ReadWriteIOUtils.write(getOperatorType().ordinal(), outputStream);
ReadWriteIOUtils.write(originalStartTime, outputStream);
ReadWriteIOUtils.write(originalEndTime, outputStream);
originalInterval.serialize(outputStream);
originalSlidingStep.serialize(outputStream);
ReadWriteIOUtils.write(timeZone.getID(), outputStream);
ReadWriteIOUtils.write(currPrecision.ordinal(), outputStream);
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
if (!super.equals(o)) {
return false;
}
GroupByMonthFilter that = (GroupByMonthFilter) o;
return originalStartTime == that.originalStartTime
&& originalEndTime == that.originalEndTime
&& originalSlidingStep.equals(that.originalSlidingStep)
&& originalInterval.equals(that.originalInterval)
&& timeZone.equals(that.timeZone)
&& currPrecision == that.currPrecision
&& Arrays.equals(startTimes, that.startTimes);
}
@Override
public int hashCode() {
int result =
Objects.hash(
super.hashCode(),
originalSlidingStep,
originalInterval,
timeZone,
originalStartTime,
originalEndTime,
currPrecision);
result = 31 * result + Arrays.hashCode(startTimes);
return result;
}
@Override
public Filter copy() {
// A stateful filter must implement copy() method
// to make sure that each filter has its own state.
return new GroupByMonthFilter(
originalStartTime,
originalEndTime,
originalInterval,
originalSlidingStep,
timeZone,
currPrecision);
}
}