FSTLocalDocumentsView.mm 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245
  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. #include <string>
  18. #include <vector>
  19. #import "Firestore/Source/Core/FSTQuery.h"
  20. #import "Firestore/Source/Model/FSTDocument.h"
  21. #import "Firestore/Source/Model/FSTMutation.h"
  22. #import "Firestore/Source/Model/FSTMutationBatch.h"
  23. #include "Firestore/core/src/firebase/firestore/local/index_manager.h"
  24. #include "Firestore/core/src/firebase/firestore/local/mutation_queue.h"
  25. #include "Firestore/core/src/firebase/firestore/local/remote_document_cache.h"
  26. #include "Firestore/core/src/firebase/firestore/model/document_key.h"
  27. #include "Firestore/core/src/firebase/firestore/model/document_map.h"
  28. #include "Firestore/core/src/firebase/firestore/model/resource_path.h"
  29. #include "Firestore/core/src/firebase/firestore/model/snapshot_version.h"
  30. #include "Firestore/core/src/firebase/firestore/util/hard_assert.h"
  31. #include "Firestore/core/src/firebase/firestore/util/string_apple.h"
  32. using firebase::firestore::local::IndexManager;
  33. using firebase::firestore::local::MutationQueue;
  34. using firebase::firestore::local::RemoteDocumentCache;
  35. using firebase::firestore::model::DocumentKey;
  36. using firebase::firestore::model::DocumentKeySet;
  37. using firebase::firestore::model::DocumentMap;
  38. using firebase::firestore::model::MaybeDocumentMap;
  39. using firebase::firestore::model::ResourcePath;
  40. using firebase::firestore::model::SnapshotVersion;
  41. using firebase::firestore::util::MakeString;
  42. NS_ASSUME_NONNULL_BEGIN
  43. @interface FSTLocalDocumentsView ()
  44. - (instancetype)initWithRemoteDocumentCache:(RemoteDocumentCache *)remoteDocumentCache
  45. mutationQueue:(MutationQueue *)mutationQueue
  46. indexManager:(IndexManager *)indexManager NS_DESIGNATED_INITIALIZER;
  47. @end
  48. @implementation FSTLocalDocumentsView {
  49. RemoteDocumentCache *_remoteDocumentCache;
  50. MutationQueue *_mutationQueue;
  51. IndexManager *_indexManager;
  52. }
  53. + (instancetype)viewWithRemoteDocumentCache:(RemoteDocumentCache *)remoteDocumentCache
  54. mutationQueue:(MutationQueue *)mutationQueue
  55. indexManager:(IndexManager *)indexManager {
  56. return [[FSTLocalDocumentsView alloc] initWithRemoteDocumentCache:remoteDocumentCache
  57. mutationQueue:mutationQueue
  58. indexManager:indexManager];
  59. }
  60. - (instancetype)initWithRemoteDocumentCache:(RemoteDocumentCache *)remoteDocumentCache
  61. mutationQueue:(MutationQueue *)mutationQueue
  62. indexManager:(IndexManager *)indexManager {
  63. if (self = [super init]) {
  64. _remoteDocumentCache = remoteDocumentCache;
  65. _mutationQueue = mutationQueue;
  66. _indexManager = indexManager;
  67. }
  68. return self;
  69. }
  70. - (nullable FSTMaybeDocument *)documentForKey:(const DocumentKey &)key {
  71. std::vector<FSTMutationBatch *> batches =
  72. _mutationQueue->AllMutationBatchesAffectingDocumentKey(key);
  73. return [self documentForKey:key inBatches:batches];
  74. }
  75. // Internal version of documentForKey: which allows reusing `batches`.
  76. - (nullable FSTMaybeDocument *)documentForKey:(const DocumentKey &)key
  77. inBatches:(const std::vector<FSTMutationBatch *> &)batches {
  78. FSTMaybeDocument *_Nullable document = _remoteDocumentCache->Get(key);
  79. for (FSTMutationBatch *batch : batches) {
  80. document = [batch applyToLocalDocument:document documentKey:key];
  81. }
  82. return document;
  83. }
  84. // Returns the view of the given `docs` as they would appear after applying all
  85. // mutations in the given `batches`.
  86. - (MaybeDocumentMap)applyLocalMutationsToDocuments:(const MaybeDocumentMap &)docs
  87. fromBatches:
  88. (const std::vector<FSTMutationBatch *> &)batches {
  89. MaybeDocumentMap results;
  90. for (const auto &kv : docs) {
  91. const DocumentKey &key = kv.first;
  92. FSTMaybeDocument *localView = kv.second;
  93. for (FSTMutationBatch *batch : batches) {
  94. localView = [batch applyToLocalDocument:localView documentKey:key];
  95. }
  96. results = results.insert(key, localView);
  97. }
  98. return results;
  99. }
  100. - (MaybeDocumentMap)documentsForKeys:(const DocumentKeySet &)keys {
  101. MaybeDocumentMap docs = _remoteDocumentCache->GetAll(keys);
  102. return [self localViewsForDocuments:docs];
  103. }
  104. /**
  105. * Similar to `documentsForKeys`, but creates the local view from the given
  106. * `baseDocs` without retrieving documents from the local store.
  107. */
  108. - (MaybeDocumentMap)localViewsForDocuments:(const MaybeDocumentMap &)baseDocs {
  109. MaybeDocumentMap results;
  110. DocumentKeySet allKeys;
  111. for (const auto &kv : baseDocs) {
  112. allKeys = allKeys.insert(kv.first);
  113. }
  114. std::vector<FSTMutationBatch *> batches =
  115. _mutationQueue->AllMutationBatchesAffectingDocumentKeys(allKeys);
  116. MaybeDocumentMap docs = [self applyLocalMutationsToDocuments:baseDocs fromBatches:batches];
  117. for (const auto &kv : docs) {
  118. const DocumentKey &key = kv.first;
  119. FSTMaybeDocument *maybeDoc = kv.second;
  120. // TODO(http://b/32275378): Don't conflate missing / deleted.
  121. if (!maybeDoc) {
  122. maybeDoc = [FSTDeletedDocument documentWithKey:key
  123. version:SnapshotVersion::None()
  124. hasCommittedMutations:NO];
  125. }
  126. results = results.insert(key, maybeDoc);
  127. }
  128. return results;
  129. }
  130. - (DocumentMap)documentsMatchingQuery:(FSTQuery *)query {
  131. if ([query isDocumentQuery]) {
  132. return [self documentsMatchingDocumentQuery:query.path];
  133. } else if ([query isCollectionGroupQuery]) {
  134. return [self documentsMatchingCollectionGroupQuery:query];
  135. } else {
  136. return [self documentsMatchingCollectionQuery:query];
  137. }
  138. }
  139. - (DocumentMap)documentsMatchingDocumentQuery:(const ResourcePath &)docPath {
  140. DocumentMap result;
  141. // Just do a simple document lookup.
  142. FSTMaybeDocument *doc = [self documentForKey:DocumentKey{docPath}];
  143. if ([doc isKindOfClass:[FSTDocument class]]) {
  144. result = result.insert(doc.key, static_cast<FSTDocument *>(doc));
  145. }
  146. return result;
  147. }
  148. - (DocumentMap)documentsMatchingCollectionGroupQuery:(FSTQuery *)query {
  149. HARD_ASSERT(query.path.empty(),
  150. "Currently we only support collection group queries at the root.");
  151. std::string collection_id = MakeString(query.collectionGroup);
  152. std::vector<ResourcePath> parents = _indexManager->GetCollectionParents(collection_id);
  153. DocumentMap results;
  154. // Perform a collection query against each parent that contains the collection_id and
  155. // aggregate the results.
  156. for (const ResourcePath &parent : parents) {
  157. FSTQuery *collectionQuery = [query collectionQueryAtPath:parent.Append(collection_id)];
  158. DocumentMap collectionResults = [self documentsMatchingCollectionQuery:collectionQuery];
  159. for (const auto &kv : collectionResults.underlying_map()) {
  160. const DocumentKey &key = kv.first;
  161. FSTDocument *doc = static_cast<FSTDocument *>(kv.second);
  162. results = results.insert(key, doc);
  163. }
  164. }
  165. return results;
  166. }
  167. - (DocumentMap)documentsMatchingCollectionQuery:(FSTQuery *)query {
  168. DocumentMap results = _remoteDocumentCache->GetMatching(query);
  169. // Get locally persisted mutation batches.
  170. std::vector<FSTMutationBatch *> matchingBatches =
  171. _mutationQueue->AllMutationBatchesAffectingQuery(query);
  172. for (FSTMutationBatch *batch : matchingBatches) {
  173. for (FSTMutation *mutation : [batch mutations]) {
  174. // Only process documents belonging to the collection.
  175. if (!query.path.IsImmediateParentOf(mutation.key.path())) {
  176. continue;
  177. }
  178. const DocumentKey &key = mutation.key;
  179. // baseDoc may be nil for the documents that weren't yet written to the backend.
  180. FSTMaybeDocument *baseDoc = nil;
  181. auto found = results.underlying_map().find(key);
  182. if (found != results.underlying_map().end()) {
  183. baseDoc = found->second;
  184. }
  185. FSTMaybeDocument *mutatedDoc = [mutation applyToLocalDocument:baseDoc
  186. baseDocument:baseDoc
  187. localWriteTime:batch.localWriteTime];
  188. if ([mutatedDoc isKindOfClass:[FSTDocument class]]) {
  189. results = results.insert(key, static_cast<FSTDocument *>(mutatedDoc));
  190. } else {
  191. results = results.erase(key);
  192. }
  193. }
  194. }
  195. // Finally, filter out any documents that don't actually match the query. Note that the extra
  196. // reference here prevents ARC from deallocating the initial unfiltered results while we're
  197. // enumerating them.
  198. DocumentMap unfiltered = results;
  199. for (const auto &kv : unfiltered.underlying_map()) {
  200. const DocumentKey &key = kv.first;
  201. FSTDocument *doc = static_cast<FSTDocument *>(kv.second);
  202. if (![query matchesDocument:doc]) {
  203. results = results.erase(key);
  204. }
  205. }
  206. return results;
  207. }
  208. @end
  209. NS_ASSUME_NONNULL_END