@@ -273,7 +273,8 @@ describeSpec('Existence Filters:', [], () => {
273273 ) ;
274274
275275 /**
276- * Test existence filter with bloom filter.
276+ * Test existence filter with bloom filter. Existence filters below is sent mid-stream for
277+ * testing simplicity.
277278 */
278279 specTest (
279280 'Full re-query is skipped when bloom filter can identify documents deleted' ,
@@ -626,9 +627,185 @@ describeSpec('Existence Filters:', [], () => {
626627 // Doc0 to doc49 are deleted in the next sync.
627628 . watchFilters ( [ query1 ] , docKeys . slice ( 0 , 50 ) , bloomFilterProto )
628629 . watchSnapshots ( 2000 )
629- // BloomFilter correctly identifies docs that deleted, skip full query.
630+ // Bloom Filter correctly identifies docs that deleted, skips full query.
630631 . expectEvents ( query1 , { fromCache : true } )
631632 . expectLimboDocs ( ...docKeys . slice ( 50 ) )
632633 ) ;
633634 } ) ;
635+
636+ specTest (
637+ 'Resume a query with bloom filter when there is no document changes' ,
638+ [ ] ,
639+ ( ) => {
640+ const query1 = query ( 'collection' ) ;
641+ const docA = doc ( 'collection/a' , 1000 , { v : 1 } ) ;
642+ const bloomFilterProto = generateBloomFilterProto ( {
643+ contains : [ docA ] ,
644+ notContains : [ ]
645+ } ) ;
646+ return (
647+ spec ( )
648+ . userListens ( query1 )
649+ . watchAcksFull ( query1 , 1000 , docA )
650+ . expectEvents ( query1 , { added : [ docA ] } )
651+ . disableNetwork ( )
652+ . expectEvents ( query1 , { fromCache : true } )
653+ . enableNetwork ( )
654+ . restoreListen ( query1 , 'resume-token-1000' )
655+ . watchAcks ( query1 )
656+ // Nothing happened while this client was disconnected.
657+ // Bloom Filter includes docA as there are no changes since the resume token.
658+ . watchFilters ( [ query1 ] , [ docA . key ] , bloomFilterProto )
659+ // Expected count equals to documents in cache. Existence Filter matches.
660+ . watchCurrents ( query1 , 'resume-token-2000' )
661+ . watchSnapshots ( 2000 )
662+ . expectEvents ( query1 , { fromCache : false } )
663+ ) ;
664+ }
665+ ) ;
666+
667+ specTest (
668+ 'Resume a query with bloom filter when new documents are added' ,
669+ [ ] ,
670+ ( ) => {
671+ const query1 = query ( 'collection' ) ;
672+ const docA = doc ( 'collection/a' , 1000 , { v : 1 } ) ;
673+ const docB = doc ( 'collection/b' , 1000 , { v : 2 } ) ;
674+ const bloomFilterProto = generateBloomFilterProto ( {
675+ contains : [ docA , docB ] ,
676+ notContains : [ ]
677+ } ) ;
678+ return (
679+ spec ( )
680+ . userListens ( query1 )
681+ . watchAcksFull ( query1 , 1000 , docA )
682+ . expectEvents ( query1 , { added : [ docA ] } )
683+ . disableNetwork ( )
684+ . expectEvents ( query1 , { fromCache : true } )
685+ . enableNetwork ( )
686+ . restoreListen ( query1 , 'resume-token-1000' )
687+ . watchAcks ( query1 )
688+ // While this client was disconnected, another client added docB.
689+ . watchSends ( { affects : [ query1 ] } , docB )
690+ // Bloom Filter includes all the documents that match the query, both
691+ // those that haven't changed since the resume token and those newly added.
692+ . watchFilters ( [ query1 ] , [ docA . key , docB . key ] , bloomFilterProto )
693+ // Expected count equals to documents in cache. Existence Filter matches.
694+ . watchCurrents ( query1 , 'resume-token-2000' )
695+ . watchSnapshots ( 2000 )
696+ . expectEvents ( query1 , { added : [ docB ] , fromCache : false } )
697+ ) ;
698+ }
699+ ) ;
700+
701+ specTest (
702+ 'Resume a query with bloom filter when existing docs are updated' ,
703+ [ ] ,
704+ ( ) => {
705+ const query1 = query ( 'collection' , filter ( 'v' , '>=' , 1 ) ) ;
706+ const docA = doc ( 'collection/a' , 1000 , { v : 1 } ) ;
707+ const docB = doc ( 'collection/b' , 1000 , { v : 1 } ) ;
708+ const updatedDocB = doc ( 'collection/b' , 1000 , { v : 2 } ) ;
709+
710+ const bloomFilterProto = generateBloomFilterProto ( {
711+ contains : [ docA , updatedDocB ] ,
712+ notContains : [ ]
713+ } ) ;
714+ return (
715+ spec ( )
716+ . userListens ( query1 )
717+ . watchAcksFull ( query1 , 1000 , docA , docB )
718+ . expectEvents ( query1 , { added : [ docA , docB ] } )
719+ . disableNetwork ( )
720+ . expectEvents ( query1 , { fromCache : true } )
721+ . enableNetwork ( )
722+ . restoreListen ( query1 , 'resume-token-1000' )
723+ . watchAcks ( query1 )
724+ // While this client was disconnected, another client updated fields in docB.
725+ . watchSends ( { affects : [ query1 ] } , updatedDocB )
726+ // Bloom Filter includes all the documents that match the query, both
727+ // those that have changed since the resume token and those that have not.
728+ . watchFilters ( [ query1 ] , [ docA . key , updatedDocB . key ] , bloomFilterProto )
729+ // Expected count equals to documents in cache. Existence Filter matches.
730+ . watchCurrents ( query1 , 'resume-token-2000' )
731+ . watchSnapshots ( 2000 )
732+ . expectEvents ( query1 , { fromCache : false } )
733+ ) ;
734+ }
735+ ) ;
736+
737+ specTest (
738+ 'Resume a query with bloom filter when documents are updated to no longer match the query' ,
739+ [ ] ,
740+ ( ) => {
741+ const query1 = query ( 'collection' , filter ( 'v' , '==' , 1 ) ) ;
742+ const docA = doc ( 'collection/a' , 1000 , { v : 1 } ) ;
743+ const docB = doc ( 'collection/b' , 1000 , { v : 1 } ) ;
744+ const updatedDocB = doc ( 'collection/b' , 2000 , { v : 2 } ) ;
745+
746+ const bloomFilterProto = generateBloomFilterProto ( {
747+ contains : [ docA ] ,
748+ notContains : [ docB ]
749+ } ) ;
750+ return (
751+ spec ( )
752+ . userListens ( query1 )
753+ . watchAcksFull ( query1 , 1000 , docA , docB )
754+ . expectEvents ( query1 , { added : [ docA , docB ] } )
755+ . disableNetwork ( )
756+ . expectEvents ( query1 , { fromCache : true } )
757+ . enableNetwork ( )
758+ . restoreListen ( query1 , 'resume-token-1000' )
759+ . watchAcks ( query1 )
760+ // While this client was disconnected, another client modified docB to no longer match the
761+ // query. Bloom Filter includes only docA that matches the query since the resume token.
762+ . watchFilters ( [ query1 ] , [ docA . key ] , bloomFilterProto )
763+ . watchCurrents ( query1 , 'resume-token-2000' )
764+ . watchSnapshots ( 2000 )
765+ // Bloom Filter identifies that updatedDocB no longer matches the query, skips full query
766+ // and puts updatedDocB into limbo directly.
767+ . expectLimboDocs ( updatedDocB . key ) // updatedDocB is now in limbo.
768+ ) ;
769+ }
770+ ) ;
771+
772+ specTest (
773+ 'Resume a query with bloom filter when documents are added, removed and deleted' ,
774+ [ ] ,
775+ ( ) => {
776+ const query1 = query ( 'collection' , filter ( 'v' , '==' , 1 ) ) ;
777+ const docA = doc ( 'collection/a' , 1000 , { v : 1 } ) ;
778+ const docB = doc ( 'collection/b' , 1000 , { v : 1 } ) ;
779+ const updatedDocB = doc ( 'collection/b' , 2000 , { v : 2 } ) ;
780+ const docC = doc ( 'collection/c' , 1000 , { v : 1 } ) ;
781+ const docD = doc ( 'collection/d' , 1000 , { v : 1 } ) ;
782+ const bloomFilterProto = generateBloomFilterProto ( {
783+ contains : [ docA , docD ] ,
784+ notContains : [ docB , docC ]
785+ } ) ;
786+
787+ return (
788+ spec ( )
789+ . userListens ( query1 )
790+ . watchAcksFull ( query1 , 1000 , docA , docB , docC )
791+ . expectEvents ( query1 , { added : [ docA , docB , docC ] } )
792+ . disableNetwork ( )
793+ . expectEvents ( query1 , { fromCache : true } )
794+ . enableNetwork ( )
795+ . restoreListen ( query1 , 'resume-token-1000' )
796+ . watchAcks ( query1 )
797+ // While this client was disconnected, another client modified docB to no longer match the
798+ // query, deleted docC and added docD.
799+ . watchSends ( { affects : [ query1 ] } , docD )
800+ // Bloom Filter includes all the documents that match the query.
801+ . watchFilters ( [ query1 ] , [ docA . key , docD . key ] , bloomFilterProto )
802+ . watchCurrents ( query1 , 'resume-token-2000' )
803+ . watchSnapshots ( 2000 )
804+ . expectEvents ( query1 , { added : [ docD ] , fromCache : true } )
805+ // Bloom Filter identifies that updatedDocB and docC no longer match the query, skips full
806+ // query and puts them into limbo directly.
807+ . expectLimboDocs ( updatedDocB . key , docC . key ) // updatedDocB and docC is now in limbo.
808+ ) ;
809+ }
810+ ) ;
634811} ) ;
0 commit comments