use of io.questdb.cairo.map.MapValue in project questdb by bluestreak01.
the class SampleByInterpolateRecordCursorFactory method getCursor.
@Override
public RecordCursor getCursor(SqlExecutionContext executionContext) throws SqlException {
recordKeyMap.clear();
dataMap.clear();
final RecordCursor baseCursor = base.getCursor(executionContext);
final Record baseRecord = baseCursor.getRecord();
final SqlExecutionInterruptor interruptor = executionContext.getSqlExecutionInterruptor();
try {
// init all record function for this cursor, in case functions require metadata and/or symbol tables
Function.init(recordFunctions, baseCursor, executionContext);
// At the same time check if cursor has data
while (baseCursor.hasNext()) {
interruptor.checkInterrupted();
final MapKey key = recordKeyMap.withKey();
mapSink.copy(baseRecord, key);
key.createValue();
}
// no data, nothing to do
if (recordKeyMap.size() == 0) {
baseCursor.close();
return EmptyTableRandomRecordCursor.INSTANCE;
}
// topTop() is guaranteeing that we get
// the same data as previous while() loop
// there is no data
baseCursor.toTop();
// Evaluate group-by functions.
// On every change of timestamp sample value we
// check group for gaps and fill them with placeholder
// entries. Values for these entries will be interpolated later
// we have data in cursor, so we can grab first value
final boolean good = baseCursor.hasNext();
assert good;
long timestamp = baseRecord.getTimestamp(timestampIndex);
sampler.setStart(timestamp);
long prevSample = sampler.round(timestamp);
// the lowest timestamp value
long loSample = prevSample;
long hiSample;
do {
// this seems inefficient, but we only double-sample
// very first record and nothing else
long sample = sampler.round(baseRecord.getTimestamp(timestampIndex));
if (sample != prevSample) {
// before we continue with next interval
// we need to fill gaps in current interval
// we will go over unique keys and attempt to
// find them in data map with current timestamp
fillGaps(prevSample, sample);
prevSample = sample;
GroupByUtils.toTop(groupByFunctions);
}
// same data group - evaluate group-by functions
MapKey key = dataMap.withKey();
mapSink.copy(baseRecord, key);
key.putLong(sample);
MapValue value = key.createValue();
if (value.isNew()) {
// not a gap
value.putByte(0, (byte) 0);
for (int i = 0; i < groupByFunctionCount; i++) {
groupByFunctions.getQuick(i).computeFirst(value, baseRecord);
}
} else {
for (int i = 0; i < groupByFunctionCount; i++) {
groupByFunctions.getQuick(i).computeNext(value, baseRecord);
}
}
if (!baseCursor.hasNext()) {
hiSample = sampler.nextTimestamp(prevSample);
break;
}
interruptor.checkInterrupted();
} while (true);
// fill gaps if any at end of base cursor
fillGaps(prevSample, hiSample);
if (groupByTwoPointFunctionCount > 0) {
final RecordCursor mapCursor = recordKeyMap.getCursor();
final Record mapRecord = mapCursor.getRecord();
while (mapCursor.hasNext()) {
MapValue value = findDataMapValue(mapRecord, loSample);
if (value.getByte(0) == 0) {
// we have at least 1 data point
long x1 = loSample;
long x2 = x1;
while (true) {
// to timestamp after 'sample' to begin with
x2 = sampler.nextTimestamp(x2);
if (x2 < hiSample) {
value = findDataMapValue(mapRecord, x2);
if (value.getByte(0) == 0) {
interpolateBoundaryRange(x1, x2, mapRecord);
x1 = x2;
}
} else {
break;
}
}
}
}
}
// find gaps by checking each of the unique keys against every sample
long sample;
for (sample = prevSample = loSample; sample < hiSample; prevSample = sample, sample = sampler.nextTimestamp(sample)) {
final RecordCursor mapCursor = recordKeyMap.getCursor();
final Record mapRecord = mapCursor.getRecord();
while (mapCursor.hasNext()) {
// locate first gap
MapValue value = findDataMapValue(mapRecord, sample);
if (value.getByte(0) == 1) {
// gap is at 'sample', so potential X-value is at 'prevSample'
// now we need to find Y-value
long current = sample;
while (true) {
// to timestamp after 'sample' to begin with
long x2 = sampler.nextTimestamp(current);
// is this timestamp within range?
if (x2 < hiSample) {
value = findDataMapValue(mapRecord, x2);
if (value.getByte(0) == 1) {
// gap
current = x2;
} else {
// do we really have X-value?
if (sample == loSample) {
// prevSample does not exist
// find first valid value from 'x2+1' onwards
long x1 = x2;
while (true) {
x2 = sampler.nextTimestamp(x2);
if (x2 < hiSample) {
final MapValue x2value = findDataMapValue(mapRecord, x2);
if (x2value.getByte(0) == 0) {
// non-gap
// found value at 'x2' - this is our Y-value
// the X-value it at 'x1'
// compute slope and go back down all the way to start
// computing values in records
// this has to be a loop that would store y1 and y2 values for each
// group-by function
// use current 'value' for record
MapValue x1Value = findDataMapValue2(mapRecord, x1);
interpolate(loSample, x1, mapRecord, x1, x2, x1Value, x2value);
break;
}
} else {
// we only have a single value at 'x1' - cannot interpolate
// make all values before and after 'x1' NULL
nullifyRange(loSample, x1, mapRecord);
nullifyRange(sampler.nextTimestamp(x1), hiSample, mapRecord);
break;
}
}
} else {
// calculate slope between 'preSample' and 'x2'
// yep, that's right, and go all the way back down
// to 'sample' calculating interpolated values
MapValue x1Value = findDataMapValue2(mapRecord, prevSample);
interpolate(sampler.nextTimestamp(prevSample), x2, mapRecord, prevSample, x2, x1Value, value);
}
break;
}
} else {
// try using first two values
// we had X-value at 'prevSample'
// it will become Y-value and X is at 'prevSample-1'
// and calculate interpolated value all the way to 'hiSample'
long x1 = sampler.previousTimestamp(prevSample);
if (x1 < loSample) {
// not enough data points
// fill all data points from 'sample' down with null
nullifyRange(sample, hiSample, mapRecord);
} else {
MapValue x1Value = findDataMapValue2(mapRecord, x1);
MapValue x2value = findDataMapValue(mapRecord, prevSample);
interpolate(sampler.nextTimestamp(prevSample), hiSample, mapRecord, x1, prevSample, x1Value, x2value);
}
break;
}
}
}
}
}
cursor.of(baseCursor, dataMap.getCursor());
return cursor;
} catch (Throwable e) {
baseCursor.close();
throw e;
}
}
use of io.questdb.cairo.map.MapValue in project questdb by bluestreak01.
the class SampleByInterpolateRecordCursorFactory method interpolate.
private void interpolate(long lo, long hi, Record mapRecord, long x1, long x2, MapValue x1Value, MapValue x2value) {
computeYPoints(x1Value, x2value);
for (long x = lo; x < hi; x = sampler.nextTimestamp(x)) {
final MapValue result = findDataMapValue3(mapRecord, x);
assert result != null && result.getByte(0) == 1;
for (int i = 0; i < groupByTwoPointFunctionCount; i++) {
GroupByFunction function = groupByTwoPointFunctions.getQuick(i);
InterpolationUtil.interpolateGap(function, result, sampler.getBucketSize(), x1Value, x2value);
}
for (int i = 0; i < groupByScalarFunctionCount; i++) {
GroupByFunction function = groupByScalarFunctions.getQuick(i);
interpolatorFunctions.getQuick(i).interpolateAndStore(function, result, x, x1, x2, yData + i * 16L, yData + i * 16L + 8);
}
// fill the value, change flag from 'gap' to 'fill'
result.putByte(0, (byte) 0);
}
}
use of io.questdb.cairo.map.MapValue in project questdb by bluestreak01.
the class SampleByInterpolateRecordCursorFactory method nullifyRange.
private void nullifyRange(long lo, long hi, Record record) {
for (long x = lo; x < hi; x = sampler.nextTimestamp(x)) {
final MapKey key = dataMap.withKey();
mapSink2.copy(record, key);
key.putLong(x);
MapValue value = key.findValue();
// expect 'gap' flag
assert value != null && value.getByte(0) == 1;
// fill the value, change flag from 'gap' to 'fill'
value.putByte(0, (byte) 0);
for (int i = 0; i < groupByFunctionCount; i++) {
groupByFunctions.getQuick(i).setNull(value);
}
}
}
use of io.questdb.cairo.map.MapValue in project questdb by bluestreak01.
the class SampleByInterpolateRecordCursorFactory method fillGaps.
private void fillGaps(long lo, long hi) {
final RecordCursor keyCursor = recordKeyMap.getCursor();
final Record record = keyCursor.getRecord();
long timestamp = lo;
while (timestamp < hi) {
while (keyCursor.hasNext()) {
MapKey key = dataMap.withKey();
mapSink2.copy(record, key);
key.putLong(timestamp);
MapValue value = key.createValue();
if (value.isNew()) {
// this is gap
value.putByte(0, (byte) 1);
}
}
timestamp = sampler.nextTimestamp(timestamp);
keyCursor.toTop();
}
}
use of io.questdb.cairo.map.MapValue in project questdb by bluestreak01.
the class GroupByRecordCursorFactory method getCursor.
@Override
public RecordCursor getCursor(SqlExecutionContext executionContext) throws SqlException {
dataMap.clear();
final RecordCursor baseCursor = base.getCursor(executionContext);
try {
Function.init(recordFunctions, baseCursor, executionContext);
final Record baseRecord = baseCursor.getRecord();
final int n = groupByFunctions.size();
while (baseCursor.hasNext()) {
executionContext.getSqlExecutionInterruptor().checkInterrupted();
final MapKey key = dataMap.withKey();
mapSink.copy(baseRecord, key);
MapValue value = key.createValue();
GroupByUtils.updateFunctions(groupByFunctions, n, value, baseRecord);
}
cursor.of(baseCursor, dataMap.getCursor());
// init all record function for this cursor, in case functions require metadata and/or symbol tables
return cursor;
} catch (Throwable e) {
baseCursor.close();
throw e;
}
}
Aggregations