Main Page   Class Hierarchy   Compound List   File List   Compound 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 "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); // doubled interval in seconds, one element per day
00363         }        
00364         this->maxElementsInBlock = maxElementsInBlock;
00365         this->minElementsInBlock = minElementsInBlock;
00366         this->maxBlockTimeInterval = maxBlockTimeInterval;
00367 
00368         // correct instance of interval will be specified in select
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

Generated on Mon Oct 23 13:23:58 2006 for FastDB by doxygen1.2.18