blob: 61224a9df02aea53c7a2b1ed9ec22e7f9205deca [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.file.metadata.statistics;
import org.apache.iotdb.tsfile.exception.filter.StatisticsClassException;
import org.apache.iotdb.tsfile.exception.write.UnknownColumnTypeException;
import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType;
import org.apache.iotdb.tsfile.read.filter.basic.Filter;
import org.apache.iotdb.tsfile.utils.Binary;
import org.apache.iotdb.tsfile.utils.ReadWriteForEncodingUtils;
import org.apache.iotdb.tsfile.utils.ReadWriteIOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Serializable;
import java.nio.ByteBuffer;
import java.util.Objects;
/**
* This class is used for recording statistic information of each measurement in a delta file. While
* writing processing, the processor records the statistics information. Statistics includes
* maximum, minimum and null value count up to version 0.0.1.<br>
* Each data type extends this Statistic as super class.<br>
* <br>
* For the statistics in the Unseq file TimeSeriesMetadata, only firstValue, lastValue, startTime
* and endTime can be used.</br>
*/
public abstract class Statistics<T extends Serializable> {
private static final Logger LOG = LoggerFactory.getLogger(Statistics.class);
/**
* isEmpty being false means this statistic has been initialized and the max and min is not null.
*/
protected boolean isEmpty = true;
/** number of time-value points. */
private int count = 0;
private long startTime = Long.MAX_VALUE;
private long endTime = Long.MIN_VALUE;
static final String STATS_UNSUPPORTED_MSG = "%s statistics does not support: %s";
/**
* static method providing statistic instance for respective data type.
*
* @param type - data type
* @return Statistics
* @throws UnknownColumnTypeException if the type is unknown
*/
public static Statistics<? extends Serializable> getStatsByType(TSDataType type) {
switch (type) {
case INT32:
return new IntegerStatistics();
case INT64:
return new LongStatistics();
case TEXT:
return new BinaryStatistics();
case BOOLEAN:
return new BooleanStatistics();
case DOUBLE:
return new DoubleStatistics();
case FLOAT:
return new FloatStatistics();
case VECTOR:
return new TimeStatistics();
default:
throw new UnknownColumnTypeException(type.toString());
}
}
public static long getSizeByType(TSDataType type) {
switch (type) {
case INT32:
return IntegerStatistics.INSTANCE_SIZE;
case INT64:
return LongStatistics.INSTANCE_SIZE;
case TEXT:
return BinaryStatistics.INSTANCE_SIZE;
case BOOLEAN:
return BooleanStatistics.INSTANCE_SIZE;
case DOUBLE:
return DoubleStatistics.INSTANCE_SIZE;
case FLOAT:
return FloatStatistics.INSTANCE_SIZE;
case VECTOR:
return TimeStatistics.INSTANCE_SIZE;
default:
throw new UnknownColumnTypeException(type.toString());
}
}
public abstract TSDataType getType();
public int getSerializedSize() {
return ReadWriteForEncodingUtils.uVarIntSize(count) // count
+ 16 // startTime, endTime
+ getStatsSize();
}
public abstract int getStatsSize();
public abstract long getRetainedSizeInBytes();
public int serialize(OutputStream outputStream) throws IOException {
int byteLen = 0;
byteLen += ReadWriteForEncodingUtils.writeUnsignedVarInt(count, outputStream);
byteLen += ReadWriteIOUtils.write(startTime, outputStream);
byteLen += ReadWriteIOUtils.write(endTime, outputStream);
// value statistics of different data type
byteLen += serializeStats(outputStream);
return byteLen;
}
abstract int serializeStats(OutputStream outputStream) throws IOException;
/**
* deserialize data from the inputStream.
*
* @param inputStream input stream
* @throws IOException exception when operating stream
*/
public abstract void deserialize(InputStream inputStream) throws IOException;
public abstract void deserialize(ByteBuffer byteBuffer);
public static Statistics<? extends Serializable> deserialize(
InputStream inputStream, TSDataType dataType) throws IOException {
Statistics<? extends Serializable> statistics = getStatsByType(dataType);
statistics.setCount(ReadWriteForEncodingUtils.readUnsignedVarInt(inputStream));
statistics.setStartTime(ReadWriteIOUtils.readLong(inputStream));
statistics.setEndTime(ReadWriteIOUtils.readLong(inputStream));
statistics.deserialize(inputStream);
statistics.isEmpty = false;
return statistics;
}
public static Statistics<? extends Serializable> deserialize(
ByteBuffer buffer, TSDataType dataType) {
Statistics<? extends Serializable> statistics = getStatsByType(dataType);
statistics.setCount(ReadWriteForEncodingUtils.readUnsignedVarInt(buffer));
statistics.setStartTime(ReadWriteIOUtils.readLong(buffer));
statistics.setEndTime(ReadWriteIOUtils.readLong(buffer));
statistics.deserialize(buffer);
statistics.isEmpty = false;
return statistics;
}
public abstract T getMinValue();
public abstract T getMaxValue();
public abstract T getFirstValue();
public abstract T getLastValue();
public abstract double getSumDoubleValue();
public abstract long getSumLongValue();
/**
* merge parameter to this statistic.
*
* @throws StatisticsClassException cannot merge statistics
*/
@SuppressWarnings("unchecked")
public void mergeStatistics(Statistics<? extends Serializable> stats) {
if (this.getClass() == stats.getClass()) {
if (!stats.isEmpty) {
if (stats.startTime < this.startTime) {
this.startTime = stats.startTime;
}
if (stats.endTime > this.endTime) {
this.endTime = stats.endTime;
}
// must be sure no overlap between two statistics
this.count += stats.count;
mergeStatisticsValue((Statistics<T>) stats);
isEmpty = false;
}
} else {
Class<?> thisClass = this.getClass();
Class<?> statsClass = stats.getClass();
LOG.warn("Statistics classes mismatched,no merge: {} v.s. {}", thisClass, statsClass);
throw new StatisticsClassException(thisClass, statsClass);
}
}
public void update(long time, boolean value) {
update(time);
updateStats(value);
}
public void update(long time, int value) {
update(time);
updateStats(value);
}
public void update(long time, long value) {
update(time);
updateStats(value);
}
public void update(long time, float value) {
update(time);
updateStats(value);
}
public void update(long time, double value) {
update(time);
updateStats(value);
}
public void update(long time, Binary value) {
update(time);
updateStats(value);
}
public void update(long time) {
if (time < startTime) {
startTime = time;
}
if (time > endTime) {
endTime = time;
}
count++;
}
public void update(long[] time, boolean[] values, int batchSize) {
update(time, batchSize);
updateStats(values, batchSize);
}
public void update(long[] time, int[] values, int batchSize) {
update(time, batchSize);
updateStats(values, batchSize);
}
public void update(long[] time, long[] values, int batchSize) {
update(time, batchSize);
updateStats(values, batchSize);
}
public void update(long[] time, float[] values, int batchSize) {
update(time, batchSize);
updateStats(values, batchSize);
}
public void update(long[] time, double[] values, int batchSize) {
update(time, batchSize);
updateStats(values, batchSize);
}
public void update(long[] time, Binary[] values, int batchSize) {
update(time, batchSize);
updateStats(values, batchSize);
}
public void update(long[] time, int batchSize) {
if (time[0] < startTime) {
startTime = time[0];
}
if (time[batchSize - 1] > this.endTime) {
endTime = time[batchSize - 1];
}
count += batchSize;
}
public void update(long[] time, int batchSize, int arrayOffset) {
if (time[arrayOffset] < startTime) {
startTime = time[arrayOffset];
}
if (time[arrayOffset + batchSize - 1] > this.endTime) {
endTime = time[arrayOffset + batchSize - 1];
}
count += batchSize;
}
protected abstract void mergeStatisticsValue(Statistics<T> stats);
public boolean isEmpty() {
return isEmpty;
}
public void setEmpty(boolean empty) {
isEmpty = empty;
}
void updateStats(boolean value) {
throw new UnsupportedOperationException();
}
void updateStats(int value) {
throw new UnsupportedOperationException();
}
void updateStats(long value) {
throw new UnsupportedOperationException();
}
void updateStats(float value) {
throw new UnsupportedOperationException();
}
void updateStats(double value) {
throw new UnsupportedOperationException();
}
void updateStats(Binary value) {
throw new UnsupportedOperationException();
}
void updateStats(boolean[] values, int batchSize) {
throw new UnsupportedOperationException();
}
void updateStats(int[] values, int batchSize) {
throw new UnsupportedOperationException();
}
void updateStats(long[] values, int batchSize) {
throw new UnsupportedOperationException();
}
void updateStats(float[] values, int batchSize) {
throw new UnsupportedOperationException();
}
void updateStats(double[] values, int batchSize) {
throw new UnsupportedOperationException();
}
void updateStats(Binary[] values, int batchSize) {
throw new UnsupportedOperationException();
}
/**
* This method with two parameters is only used by {@code unsequence} which
* updates/inserts/deletes timestamp.
*
* @param min min timestamp
* @param max max timestamp
* @throws UnsupportedOperationException throw exception when executing this method
*/
public void updateStats(long min, long max) {
throw new UnsupportedOperationException();
}
public long getStartTime() {
return startTime;
}
public long getEndTime() {
return endTime;
}
public long getCount() {
return count;
}
public void setStartTime(long startTime) {
this.startTime = startTime;
}
public void setEndTime(long endTime) {
this.endTime = endTime;
}
public void setCount(int count) {
this.count = count;
}
public boolean containedByTimeFilter(Filter timeFilter) {
return timeFilter == null || timeFilter.containStartEndTime(getStartTime(), getEndTime());
}
public boolean hasNullValue(long timeRowCount) {
return getCount() != timeRowCount;
}
@Override
public String toString() {
return "startTime: " + startTime + " endTime: " + endTime + " count: " + count;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
return o != null && getClass() == o.getClass();
}
@Override
public int hashCode() {
return Objects.hash(super.hashCode(), count, startTime, endTime);
}
}