@@ -81,7 +81,9 @@ Backend.prototype.SNAPSHOT_TYPES = {
8181 // The current snapshot is being fetched (eg through backend.fetch)
8282 current : 'current' ,
8383 // A specific snapshot is being fetched by version (eg through backend.fetchSnapshot)
84- byVersion : 'byVersion'
84+ byVersion : 'byVersion' ,
85+ // A specific snapshot is being fetch by timestamp (eg through backend.fetchSnapshotByTimestamp)
86+ byTimestamp : 'byTimestamp'
8587} ;
8688
8789Backend . prototype . _shimDocAction = function ( ) {
@@ -627,6 +629,8 @@ Backend.prototype.fetchSnapshot = function(agent, index, id, version, callback)
627629
628630Backend . prototype . _fetchSnapshot = function ( collection , id , version , callback ) {
629631 var db = this . db ;
632+ var backend = this ;
633+
630634 this . milestoneDb . getMilestoneSnapshot ( collection , id , version , function ( error , milestoneSnapshot ) {
631635 if ( error ) return callback ( error ) ;
632636
@@ -637,49 +641,98 @@ Backend.prototype._fetchSnapshot = function (collection, id, version, callback)
637641 db . getOps ( collection , id , from , version , null , function ( error , ops ) {
638642 if ( error ) return callback ( error ) ;
639643
640- var type = null ;
641- var data ;
642- var fetchedVersion = 0 ;
643-
644- if ( milestoneSnapshot ) {
645- type = types . map [ milestoneSnapshot . type ] ;
646- if ( ! type ) return callback ( { code : 4008 , message : 'Unknown type' } ) ;
647- data = milestoneSnapshot . data ;
648- fetchedVersion = milestoneSnapshot . v ;
649- }
644+ backend . _buildSnapshotFromOps ( id , milestoneSnapshot , ops , function ( error , snapshot ) {
645+ if ( error ) return callback ( error ) ;
650646
651- for ( var index = 0 ; index < ops . length ; index ++ ) {
652- var op = ops [ index ] ;
653- fetchedVersion = op . v + 1 ;
654-
655- if ( op . create ) {
656- type = types . map [ op . create . type ] ;
657- if ( ! type ) return callback ( { code : 4008 , message : 'Unknown type' } ) ;
658- data = type . create ( op . create . data ) ;
659- } else if ( op . del ) {
660- data = undefined ;
661- type = null ;
662- } else {
663- data = type . apply ( data , op . op ) ;
647+ if ( version > snapshot . v ) {
648+ return callback ( { code : 4024 , message : 'Requested version exceeds latest snapshot version' } ) ;
664649 }
665- }
666650
667- type = type ? type . uri : null ;
651+ callback ( null , snapshot ) ;
652+ } ) ;
653+ } ) ;
654+ } ) ;
655+ } ;
668656
669- if ( version > fetchedVersion ) {
670- return callback ( { code : 4024 , message : 'Requested version exceeds latest snapshot version' } ) ;
671- }
657+ Backend . prototype . fetchSnapshotByTimestamp = function ( agent , index , id , timestamp , callback ) {
658+ var start = Date . now ( ) ;
659+ var backend = this ;
660+ var projection = this . projections [ index ] ;
661+ var collection = projection ? projection . target : index ;
662+ var request = {
663+ agent : agent ,
664+ index : index ,
665+ collection : collection ,
666+ id : id ,
667+ timestamp : timestamp
668+ } ;
672669
673- var snapshot = new Snapshot ( id , fetchedVersion , type , data , null ) ;
670+ this . _fetchSnapshotByTimestamp ( collection , id , timestamp , function ( error , snapshot ) {
671+ if ( error ) return callback ( error ) ;
672+ var snapshotProjection = backend . _getSnapshotProjection ( backend . db , projection ) ;
673+ var snapshots = [ snapshot ] ;
674+ var snapshotType = backend . SNAPSHOT_TYPES . byTimestamp ;
675+ backend . _sanitizeSnapshots ( agent , snapshotProjection , collection , snapshots , snapshotType , function ( error ) {
676+ if ( error ) return callback ( error ) ;
677+ backend . emit ( 'timing' , 'fetchSnapshot' , Date . now ( ) - start , request ) ;
674678 callback ( null , snapshot ) ;
675679 } ) ;
676680 } ) ;
677681} ;
678682
683+ Backend . prototype . _fetchSnapshotByTimestamp = function ( collection , id , timestamp , callback ) {
684+ var db = this . db ;
685+ var milestoneDb = this . milestoneDb ;
686+ var backend = this ;
687+
688+ var milestoneSnapshot ;
689+ var from = 0 ;
690+ var to = null ;
691+
692+ milestoneDb . getMilestoneSnapshotAtOrBeforeTime ( collection , id , timestamp , function ( error , snapshot ) {
693+ if ( error ) return callback ( error ) ;
694+ milestoneSnapshot = snapshot ;
695+ if ( snapshot ) from = snapshot . v ;
696+
697+ milestoneDb . getMilestoneSnapshotAtOrAfterTime ( collection , id , timestamp , function ( error , snapshot ) {
698+ if ( error ) return callback ( error ) ;
699+ if ( snapshot ) to = snapshot . v ;
700+
701+ var options = { metadata : true } ;
702+ db . getOps ( collection , id , from , to , options , function ( error , ops ) {
703+ if ( error ) return callback ( error ) ;
704+ filterOpsInPlaceBeforeTimestamp ( ops , timestamp ) ;
705+ backend . _buildSnapshotFromOps ( id , milestoneSnapshot , ops , callback ) ;
706+ } ) ;
707+ } ) ;
708+ } ) ;
709+ } ;
710+
711+ Backend . prototype . _buildSnapshotFromOps = function ( id , startingSnapshot , ops , callback ) {
712+ var snapshot = startingSnapshot || new Snapshot ( id , 0 , null , undefined , null ) ;
713+ var error = ot . applyOps ( snapshot , ops ) ;
714+ callback ( error , snapshot ) ;
715+ } ;
716+
679717function pluckIds ( snapshots ) {
680718 var ids = [ ] ;
681719 for ( var i = 0 ; i < snapshots . length ; i ++ ) {
682720 ids . push ( snapshots [ i ] . id ) ;
683721 }
684722 return ids ;
685723}
724+
725+ function filterOpsInPlaceBeforeTimestamp ( ops , timestamp ) {
726+ if ( timestamp === null ) {
727+ return ;
728+ }
729+
730+ for ( var i = 0 ; i < ops . length ; i ++ ) {
731+ var op = ops [ i ] ;
732+ var opTimestamp = op . m && op . m . ts ;
733+ if ( opTimestamp > timestamp ) {
734+ ops . length = i ;
735+ return ;
736+ }
737+ }
738+ }
0 commit comments