📄 timeseries.h
字号:
//-< TIMESERIES.H >--------------------------------------------------*--------*// FastDB Version 1.0 (c) 1999 GARRET * ? *// (Post Relational Database Management System) * /\| *// * / \ *// Created: 22-Nov-2002 K.A. Knizhnik * / [] \ *// Last update: 22-Nov-2002 K.A. Knizhnik * GARRET *//-------------------------------------------------------------------*--------*// Container for time serires data//-------------------------------------------------------------------*--------*#ifndef __TIMESERIES_H__#define __TIMESERIES_H__#include <time.h>#include "fastdb.h"#define INFINITE_TIME 0x7fffffff/** * Time series block contaning array of elements. Grouping several elements in one block (record) * reduce space overhead and increase processing speed.<BR> * <B>Attention!</B> This class is not serialized, so it is can be accessed only by one thread<P> * <I>You are defining your own time series class, for example:</I> * <PRE> * class Stock { * public: * char const* name; * TYPE_DESCRIPTOR((KEY(name, INDEXED))); * }; * * * class Quote { * public: * int4 tickerDate; * real4 bid; * int4 bidSize; * real4 ask; * int4 askSize; * * time_t time() const { return tickerDate; } // this method should be defined * * TYPE_DESCRIPTOR((FIELD(tickerDate), FIELD(bid), FIELD(bidSize), FIELD(ask), FIELD(askSize))); * }; * typedef dbTimeSeriesBlock<Daily> DailyBlock; * REGISTER(DailyBlock); * </PRE> * <I>Now you can work with time series objects in the followin way:</I> * <PRE> * dbDatabase db; * if (db.open("mydatabase.dbs")) { * dbTimeSeriesProcessor<Quote> proc(db, MIN_ELEMENTS_IN_BLOCK,MAX_ELEMENTS_IN_BLOCK); * Quote quote; * // initialize quote * Stock stock; * stock.name = "AAD"; * oid_t stockId = insert(oid).getOid(); * proc.add(stockId, quote); // add new element in time series * * Quote quoteBuf[MAX_QUOTES]; * // select quotes for the specified interval * int n = proc.getInterval(stockId, fromDate, tillDate, quoteBuf, MAX_QUOTES); * for (int i = 0; i < n; i++) { * printf("bid=d ask=%d\n", quoteBuf[i].bid, quoteBuf[i].ask); * } * } * </PRE> */template<class T>class dbTimeSeriesBlock { public: db_int8 blockId; db_int4 used; dbArray<T> elements; TYPE_DESCRIPTOR((KEY(blockId, INDEXED), FIELD(used), FIELD(elements)));};/** * Time series processor.<BR> * Element of time series can be arbitrary type with declared TYPE_DESCRIPTOR and defined * <code>time_t time()</code> method */template<class T>class dbTimeSeriesProcessor { struct Interval { db_int8 from; db_int8 till; }; public: /** * Virtual method for processing elements, Should be redefinedin derived class. * @param data reference to the processed data element */ virtual void process(T const&) {} /** * Add new element * @param oid time series identifer (OID of the object associated with this time series) * @param data reference to the inserted element */ void add(oid_t oid, T const& data) { Interval interval; interval.from = generateBlockId(oid, data.time() - maxBlockTimeInterval); interval.till = generateBlockId(oid, data.time()); dbCursor< dbTimeSeriesBlock<T> > blocks; blocks.select(selectBlock, dbCursorForUpdate, &interval); if (blocks.last()) { insertInBlock(oid, blocks, data); } else { addNewBlock(oid, data); } } /** * Process elements in the block belonging to the specified range * @param oid time series identifer (OID of the object associated with this time series) * @param from inclusive low bound for element timestamp (set 0 to disable this criteria) * @param till inclusive high bound for element timestamp (set INFINITE_TIME to disable this criteria) */ void select(oid_t oid, time_t from, time_t till) { Interval interval; interval.from = generateBlockId(oid, from - maxBlockTimeInterval); interval.till = generateBlockId(oid, till); dbCursor< dbTimeSeriesBlock<T> > blocks; if (blocks.select(selectBlock, dbCursorViewOnly, &interval)) { do { int n = blocks->used; T const* e = blocks->elements.get(); int l = 0, r = n; while (l < r) { int i = (l+r) >> 1; if (from > e[i].time()) { l = i+1; } else { r = i; } } assert(l == r && (l == n || e[l].time() >= from)); while (l < n && e[l].time() <= till) { process(e[l++]); } } while (blocks.next()); } } /** * Get the time of the first element in time series * @param oid time series identifer (OID of the object associated with this time series) * @return earliest time in times series or -1 if there are no elements in time series */ time_t getFirstTime(oid_t oid) { Interval interval; interval.from = generateBlockId(oid, 0); interval.till = generateBlockId(oid, INFINITE_TIME); dbCursor< dbTimeSeriesBlock<T> > blocks; blocks.setSelectionLimit(1); if (blocks.select(selectBlock, dbCursorViewOnly, &interval)) { return blocks->elements[0].time(); } return (time_t)-1; } /** * Get the time of the last element in time series * @param oid time series identifer (OID of the object associated with this time series) * @return latest time in times series or -1 if there are no elements in time series */ time_t getLastTime(oid_t oid) { Interval interval; interval.from = generateBlockId(oid, 0); interval.till = generateBlockId(oid, INFINITE_TIME); dbCursor< dbTimeSeriesBlock<T> > blocks; blocks.setSelectionLimit(1); if (blocks.select(selectBlockReverse, dbCursorViewOnly, &interval)) { return blocks->elements[blocks->used-1].time(); } return (time_t)-1; } /** * Get number of elements in time series. * @param oid time series identifer (OID of the object associated with this time series) * @return number of elements in time series. */ size_t getNumberOfElements(oid_t oid) { Interval interval; interval.from = generateBlockId(oid, 0); interval.till = generateBlockId(oid, INFINITE_TIME); dbCursor< dbTimeSeriesBlock<T> > blocks; int n = 0; if (blocks.select(selectBlock, dbCursorViewOnly, &interval)) { do { n += blocks->used; } while (blocks.next()); } return n; } /** * Select elements belonging to the specified interval * @param oid time series identifer (OID of the object associated with this time series) * @param from inclusive low bound for element timestamp (set 0 to disable this criteria) * @param till inclusive high bound for element timestamp (set INFINITE_TIME to disable this criteria) * @param buf destination buffer for selected elements * @param bufSize size of buffer: up to bufSize elements will be placed in buffer * @return number of elements belonging to the specified interval (can be greater than bufSize) */ size_t getInterval(oid_t oid, time_t from, time_t till, T* buf, size_t bufSize) { Interval interval; interval.from = generateBlockId(oid, from == 0 ? 0 : from - maxBlockTimeInterval); interval.till = generateBlockId(oid, till); dbCursor< dbTimeSeriesBlock<T> > blocks; size_t nSelected = 0; if (blocks.select(selectBlock, dbCursorViewOnly, &interval)) { do { int n = blocks->used; T const* e = blocks->elements.get(); int l = 0, r = n; while (l < r) { int i = (l+r) >> 1; if (from > e[i].time()) { l = i+1; } else { r = i; } } assert(l == r && (l == n || e[l].time() >= from)); while (l < n && e[l].time() <= till) { if (nSelected < bufSize) { buf[nSelected] = e[l]; } l += 1; nSelected += 1; } } while (blocks.next()); } return nSelected; } /** * Get time series element with specified time * @param oid time series identifer (OID of the object associated with this time series) * @param elem reference to the extracted element * @param t timestamp of extracted element * @return <code>true</code> if element with specifed times exists in time series */ bool getElement(oid_t oid, T& elem, time_t t) { return getInterval(oid, t, t, &elem, 1) == 1; } /** * Select first N elements of times series with timestamp less than or equal to specified * @param oid time series identifer (OID of the object associated with this time series) * @param till inclusive high bound for element timestamp (set INFINITE_TIME to disable this criteria) * @param buf destination buffer for selected elements * @param bufSize size of buffer: up to bufSize elements will be placed in buffer * @return number of selected elements (can be less than bufSize if there are less elements in time series * with timestamp less or equal than specified, but can not be greater than bufSize) */ size_t getFirstInterval(oid_t oid, time_t till, T* buf, size_t bufSize) { if (bufSize == 0) { return 0; } Interval interval; interval.from = generateBlockId(oid, 0); interval.till = generateBlockId(oid, till); dbCursor< dbTimeSeriesBlock<T> > blocks; size_t nSelected = 0; if (blocks.select(selectBlock, dbCursorViewOnly, &interval)) { do { int n = blocks->used; T const* e = blocks->elements.get(); for (int i = 0; i < n && e[i].time() <= till; i++) { buf[nSelected++] = e[i]; if (nSelected == bufSize) { return nSelected; } } } while (blocks.next()); } return nSelected; } /** * Select last N elements of times series with timestamp greater than or equal to specified * @param oid time series identifer (OID of the object associated with this time series) * @param from inclusive low bound for element timestamp (set 0 to disable this criteria) * @param buf destination buffer for selected elements * @param bufSize size of buffer: up to bufSize elements will be placed in buffer * @return number of selected elements (can be less than bufSize if there are less elements in time series * with timestamp greater or equal than specified, but can not be greater than bufSize) */ size_t getLastInterval(oid_t oid, time_t from, T* buf, size_t bufSize) { if (bufSize == 0) { return 0; } Interval interval; interval.from = generateBlockId(oid, from == 0 ? 0 : from - maxBlockTimeInterval); interval.till = generateBlockId(oid, INFINITE_TIME); dbCursor< dbTimeSeriesBlock<T> > blocks; size_t nSelected = 0; blocks.select(selectBlock, dbCursorViewOnly, &interval); if (blocks.last()) { do { int n = blocks->used; T const* e = blocks->elements.get(); for (int i = n; --i >= 0 && e[i].time() >= from;) { buf[nSelected++] = e[i]; if (nSelected == bufSize) { return nSelected; } } } while (blocks.prev()); } return nSelected; } /** * Check if there is element for specified data in time series * @param oid time series identifer (OID of the object associated with this time series) * @param t timestamp of checked element * @return <code>true</code> if element with specifed times exists in time series */ bool hasElement(oid_t oid, time_t t) { T dummy; return getElement(oid, dummy, t); } /** * TimeSeries processor constructor * @param database reference to the database * @param minElementsInBlock preallocated number of the elements in the block: * array with specified number of elements will be allocated for new block * @param maxElementsInBlock maximal number of the elements in the block: block will be splitten if it has maxElementsInBlock * elements and new is added to the block * @param maxBlockTimeInterval maximal interval between first and last element in the block, new block will be created if * adding new element to the block cause violation of this assumption. If maxBlockTimeInterval is 0, then it is assigned
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -