Search in sources :

Example 1 with MapKey

use of io.questdb.cairo.map.MapKey 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;
    }
}
Also used : EmptyTableRandomRecordCursor(io.questdb.griffin.engine.EmptyTableRandomRecordCursor) MapKey(io.questdb.cairo.map.MapKey) SqlExecutionInterruptor(io.questdb.griffin.SqlExecutionInterruptor) MapValue(io.questdb.cairo.map.MapValue)

Example 2 with MapKey

use of io.questdb.cairo.map.MapKey in project questdb by bluestreak01.

the class SampleByInterpolateRecordCursorFactory method findDataMapValue2.

private MapValue findDataMapValue2(Record record, long timestamp) {
    final MapKey key = dataMap.withKey();
    mapSink2.copy(record, key);
    key.putLong(timestamp);
    return key.findValue2();
}
Also used : MapKey(io.questdb.cairo.map.MapKey)

Example 3 with MapKey

use of io.questdb.cairo.map.MapKey in project questdb by bluestreak01.

the class SampleByInterpolateRecordCursorFactory method findDataMapValue.

private MapValue findDataMapValue(Record record, long timestamp) {
    final MapKey key = dataMap.withKey();
    mapSink2.copy(record, key);
    key.putLong(timestamp);
    return key.findValue();
}
Also used : MapKey(io.questdb.cairo.map.MapKey)

Example 4 with MapKey

use of io.questdb.cairo.map.MapKey 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);
        }
    }
}
Also used : MapKey(io.questdb.cairo.map.MapKey) MapValue(io.questdb.cairo.map.MapValue)

Example 5 with MapKey

use of io.questdb.cairo.map.MapKey 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();
    }
}
Also used : EmptyTableRandomRecordCursor(io.questdb.griffin.engine.EmptyTableRandomRecordCursor) MapKey(io.questdb.cairo.map.MapKey) MapValue(io.questdb.cairo.map.MapValue)

Aggregations

MapKey (io.questdb.cairo.map.MapKey)34 MapValue (io.questdb.cairo.map.MapValue)17 DataFrame (io.questdb.cairo.sql.DataFrame)2 Record (io.questdb.cairo.sql.Record)2 SqlExecutionInterruptor (io.questdb.griffin.SqlExecutionInterruptor)2 EmptyTableRandomRecordCursor (io.questdb.griffin.engine.EmptyTableRandomRecordCursor)2 EmptyTableNoSizeRecordCursor (io.questdb.griffin.engine.EmptyTableNoSizeRecordCursor)1