3030#include " database/src/desktop/core/listen_provider.h"
3131#include " database/src/desktop/core/server_values.h"
3232#include " database/src/desktop/core/sync_point.h"
33+ #include " database/src/desktop/core/tag.h"
3334#include " database/src/desktop/core/tree.h"
3435#include " database/src/desktop/util_desktop.h"
3536#include " database/src/desktop/view/event.h"
@@ -190,6 +191,14 @@ std::vector<Event> SyncTree::AddEventRegistration(
190191 // Now that we have the sync point, see if there is an existing view of the
191192 // database, and if there isn't, then set one up.
192193 bool view_already_exists = sync_point->ViewExistsForQuery (query_spec);
194+ if (!view_already_exists && !QuerySpecLoadsAllData (query_spec)) {
195+ // We need to track a tag for this query
196+ FIREBASE_DEV_ASSERT_MESSAGE (!query_spec_to_tag_map_.count (query_spec),
197+ " View does not exist but we have a tag" );
198+ Tag tag = GetNextQueryTag ();
199+ query_spec_to_tag_map_[query_spec] = tag;
200+ tag_to_query_spec_map_[*tag] = query_spec;
201+ }
193202 WriteTreeRef writes_cache = pending_write_tree_->ChildWrites (path);
194203 events = sync_point->AddEventRegistration (Move (event_registration),
195204 writes_cache, server_cache,
@@ -203,6 +212,86 @@ std::vector<Event> SyncTree::AddEventRegistration(
203212 return events;
204213}
205214
215+ // Apply a listen complete to a path.
216+ std::vector<Event> SyncTree::ApplyTaggedListenComplete (const Tag& tag) {
217+ std::vector<Event> results;
218+ persistence_manager_->RunInTransaction ([&, this ]() -> bool {
219+ const QuerySpec* query_spec = this ->QuerySpecForTag (tag);
220+ if (query_spec != nullptr ) {
221+ this ->persistence_manager_ ->SetQueryComplete (*query_spec);
222+ Operation op = Operation::ListenComplete (
223+ OperationSource::ForServerTaggedQuery (query_spec->params ), Path ());
224+ results = this ->ApplyTaggedOperation (*query_spec, op);
225+ return true ;
226+ } else {
227+ // We've already removed the query. No big deal, ignore the update
228+ return true ;
229+ }
230+ });
231+ return results;
232+ }
233+
234+ std::vector<Event> SyncTree::ApplyTaggedOperation (const QuerySpec& query_spec,
235+ const Operation& operation) {
236+ const Path& query_path = query_spec.path ;
237+ SyncPoint* sync_point = sync_point_tree_.GetValueAt (query_path);
238+ FIREBASE_DEV_ASSERT_MESSAGE (
239+ sync_point != nullptr ,
240+ " Missing sync point for query tag that we're tracking" );
241+ WriteTreeRef writes_cache = pending_write_tree_->ChildWrites (query_path);
242+ return sync_point->ApplyOperation (operation, writes_cache, nullptr ,
243+ persistence_manager_.get ());
244+ }
245+
246+ // Apply new server data for the specified tagged query.
247+ std::vector<Event> SyncTree::ApplyTaggedQueryOverwrite (const Path& path,
248+ const Variant& snap,
249+ const Tag& tag) {
250+ std::vector<Event> results;
251+ persistence_manager_->RunInTransaction ([&, this ]() -> bool {
252+ const QuerySpec* query_spec = this ->QuerySpecForTag (tag);
253+ if (query_spec != nullptr ) {
254+ Optional<Path> relative_path = Path::GetRelative (query_spec->path , path);
255+ QuerySpec query_to_overwrite =
256+ relative_path->empty () ? *query_spec : QuerySpec (path);
257+ this ->persistence_manager_ ->UpdateServerCache (query_to_overwrite, snap);
258+ Operation op = Operation::Overwrite (
259+ OperationSource::ForServerTaggedQuery (query_spec->params ),
260+ *relative_path, snap);
261+ results = this ->ApplyTaggedOperation (*query_spec, op);
262+ return true ;
263+ } else {
264+ // Query must have been removed already
265+ return true ;
266+ }
267+ });
268+ return results;
269+ }
270+
271+ std::vector<Event> SyncTree::ApplyTaggedQueryMerge (
272+ const Path& path, const std::map<Path, Variant>& changed_children,
273+ const Tag& tag) {
274+ std::vector<Event> results;
275+ persistence_manager_->RunInTransaction ([&, this ]() -> bool {
276+ const QuerySpec* query_spec = QuerySpecForTag (tag);
277+ if (query_spec != nullptr ) {
278+ Optional<Path> relative_path = Path::GetRelative (query_spec->path , path);
279+ FIREBASE_DEV_ASSERT (relative_path.has_value ());
280+ CompoundWrite merge = CompoundWrite::FromPathMerge (changed_children);
281+ this ->persistence_manager_ ->UpdateServerCache (path, merge);
282+ Operation op = Operation::Merge (
283+ OperationSource::ForServerTaggedQuery (query_spec->params ),
284+ *relative_path, merge);
285+ results = ApplyTaggedOperation (*query_spec, op);
286+ return true ;
287+ } else {
288+ // We've already removed the query. No big deal, ignore the update
289+ return true ;
290+ }
291+ });
292+ return results;
293+ }
294+
206295std::vector<Event> SyncTree::ApplyListenComplete (const Path& path) {
207296 std::vector<Event> results;
208297 persistence_manager_->RunInTransaction ([&, this ]() -> bool {
@@ -457,30 +546,40 @@ static QuerySpec QuerySpecForListening(const QuerySpec& query_spec) {
457546
458547void SyncTree::SetupListener (const QuerySpec& query_spec, const View* view) {
459548 const Path& path = query_spec.path ;
460- listen_provider_->StartListening (QuerySpecForListening (query_spec), view);
549+ const Tag& tag = TagForQuerySpec (query_spec);
550+ listen_provider_->StartListening (QuerySpecForListening (query_spec), tag,
551+ view);
461552
462553 Tree<SyncPoint>* subtree = sync_point_tree_.GetChild (path);
463554
464555 // The root of this subtree has our query. We're here because we definitely
465556 // need to send a listen for that, but we may need to shadow other listens
466557 // as well.
467-
468- // Shadow everything at or below this location, this is a default listener.
469- subtree->CallOnEach (path, [this ](const Path& relative_path,
470- const SyncPoint& child_sync_point) {
471- if (!relative_path.empty () && child_sync_point.HasCompleteView ()) {
472- const QuerySpec& query_spec =
473- child_sync_point.GetCompleteView ()->query_spec ();
474- listen_provider_->StopListening (MakeDefaultQuerySpec (query_spec));
475- } else {
476- // No default listener here.
477- for (const View* sync_point_view :
478- child_sync_point.GetIncompleteQueryViews ()) {
479- const QuerySpec& child_query_spec = sync_point_view->query_spec ();
480- listen_provider_->StopListening (MakeDefaultQuerySpec (child_query_spec));
558+ if (tag.has_value ()) {
559+ FIREBASE_DEV_ASSERT_MESSAGE (
560+ !subtree->value ()->HasCompleteView (),
561+ " If we're adding a query, it shouldn't be shadowed" );
562+ } else {
563+ // Shadow everything at or below this location, this is a default listener.
564+ subtree->CallOnEach (path, [this ](const Path& relative_path,
565+ const SyncPoint& child_sync_point) {
566+ if (!relative_path.empty () && child_sync_point.HasCompleteView ()) {
567+ const QuerySpec& query_spec =
568+ child_sync_point.GetCompleteView ()->query_spec ();
569+ listen_provider_->StopListening (QuerySpecForListening (query_spec),
570+ TagForQuerySpec (query_spec));
571+ } else {
572+ // No default listener here.
573+ for (const View* sync_point_view :
574+ child_sync_point.GetIncompleteQueryViews ()) {
575+ const QuerySpec& child_query_spec = sync_point_view->query_spec ();
576+ listen_provider_->StopListening (
577+ QuerySpecForListening (child_query_spec),
578+ TagForQuerySpec (child_query_spec));
579+ }
481580 }
482- }
483- });
581+ });
582+ }
484583}
485584
486585static void CollectDistinctViewsForSubTree (Tree<SyncPoint>* subtree,
@@ -561,7 +660,7 @@ std::vector<Event> SyncTree::RemoveEventRegistration(
561660 for (const View* view : new_views) {
562661 QuerySpec new_query = view->query_spec ();
563662 listen_provider_->StartListening (QuerySpecForListening (new_query),
564- view);
663+ TagForQuerySpec (new_query), view);
565664 }
566665 } else {
567666 // There's nothing below us, so nothing we need to start listening on
@@ -578,14 +677,19 @@ std::vector<Event> SyncTree::RemoveEventRegistration(
578677 // other queries here. Just cancel the one default. Otherwise, we need
579678 // to iterate through and cancel each individual query
580679 if (removing_default) {
581- listen_provider_->StopListening (QuerySpecForListening (query_spec));
680+ listen_provider_->StopListening (QuerySpecForListening (query_spec),
681+ Tag ());
582682 } else {
583683 for (QuerySpec query_to_remove : removed) {
684+ Tag tag = TagForQuerySpec (query_to_remove);
685+ FIREBASE_DEV_ASSERT (tag.has_value ());
584686 listen_provider_->StopListening (
585- QuerySpecForListening (query_to_remove));
687+ QuerySpecForListening (query_to_remove), tag );
586688 }
587689 }
588690 }
691+ // Now, clear all of the tags we're tracking for the removed listens.
692+ RemoveTags (removed);
589693 } else {
590694 // No-op, this listener must've been already removed.
591695 }
@@ -594,6 +698,29 @@ std::vector<Event> SyncTree::RemoveEventRegistration(
594698 return cancel_events;
595699}
596700
701+ void SyncTree::RemoveTags (const std::vector<QuerySpec>& queries) {
702+ for (const QuerySpec& removed_query : queries) {
703+ if (!QuerySpecLoadsAllData (removed_query)) {
704+ // We should have a tag for this
705+ Tag tag = TagForQuerySpec (removed_query);
706+ FIREBASE_DEV_ASSERT (tag.has_value ());
707+ query_spec_to_tag_map_.erase (removed_query);
708+ tag_to_query_spec_map_.erase (*tag);
709+ }
710+ }
711+ }
712+
713+ const QuerySpec* SyncTree::QuerySpecForTag (const Tag& tag) {
714+ return MapGet (&tag_to_query_spec_map_, *tag);
715+ }
716+
717+ Tag SyncTree::TagForQuerySpec (const QuerySpec& query_spec) {
718+ const Tag* tag_ptr = MapGet (&query_spec_to_tag_map_, query_spec);
719+ return tag_ptr ? *tag_ptr : Tag ();
720+ }
721+
722+ Tag SyncTree::GetNextQueryTag () { return Tag (next_query_tag_++); }
723+
597724} // namespace internal
598725} // namespace database
599726} // namespace firebase
0 commit comments