@@ -28,6 +28,7 @@ use diesel::{connection::SimpleConnection, Connection};
2828use diesel:: {
2929 debug_query, sql_query, OptionalExtension , PgConnection , QueryDsl , QueryResult , RunQueryDsl ,
3030} ;
31+ use graph:: blockchain:: block_stream:: { EntitySubgraphOperation , EntityWithType } ;
3132use graph:: blockchain:: BlockTime ;
3233use graph:: cheap_clone:: CheapClone ;
3334use graph:: components:: store:: write:: { RowGroup , WriteChunk } ;
@@ -57,8 +58,8 @@ use std::time::{Duration, Instant};
5758
5859use crate :: relational:: value:: { FromOidRow , OidRow } ;
5960use crate :: relational_queries:: {
60- ConflictingEntitiesData , ConflictingEntitiesQuery , FindChangesQuery , FindDerivedQuery ,
61- FindPossibleDeletionsQuery , ReturnedEntityData ,
61+ ConflictingEntitiesData , ConflictingEntitiesQuery , EntityDataExt , FindChangesQuery ,
62+ FindDerivedQuery , FindPossibleDeletionsQuery , ReturnedEntityData ,
6263} ;
6364use crate :: {
6465 primary:: { Namespace , Site } ,
@@ -75,7 +76,7 @@ use graph::prelude::{
7576 QueryExecutionError , StoreError , StoreEvent , ValueType , BLOCK_NUMBER_MAX ,
7677} ;
7778
78- use crate :: block_range:: { BLOCK_COLUMN , BLOCK_RANGE_COLUMN } ;
79+ use crate :: block_range:: { BoundSide , BLOCK_COLUMN , BLOCK_RANGE_COLUMN } ;
7980pub use crate :: catalog:: Catalog ;
8081use crate :: connection_pool:: ForeignServer ;
8182use crate :: { catalog, deployment} ;
@@ -545,21 +546,129 @@ impl Layout {
545546 pub fn find_range (
546547 & self ,
547548 conn : & mut PgConnection ,
548- entity_type : & EntityType ,
549+ entity_types : Vec < EntityType > ,
550+ causality_region : CausalityRegion ,
549551 block_range : Range < BlockNumber > ,
550- ) -> Result < BTreeMap < BlockNumber , Vec < Entity > > , StoreError > {
551- let table = self . table_for_entity ( entity_type) ?;
552- let mut entities: BTreeMap < BlockNumber , Vec < Entity > > = BTreeMap :: new ( ) ;
553- if let Some ( vec) = FindRangeQuery :: new ( table. as_ref ( ) , block_range)
554- . get_results :: < EntityData > ( conn)
555- . optional ( ) ?
556- {
557- for e in vec {
558- let block = e. clone ( ) . deserialize_block_number :: < Entity > ( ) ?;
559- let en = e. deserialize_with_layout :: < Entity > ( self , None ) ?;
560- entities. entry ( block) . or_default ( ) . push ( en) ;
561- }
552+ ) -> Result < BTreeMap < BlockNumber , Vec < EntityWithType > > , StoreError > {
553+ let mut tables = vec ! [ ] ;
554+ for et in entity_types {
555+ tables. push ( self . table_for_entity ( & et) ?. as_ref ( ) ) ;
562556 }
557+ let mut entities: BTreeMap < BlockNumber , Vec < EntityWithType > > = BTreeMap :: new ( ) ;
558+
559+ // Collect all entities that have their 'lower(block_range)' attribute in the
560+ // interval of blocks defined by the variable block_range. For the immutable
561+ // entities the respective attribute is 'block$'.
562+ // Here are all entities that are created or modified in the block_range.
563+ let lower_vec = FindRangeQuery :: new (
564+ & tables,
565+ causality_region,
566+ BoundSide :: Lower ,
567+ block_range. clone ( ) ,
568+ )
569+ . get_results :: < EntityDataExt > ( conn)
570+ . optional ( ) ?
571+ . unwrap_or_default ( ) ;
572+ // Collect all entities that have their 'upper(block_range)' attribute in the
573+ // interval of blocks defined by the variable block_range. For the immutable
574+ // entities no entries are returned.
575+ // Here are all entities that are modified or deleted in the block_range,
576+ // but will have the previous versions, i.e. in the case of an update, it's
577+ // the version before the update, and lower_vec will have a corresponding
578+ // entry with the new version.
579+ let upper_vec =
580+ FindRangeQuery :: new ( & tables, causality_region, BoundSide :: Upper , block_range)
581+ . get_results :: < EntityDataExt > ( conn)
582+ . optional ( ) ?
583+ . unwrap_or_default ( ) ;
584+ let mut lower_iter = lower_vec. iter ( ) . fuse ( ) . peekable ( ) ;
585+ let mut upper_iter = upper_vec. iter ( ) . fuse ( ) . peekable ( ) ;
586+ let mut lower_now = lower_iter. next ( ) ;
587+ let mut upper_now = upper_iter. next ( ) ;
588+ // A closure to convert the entity data from the database into entity operation.
589+ let transform = |ede : & EntityDataExt ,
590+ entity_op : EntitySubgraphOperation |
591+ -> Result < ( EntityWithType , BlockNumber ) , StoreError > {
592+ let e = EntityData :: new ( ede. entity . clone ( ) , ede. data . clone ( ) ) ;
593+ let block = ede. block_number ;
594+ let entity_type = e. entity_type ( & self . input_schema ) ;
595+ let entity = e. deserialize_with_layout :: < Entity > ( self , None ) ?;
596+ let vid = ede. vid ;
597+ let ewt = EntityWithType {
598+ entity_op,
599+ entity_type,
600+ entity,
601+ vid,
602+ } ;
603+ Ok ( ( ewt, block) )
604+ } ;
605+
606+ // The algorithm is a similar to merge sort algorithm and it relays on the fact that both vectors
607+ // are ordered by (block_number, entity_type, entity_id). It advances simultaneously entities from
608+ // both lower_vec and upper_vec and tries to match entities that have entries in both vectors for
609+ // a particular block. The match is successful if an entry in one array has the same values in the
610+ // other one for the number of the block, entity type and the entity id. The comparison operation
611+ // over the EntityDataExt implements that check. If there is a match it’s a modification operation,
612+ // since both sides of a range are present for that block, entity type and id. If one side of the
613+ // range exists and the other is missing it is a creation or deletion depending on which side is
614+ // present. For immutable entities the entries in upper_vec are missing, hence they are considered
615+ // having a lower bound at particular block and upper bound at infinity.
616+ while lower_now. is_some ( ) || upper_now. is_some ( ) {
617+ let ( ewt, block) = match ( lower_now, upper_now) {
618+ ( Some ( lower) , Some ( upper) ) => {
619+ match lower. cmp ( & upper) {
620+ std:: cmp:: Ordering :: Greater => {
621+ // we have upper bound at this block, but no lower bounds at the same block so it's deletion
622+ let ( ewt, block) = transform ( upper, EntitySubgraphOperation :: Delete ) ?;
623+ // advance upper_vec pointer
624+ upper_now = upper_iter. next ( ) ;
625+ ( ewt, block)
626+ }
627+ std:: cmp:: Ordering :: Less => {
628+ // we have lower bound at this block but no upper bound at the same block so its creation
629+ let ( ewt, block) = transform ( lower, EntitySubgraphOperation :: Create ) ?;
630+ // advance lower_vec pointer
631+ lower_now = lower_iter. next ( ) ;
632+ ( ewt, block)
633+ }
634+ std:: cmp:: Ordering :: Equal => {
635+ let ( ewt, block) = transform ( lower, EntitySubgraphOperation :: Modify ) ?;
636+ // advance both lower_vec and upper_vec pointers
637+ lower_now = lower_iter. next ( ) ;
638+ upper_now = upper_iter. next ( ) ;
639+ ( ewt, block)
640+ }
641+ }
642+ }
643+ ( Some ( lower) , None ) => {
644+ // we have lower bound at this block but no upper bound at the same block so its creation
645+ let ( ewt, block) = transform ( lower, EntitySubgraphOperation :: Create ) ?;
646+ // advance lower_vec pointer
647+ lower_now = lower_iter. next ( ) ;
648+ ( ewt, block)
649+ }
650+ ( None , Some ( upper) ) => {
651+ let ( ewt, block) = transform ( upper, EntitySubgraphOperation :: Delete ) ?;
652+ // advance upper_vec pointer
653+ upper_now = upper_iter. next ( ) ;
654+ ( ewt, block)
655+ }
656+ _ => panic ! ( "Imposible case to happen" ) ,
657+ } ;
658+
659+ match entities. get_mut ( & block) {
660+ Some ( vec) => vec. push ( ewt) ,
661+ None => {
662+ let _ = entities. insert ( block, vec ! [ ewt] ) ;
663+ }
664+ } ;
665+ }
666+
667+ // sort the elements in each blocks bucket by vid
668+ for ( _, vec) in & mut entities {
669+ vec. sort_by ( |a, b| a. vid . cmp ( & b. vid ) ) ;
670+ }
671+
563672 Ok ( entities)
564673 }
565674
0 commit comments