FSTLocalDocumentsView.mm 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207
  1. /*
  2. * Copyright 2017 Google
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. #import "Firestore/Source/Local/FSTLocalDocumentsView.h"
  17. #import "Firestore/Source/Core/FSTQuery.h"
  18. #import "Firestore/Source/Local/FSTMutationQueue.h"
  19. #import "Firestore/Source/Model/FSTDocument.h"
  20. #import "Firestore/Source/Model/FSTMutation.h"
  21. #import "Firestore/Source/Model/FSTMutationBatch.h"
  22. #include "Firestore/core/src/firebase/firestore/local/remote_document_cache.h"
  23. #include "Firestore/core/src/firebase/firestore/model/document_key.h"
  24. #include "Firestore/core/src/firebase/firestore/model/document_map.h"
  25. #include "Firestore/core/src/firebase/firestore/model/resource_path.h"
  26. #include "Firestore/core/src/firebase/firestore/model/snapshot_version.h"
  27. #include "Firestore/core/src/firebase/firestore/util/hard_assert.h"
  28. using firebase::firestore::local::RemoteDocumentCache;
  29. using firebase::firestore::model::DocumentKey;
  30. using firebase::firestore::model::DocumentKeySet;
  31. using firebase::firestore::model::DocumentMap;
  32. using firebase::firestore::model::MaybeDocumentMap;
  33. using firebase::firestore::model::ResourcePath;
  34. using firebase::firestore::model::SnapshotVersion;
  35. NS_ASSUME_NONNULL_BEGIN
  36. @interface FSTLocalDocumentsView ()
  37. - (instancetype)initWithRemoteDocumentCache:(RemoteDocumentCache *)remoteDocumentCache
  38. mutationQueue:(id<FSTMutationQueue>)mutationQueue
  39. NS_DESIGNATED_INITIALIZER;
  40. @property(nonatomic, strong, readonly) id<FSTMutationQueue> mutationQueue;
  41. @end
  42. @implementation FSTLocalDocumentsView {
  43. RemoteDocumentCache *_remoteDocumentCache;
  44. }
  45. + (instancetype)viewWithRemoteDocumentCache:(RemoteDocumentCache *)remoteDocumentCache
  46. mutationQueue:(id<FSTMutationQueue>)mutationQueue {
  47. return [[FSTLocalDocumentsView alloc] initWithRemoteDocumentCache:remoteDocumentCache
  48. mutationQueue:mutationQueue];
  49. }
  50. - (instancetype)initWithRemoteDocumentCache:(RemoteDocumentCache *)remoteDocumentCache
  51. mutationQueue:(id<FSTMutationQueue>)mutationQueue {
  52. if (self = [super init]) {
  53. _remoteDocumentCache = remoteDocumentCache;
  54. _mutationQueue = mutationQueue;
  55. }
  56. return self;
  57. }
  58. - (nullable FSTMaybeDocument *)documentForKey:(const DocumentKey &)key {
  59. NSArray<FSTMutationBatch *> *batches =
  60. [self.mutationQueue allMutationBatchesAffectingDocumentKey:key];
  61. return [self documentForKey:key inBatches:batches];
  62. }
  63. // Internal version of documentForKey: which allows reusing `batches`.
  64. - (nullable FSTMaybeDocument *)documentForKey:(const DocumentKey &)key
  65. inBatches:(NSArray<FSTMutationBatch *> *)batches {
  66. FSTMaybeDocument *_Nullable document = _remoteDocumentCache->Get(key);
  67. for (FSTMutationBatch *batch in batches) {
  68. document = [batch applyToLocalDocument:document documentKey:key];
  69. }
  70. return document;
  71. }
  72. // Returns the view of the given `docs` as they would appear after applying all
  73. // mutations in the given `batches`.
  74. - (MaybeDocumentMap)applyLocalMutationsToDocuments:(const MaybeDocumentMap &)docs
  75. fromBatches:(NSArray<FSTMutationBatch *> *)batches {
  76. MaybeDocumentMap results;
  77. for (const auto &kv : docs) {
  78. const DocumentKey &key = kv.first;
  79. FSTMaybeDocument *localView = kv.second;
  80. for (FSTMutationBatch *batch in batches) {
  81. localView = [batch applyToLocalDocument:localView documentKey:key];
  82. }
  83. results = results.insert(key, localView);
  84. }
  85. return results;
  86. }
  87. - (MaybeDocumentMap)documentsForKeys:(const DocumentKeySet &)keys {
  88. MaybeDocumentMap docs = _remoteDocumentCache->GetAll(keys);
  89. return [self localViewsForDocuments:docs];
  90. }
  91. /**
  92. * Similar to `documentsForKeys`, but creates the local view from the given
  93. * `baseDocs` without retrieving documents from the local store.
  94. */
  95. - (MaybeDocumentMap)localViewsForDocuments:(const MaybeDocumentMap &)baseDocs {
  96. MaybeDocumentMap results;
  97. DocumentKeySet allKeys;
  98. for (const auto &kv : baseDocs) {
  99. allKeys = allKeys.insert(kv.first);
  100. }
  101. NSArray<FSTMutationBatch *> *batches =
  102. [self.mutationQueue allMutationBatchesAffectingDocumentKeys:allKeys];
  103. MaybeDocumentMap docs = [self applyLocalMutationsToDocuments:baseDocs fromBatches:batches];
  104. for (const auto &kv : docs) {
  105. const DocumentKey &key = kv.first;
  106. FSTMaybeDocument *maybeDoc = kv.second;
  107. // TODO(http://b/32275378): Don't conflate missing / deleted.
  108. if (!maybeDoc) {
  109. maybeDoc = [FSTDeletedDocument documentWithKey:key
  110. version:SnapshotVersion::None()
  111. hasCommittedMutations:NO];
  112. }
  113. results = results.insert(key, maybeDoc);
  114. }
  115. return results;
  116. }
  117. - (DocumentMap)documentsMatchingQuery:(FSTQuery *)query {
  118. if (DocumentKey::IsDocumentKey(query.path)) {
  119. return [self documentsMatchingDocumentQuery:query.path];
  120. } else {
  121. return [self documentsMatchingCollectionQuery:query];
  122. }
  123. }
  124. - (DocumentMap)documentsMatchingDocumentQuery:(const ResourcePath &)docPath {
  125. DocumentMap result;
  126. // Just do a simple document lookup.
  127. FSTMaybeDocument *doc = [self documentForKey:DocumentKey{docPath}];
  128. if ([doc isKindOfClass:[FSTDocument class]]) {
  129. result = result.insert(doc.key, static_cast<FSTDocument *>(doc));
  130. }
  131. return result;
  132. }
  133. - (DocumentMap)documentsMatchingCollectionQuery:(FSTQuery *)query {
  134. DocumentMap results = _remoteDocumentCache->GetMatching(query);
  135. // Get locally persisted mutation batches.
  136. NSArray<FSTMutationBatch *> *matchingBatches =
  137. [self.mutationQueue allMutationBatchesAffectingQuery:query];
  138. for (FSTMutationBatch *batch in matchingBatches) {
  139. for (FSTMutation *mutation in batch.mutations) {
  140. // Only process documents belonging to the collection.
  141. if (!query.path.IsImmediateParentOf(mutation.key.path())) {
  142. continue;
  143. }
  144. const DocumentKey &key = mutation.key;
  145. // baseDoc may be nil for the documents that weren't yet written to the backend.
  146. FSTMaybeDocument *baseDoc = nil;
  147. auto found = results.underlying_map().find(key);
  148. if (found != results.underlying_map().end()) {
  149. baseDoc = found->second;
  150. }
  151. FSTMaybeDocument *mutatedDoc = [mutation applyToLocalDocument:baseDoc
  152. baseDocument:baseDoc
  153. localWriteTime:batch.localWriteTime];
  154. if ([mutatedDoc isKindOfClass:[FSTDocument class]]) {
  155. results = results.insert(key, static_cast<FSTDocument *>(mutatedDoc));
  156. } else {
  157. results = results.erase(key);
  158. }
  159. }
  160. }
  161. // Finally, filter out any documents that don't actually match the query. Note that the extra
  162. // reference here prevents ARC from deallocating the initial unfiltered results while we're
  163. // enumerating them.
  164. DocumentMap unfiltered = results;
  165. for (const auto &kv : unfiltered.underlying_map()) {
  166. const DocumentKey &key = kv.first;
  167. FSTDocument *doc = static_cast<FSTDocument *>(kv.second);
  168. if (![query matchesDocument:doc]) {
  169. results = results.erase(key);
  170. }
  171. }
  172. return results;
  173. }
  174. @end
  175. NS_ASSUME_NONNULL_END