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