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