00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011 #ifndef __TIMESERIES_H__
00012 #define __TIMESERIES_H__
00013
00014 #include <time.h>
00015 #include "fastdb.h"
00016
00017 #define INFINITE_TIME 0x7fffffff
00018
00068 template<class T>
00069 class dbTimeSeriesBlock {
00070 public:
00071 db_int8 blockId;
00072 db_int4 used;
00073 dbArray<T> elements;
00074
00075 TYPE_DESCRIPTOR((KEY(blockId, INDEXED), FIELD(used), FIELD(elements)));
00076 };
00077
00078
00084 template<class T>
00085 class dbTimeSeriesProcessor {
00086 struct Interval {
00087 db_int8 from;
00088 db_int8 till;
00089 };
00090
00091 public:
00096 virtual void process(T const&) {}
00097
00103 void add(oid_t oid, T const& data)
00104 {
00105 Interval interval;
00106 interval.from = generateBlockId(oid, data.time() - maxBlockTimeInterval);
00107 interval.till = generateBlockId(oid, data.time());
00108 dbCursor< dbTimeSeriesBlock<T> > blocks;
00109 blocks.select(selectBlock, dbCursorForUpdate, &interval);
00110 if (blocks.last()) {
00111 insertInBlock(oid, blocks, data);
00112 } else {
00113 addNewBlock(oid, data);
00114 }
00115 }
00116
00123 void select(oid_t oid, time_t from, time_t till)
00124 {
00125 Interval interval;
00126 interval.from = generateBlockId(oid, from - maxBlockTimeInterval);
00127 interval.till = generateBlockId(oid, till);
00128 dbCursor< dbTimeSeriesBlock<T> > blocks;
00129 if (blocks.select(selectBlock, dbCursorViewOnly, &interval)) {
00130 do {
00131 int n = blocks->used;
00132 T const* e = blocks->elements.get();
00133 int l = 0, r = n;
00134 while (l < r) {
00135 int i = (l+r) >> 1;
00136 if (from > e[i].time()) {
00137 l = i+1;
00138 } else {
00139 r = i;
00140 }
00141 }
00142 assert(l == r && (l == n || e[l].time() >= from));
00143 while (l < n && e[l].time() <= till) {
00144 process(e[l++]);
00145 }
00146 } while (blocks.next());
00147 }
00148 }
00149
00155 time_t getFirstTime(oid_t oid)
00156 {
00157 Interval interval;
00158 interval.from = generateBlockId(oid, 0);
00159 interval.till = generateBlockId(oid, INFINITE_TIME);
00160 dbCursor< dbTimeSeriesBlock<T> > blocks;
00161 blocks.setSelectionLimit(1);
00162 if (blocks.select(selectBlock, dbCursorViewOnly, &interval)) {
00163 return blocks->elements[0].time();
00164 }
00165 return (time_t)-1;
00166 }
00167
00173 time_t getLastTime(oid_t oid)
00174 {
00175 Interval interval;
00176 interval.from = generateBlockId(oid, 0);
00177 interval.till = generateBlockId(oid, INFINITE_TIME);
00178 dbCursor< dbTimeSeriesBlock<T> > blocks;
00179 blocks.setSelectionLimit(1);
00180 if (blocks.select(selectBlockReverse, dbCursorViewOnly, &interval)) {
00181 return blocks->elements[blocks->used-1].time();
00182 }
00183 return (time_t)-1;
00184 }
00185
00191 size_t getNumberOfElements(oid_t oid)
00192 {
00193 Interval interval;
00194 interval.from = generateBlockId(oid, 0);
00195 interval.till = generateBlockId(oid, INFINITE_TIME);
00196 dbCursor< dbTimeSeriesBlock<T> > blocks;
00197 int n = 0;
00198 if (blocks.select(selectBlock, dbCursorViewOnly, &interval)) {
00199 do {
00200 n += blocks->used;
00201 } while (blocks.next());
00202 }
00203 return n;
00204 }
00205
00215 size_t getInterval(oid_t oid, time_t from, time_t till, T* buf, size_t bufSize)
00216 {
00217 Interval interval;
00218 interval.from = generateBlockId(oid, from == 0 ? 0 : from - maxBlockTimeInterval);
00219 interval.till = generateBlockId(oid, till);
00220 dbCursor< dbTimeSeriesBlock<T> > blocks;
00221 size_t nSelected = 0;
00222 if (blocks.select(selectBlock, dbCursorViewOnly, &interval)) {
00223 do {
00224 int n = blocks->used;
00225 T const* e = blocks->elements.get();
00226 int l = 0, r = n;
00227 while (l < r) {
00228 int i = (l+r) >> 1;
00229 if (from > e[i].time()) {
00230 l = i+1;
00231 } else {
00232 r = i;
00233 }
00234 }
00235 assert(l == r && (l == n || e[l].time() >= from));
00236 while (l < n && e[l].time() <= till) {
00237 if (nSelected < bufSize) {
00238 buf[nSelected] = e[l];
00239 }
00240 l += 1;
00241 nSelected += 1;
00242 }
00243 } while (blocks.next());
00244 }
00245 return nSelected;
00246 }
00247
00255 bool getElement(oid_t oid, T& elem, time_t t)
00256 {
00257 return getInterval(oid, t, t, &elem, 1) == 1;
00258 }
00259
00269 size_t getFirstInterval(oid_t oid, time_t till, T* buf, size_t bufSize)
00270 {
00271 if (bufSize == 0) {
00272 return 0;
00273 }
00274 Interval interval;
00275 interval.from = generateBlockId(oid, 0);
00276 interval.till = generateBlockId(oid, till);
00277 dbCursor< dbTimeSeriesBlock<T> > blocks;
00278 size_t nSelected = 0;
00279 if (blocks.select(selectBlock, dbCursorViewOnly, &interval)) {
00280 do {
00281 int n = blocks->used;
00282 T const* e = blocks->elements.get();
00283 for (int i = 0; i < n && e[i].time() <= till; i++) {
00284 buf[nSelected++] = e[i];
00285 if (nSelected == bufSize) {
00286 return nSelected;
00287 }
00288 }
00289 } while (blocks.next());
00290 }
00291 return nSelected;
00292 }
00293
00294
00304 size_t getLastInterval(oid_t oid, time_t from, T* buf, size_t bufSize)
00305 {
00306 if (bufSize == 0) {
00307 return 0;
00308 }
00309 Interval interval;
00310 interval.from = generateBlockId(oid, from == 0 ? 0 : from - maxBlockTimeInterval);
00311 interval.till = generateBlockId(oid, INFINITE_TIME);
00312 dbCursor< dbTimeSeriesBlock<T> > blocks;
00313
00314 size_t nSelected = 0;
00315 blocks.select(selectBlock, dbCursorViewOnly, &interval);
00316 if (blocks.last()) {
00317 do {
00318 int n = blocks->used;
00319 T const* e = blocks->elements.get();
00320 for (int i = n; --i >= 0 && e[i].time() >= from;) {
00321 buf[nSelected++] = e[i];
00322 if (nSelected == bufSize) {
00323 return nSelected;
00324 }
00325 }
00326 } while (blocks.prev());
00327 }
00328 return nSelected;
00329 }
00330
00331
00332
00339 bool hasElement(oid_t oid, time_t t)
00340 {
00341 T dummy;
00342 return getElement(oid, dummy, t);
00343 }
00344
00356 dbTimeSeriesProcessor(dbDatabase& database, int minElementsInBlock=100, int maxElementsInBlock=100, time_t maxBlockTimeInterval=0) :
00357 db(database)
00358 {
00359 assert(minElementsInBlock > 0 && maxElementsInBlock >= minElementsInBlock);
00360 if (maxBlockTimeInterval == 0) {
00361 maxBlockTimeInterval = 2*(maxElementsInBlock*24*60*60);
00362 }
00363 this->maxElementsInBlock = maxElementsInBlock;
00364 this->minElementsInBlock = minElementsInBlock;
00365 this->maxBlockTimeInterval = maxBlockTimeInterval;
00366
00367
00368 Interval* dummy = NULL;
00369 selectBlock = "blockId between",dummy->from,"and",dummy->till;
00370 selectBlockReverse = "blockId between",dummy->from,"and",dummy->till,"order by blockId desc";
00371 }
00372
00380 int remove(oid_t oid, time_t from, time_t till)
00381 {
00382 Interval interval;
00383 interval.from = generateBlockId(oid, from == 0 ? 0 : from - maxBlockTimeInterval);
00384 interval.till = generateBlockId(oid, till);
00385 dbCursor< dbTimeSeriesBlock<T> > blocks;
00386 size_t nRemoved = 0;
00387 if (blocks.select(selectBlock, dbCursorForUpdate, &interval)) {
00388 do {
00389 int n = blocks->used;
00390 T const* e = blocks->elements.get();
00391 int l = 0, r = n;
00392 while (l < r) {
00393 int i = (l+r) >> 1;
00394 if (from > e[i].time()) {
00395 l = i+1;
00396 } else {
00397 r = i;
00398 }
00399 }
00400 assert(l == r && (l == n || e[l].time() >= from));
00401 while (r < n && e[r].time() <= till) {
00402 r += 1;
00403 nRemoved += 1;
00404 }
00405 if (l == 0 && r == n) {
00406 blocks.remove();
00407 } else if (l < n && l != r) {
00408 if (l == 0) {
00409 blocks->blockId = generateBlockId(oid, e[r].time());
00410 }
00411 T* ue = blocks->elements.update();
00412 while (r < n) {
00413 ue[l++] = ue[r++];
00414 }
00415 blocks->used = l;
00416 blocks.update();
00417 }
00418 } while (blocks.nextAvailable());
00419 }
00420 return nRemoved;
00421 }
00422
00423 virtual~dbTimeSeriesProcessor() {}
00424
00430 int _openIteratorCursor(dbCursor< dbTimeSeriesBlock<T> >& cursor, oid_t oid, time_t from, time_t till)
00431 {
00432 Interval interval;
00433 interval.from = generateBlockId(oid, from == 0 ? 0 : from - maxBlockTimeInterval);
00434 interval.till = generateBlockId(oid, till);
00435 return cursor.select(selectBlock, dbCursorViewOnly, &interval);
00436 }
00437
00438 private:
00439 db_int8 generateBlockId(oid_t oid, time_t date)
00440 {
00441 return cons_int8(oid, date);
00442 }
00443
00444
00445 void addNewBlock(oid_t oid, T const& data)
00446 {
00447 dbTimeSeriesBlock<T> block;
00448 block.blockId = generateBlockId(oid, data.time());
00449 block.elements.resize(minElementsInBlock);
00450 block.used = 1;
00451 block.elements.putat(0, data);
00452 insert(block);
00453 }
00454
00455 void insertInBlock(oid_t oid, dbCursor< dbTimeSeriesBlock<T> >& blocks, T const& data)
00456 {
00457 time_t t = data.time();
00458 int i, n = blocks->used;
00459
00460 T const* e = blocks->elements.get();
00461 int l = 0, r = n;
00462 while (l < r) {
00463 i = (l+r) >> 1;
00464 if (t > e[i].time()) {
00465 l = i+1;
00466 } else {
00467 r = i;
00468 }
00469 }
00470 assert(l == r && (l == n || e[l].time() >= t));
00471 if (r == 0) {
00472 if (e[n-1].time() - t > maxBlockTimeInterval || n == maxElementsInBlock) {
00473 addNewBlock(oid, data);
00474 return;
00475 }
00476 blocks->blockId = generateBlockId(oid, t);
00477 } else if (r == n) {
00478 if (t - e[0].time() > maxBlockTimeInterval || n == maxElementsInBlock) {
00479 addNewBlock(oid, data);
00480 return;
00481 }
00482 }
00483 if ((size_t)n == blocks->elements.length()) {
00484 if (n == maxElementsInBlock) {
00485 T* u = blocks->elements.update();
00486 addNewBlock(oid, u[n-1]);
00487 for (i = n; --i > r; ) {
00488 u[i] = u[i-1];
00489 }
00490 u[r] = data;
00491 blocks.update();
00492 return;
00493 }
00494 blocks->elements.resize(n + minElementsInBlock < maxElementsInBlock ? n + minElementsInBlock : maxElementsInBlock);
00495 }
00496 T* u = blocks->elements.update();
00497 for (i = n; i > r; i--) {
00498 u[i] = u[i-1];
00499 }
00500 u[r] = data;
00501 blocks->used += 1;
00502 blocks.update();
00503 }
00504
00505 dbDatabase& db;
00506 int maxElementsInBlock;
00507 int minElementsInBlock;
00508 time_t maxBlockTimeInterval;
00509 dbQuery selectBlock;
00510 dbQuery selectBlockReverse;
00511 };
00512
00513
00517 template<class T>
00518 class dbTimeSeriesIterator {
00519 public:
00527 void start(dbTimeSeriesProcessor<T>* processor, oid_t oid, time_t from, time_t till) {
00528 first = pos = -1;
00529 this->till = till;
00530 if (processor->_openIteratorCursor(blocks, oid, from, till)) {
00531 do {
00532 int n = blocks->used;
00533 T const* e = blocks->elements.get();
00534 int l = 0, r = n;
00535 while (l < r) {
00536 int i = (l+r) >> 1;
00537 if (from > e[i].time()) {
00538 l = i+1;
00539 } else {
00540 r = i;
00541 }
00542 }
00543 assert(l == r && (l == n || e[l].time() >= from));
00544 if (l < n) {
00545 if (e[l].time() <= till) {
00546 first = pos = l;
00547 }
00548 return;
00549 }
00550 } while (blocks.next());
00551 }
00552 }
00553
00558 bool current(T& elem) {
00559 if (pos >= 0) {
00560 elem = blocks->elements[pos];
00561 return true;
00562 }
00563 return false;
00564 }
00565
00570 bool next() {
00571 if (pos >= 0) {
00572 if (++pos == blocks->used) {
00573 if (!blocks.next()) {
00574 pos = -1;
00575 return false;
00576 }
00577 pos = 0;
00578 }
00579 if (blocks->elements[pos].time() <= till) {
00580 return true;
00581 }
00582 pos = -1;
00583 }
00584 return false;
00585 }
00586
00590 void reset() {
00591 blocks.first();
00592 pos = first;
00593 }
00594
00599 dbTimeSeriesIterator() {
00600 first = pos = -1;
00601 }
00602 private:
00603 dbCursor< dbTimeSeriesBlock<T> > blocks;
00604 int pos;
00605 int first;
00606 time_t till;
00607 };
00608
00612 template<class T>
00613 class dbTimeSeriesReverseIterator {
00614 public:
00622 void start(dbTimeSeriesProcessor<T>* processor, oid_t oid, time_t from, time_t till) {
00623 last = pos = -1;
00624 this->from = from;
00625 if (processor->_openIteratorCursor(blocks, oid, from, till)) {
00626 do {
00627 int n = blocks->used;
00628 blocks.last();
00629 T const* e = blocks->elements.get();
00630 int l = 0, r = n;
00631 while (l < r) {
00632 int i = (l+r) >> 1;
00633 if (till >= e[i].time()) {
00634 l = i+1;
00635 } else {
00636 r = i;
00637 }
00638 }
00639 assert(l == r && (l == n || e[l].time() > till));
00640 if (l > 0) {
00641 if (e[l-1].time() >= from) {
00642 last = pos = l-1;
00643 }
00644 return;
00645 }
00646 } while (blocks.prev());
00647 }
00648 }
00649
00654 bool current(T& elem) {
00655 if (pos >= 0) {
00656 elem = blocks->elements[pos];
00657 return true;
00658 }
00659 return false;
00660 }
00661
00666 bool next() {
00667 if (pos >= 0) {
00668 if (--pos < 0) {
00669 if (!blocks.prev()) {
00670 return false;
00671 }
00672 pos = blocks->used-1;
00673 }
00674 if (blocks->elements[pos].time() >= from) {
00675 return true;
00676 }
00677 pos = -1;
00678 }
00679 return false;
00680 }
00681
00685 void reset() {
00686 blocks.last();
00687 pos = last;
00688 }
00689
00694 dbTimeSeriesReverseIterator() {
00695 last = pos = -1;
00696 }
00697 private:
00698 dbCursor< dbTimeSeriesBlock<T> > blocks;
00699 int pos;
00700 int last;
00701 time_t from;
00702 };
00703
00704 #endif