Main Page | Class Hierarchy | Class List | File List | Class Members

timeseries.h

00001 //-< TIMESERIES.H >--------------------------------------------------*--------*
00002 // FastDB                    Version 1.0         (c) 1999  GARRET    *     ?  *
00003 // (Post Relational Database Management System)                      *   /\|  *
00004 //                                                                   *  /  \  *
00005 //                          Created:     22-Nov-2002  K.A. Knizhnik  * / [] \ *
00006 //                          Last update: 22-Nov-2002  K.A. Knizhnik  * GARRET *
00007 //-------------------------------------------------------------------*--------*
00008 // Container for time serires data
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); // doubled interval in seconds, one element per day
00362         }        
00363         this->maxElementsInBlock = maxElementsInBlock;
00364         this->minElementsInBlock = minElementsInBlock;
00365         this->maxBlockTimeInterval = maxBlockTimeInterval;
00366 
00367         // correct instance of interval will be specified in select
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

Generated on Thu Feb 12 13:04:48 2004 for FastDB by doxygen 1.3.5