FSTMutation.mm 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616
  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/Model/FSTMutation.h"
  17. #include <memory>
  18. #include <set>
  19. #include <string>
  20. #include <utility>
  21. #include <vector>
  22. #import "FIRTimestamp.h"
  23. #import "Firestore/Source/Model/FSTDocument.h"
  24. #import "Firestore/Source/Util/FSTClasses.h"
  25. #include "Firestore/core/include/firebase/firestore/timestamp.h"
  26. #include "Firestore/core/src/firebase/firestore/model/document.h"
  27. #include "Firestore/core/src/firebase/firestore/model/document_key.h"
  28. #include "Firestore/core/src/firebase/firestore/model/field_mask.h"
  29. #include "Firestore/core/src/firebase/firestore/model/field_path.h"
  30. #include "Firestore/core/src/firebase/firestore/model/field_transform.h"
  31. #include "Firestore/core/src/firebase/firestore/model/field_value.h"
  32. #include "Firestore/core/src/firebase/firestore/model/precondition.h"
  33. #include "Firestore/core/src/firebase/firestore/model/transform_operations.h"
  34. #include "Firestore/core/src/firebase/firestore/util/hard_assert.h"
  35. using firebase::Timestamp;
  36. using firebase::firestore::model::ArrayTransform;
  37. using firebase::firestore::model::DocumentKey;
  38. using firebase::firestore::model::DocumentState;
  39. using firebase::firestore::model::FieldMask;
  40. using firebase::firestore::model::FieldPath;
  41. using firebase::firestore::model::FieldTransform;
  42. using firebase::firestore::model::FieldValue;
  43. using firebase::firestore::model::ObjectValue;
  44. using firebase::firestore::model::Precondition;
  45. using firebase::firestore::model::ServerTimestampTransform;
  46. using firebase::firestore::model::SnapshotVersion;
  47. using firebase::firestore::model::TransformOperation;
  48. using firebase::firestore::util::Hash;
  49. NS_ASSUME_NONNULL_BEGIN
  50. #pragma mark - FSTMutationResult
  51. @implementation FSTMutationResult {
  52. SnapshotVersion _version;
  53. absl::optional<std::vector<FieldValue>> _transformResults;
  54. }
  55. - (instancetype)initWithVersion:(SnapshotVersion)version
  56. transformResults:(absl::optional<std::vector<FieldValue>>)transformResults {
  57. if (self = [super init]) {
  58. _version = std::move(version);
  59. _transformResults = std::move(transformResults);
  60. }
  61. return self;
  62. }
  63. - (const SnapshotVersion &)version {
  64. return _version;
  65. }
  66. @end
  67. #pragma mark - FSTMutation
  68. @implementation FSTMutation {
  69. DocumentKey _key;
  70. Precondition _precondition;
  71. }
  72. - (instancetype)initWithKey:(DocumentKey)key precondition:(Precondition)precondition {
  73. if (self = [super init]) {
  74. _key = std::move(key);
  75. _precondition = std::move(precondition);
  76. }
  77. return self;
  78. }
  79. - (FSTMaybeDocument *)applyToRemoteDocument:(nullable FSTMaybeDocument *)maybeDoc
  80. mutationResult:(FSTMutationResult *)mutationResult {
  81. @throw FSTAbstractMethodException(); // NOLINT
  82. }
  83. - (nullable FSTMaybeDocument *)applyToLocalDocument:(nullable FSTMaybeDocument *)maybeDoc
  84. baseDocument:(nullable FSTMaybeDocument *)baseDoc
  85. localWriteTime:(const Timestamp &)localWriteTime {
  86. @throw FSTAbstractMethodException(); // NOLINT
  87. }
  88. - (absl::optional<ObjectValue>)extractBaseValue:(nullable FSTMaybeDocument *)maybeDoc {
  89. return absl::nullopt;
  90. }
  91. - (const DocumentKey &)key {
  92. return _key;
  93. }
  94. - (const firebase::firestore::model::Precondition &)precondition {
  95. return _precondition;
  96. }
  97. - (void)verifyKeyMatches:(nullable FSTMaybeDocument *)maybeDoc {
  98. if (maybeDoc) {
  99. HARD_ASSERT(maybeDoc.key == self.key, "Can only set a document with the same key");
  100. }
  101. }
  102. /**
  103. * Returns the version from the given document for use as the result of a mutation. Mutations are
  104. * defined to return the version of the base document only if it is an existing document. Deleted
  105. * and unknown documents have a post-mutation version of {@code SnapshotVersion::None()}.
  106. */
  107. - (const SnapshotVersion &)postMutationVersionForDocument:(FSTMaybeDocument *)maybeDoc {
  108. return [maybeDoc isKindOfClass:[FSTDocument class]] ? maybeDoc.version : SnapshotVersion::None();
  109. }
  110. @end
  111. #pragma mark - FSTSetMutation
  112. @implementation FSTSetMutation {
  113. }
  114. - (instancetype)initWithKey:(DocumentKey)key
  115. value:(ObjectValue)value
  116. precondition:(Precondition)precondition {
  117. if (self = [super initWithKey:std::move(key) precondition:std::move(precondition)]) {
  118. _value = std::move(value);
  119. }
  120. return self;
  121. }
  122. - (NSString *)description {
  123. return [NSString stringWithFormat:@"<FSTSetMutation key=%s value=%s precondition=%@>",
  124. self.key.ToString().c_str(), self.value.ToString().c_str(),
  125. self.precondition.description()];
  126. }
  127. - (BOOL)isEqual:(id)other {
  128. if (other == self) {
  129. return YES;
  130. }
  131. if (![other isKindOfClass:[FSTSetMutation class]]) {
  132. return NO;
  133. }
  134. FSTSetMutation *otherMutation = (FSTSetMutation *)other;
  135. return self.key == otherMutation.key && self.value == otherMutation.value &&
  136. self.precondition == otherMutation.precondition;
  137. }
  138. - (NSUInteger)hash {
  139. return Hash(self.key, self.precondition, self.value);
  140. }
  141. - (nullable FSTMaybeDocument *)applyToLocalDocument:(nullable FSTMaybeDocument *)maybeDoc
  142. baseDocument:(nullable FSTMaybeDocument *)baseDoc
  143. localWriteTime:(const Timestamp &)localWriteTime {
  144. [self verifyKeyMatches:maybeDoc];
  145. if (!self.precondition.IsValidFor(maybeDoc)) {
  146. return maybeDoc;
  147. }
  148. SnapshotVersion version = [self postMutationVersionForDocument:maybeDoc];
  149. return [FSTDocument documentWithData:self.value
  150. key:self.key
  151. version:version
  152. state:DocumentState::kLocalMutations];
  153. }
  154. - (FSTMaybeDocument *)applyToRemoteDocument:(nullable FSTMaybeDocument *)maybeDoc
  155. mutationResult:(FSTMutationResult *)mutationResult {
  156. [self verifyKeyMatches:maybeDoc];
  157. HARD_ASSERT(!mutationResult.transformResults, "Transform results received by FSTSetMutation.");
  158. // Unlike applyToLocalView, if we're applying a mutation to a remote document the server has
  159. // accepted the mutation so the precondition must have held.
  160. return [FSTDocument documentWithData:self.value
  161. key:self.key
  162. version:mutationResult.version
  163. state:DocumentState::kCommittedMutations];
  164. }
  165. - (absl::optional<ObjectValue>)extractBaseValue:(nullable FSTMaybeDocument *)maybeDoc {
  166. return absl::nullopt;
  167. }
  168. @end
  169. #pragma mark - FSTPatchMutation
  170. @implementation FSTPatchMutation {
  171. FieldMask _fieldMask;
  172. }
  173. - (instancetype)initWithKey:(DocumentKey)key
  174. fieldMask:(FieldMask)fieldMask
  175. value:(ObjectValue)value
  176. precondition:(Precondition)precondition {
  177. self = [super initWithKey:std::move(key) precondition:std::move(precondition)];
  178. if (self) {
  179. _fieldMask = std::move(fieldMask);
  180. _value = value;
  181. }
  182. return self;
  183. }
  184. - (const FieldMask *)fieldMask {
  185. return &_fieldMask;
  186. }
  187. - (BOOL)isEqual:(id)other {
  188. if (other == self) {
  189. return YES;
  190. }
  191. if (![other isKindOfClass:[FSTPatchMutation class]]) {
  192. return NO;
  193. }
  194. FSTPatchMutation *otherMutation = (FSTPatchMutation *)other;
  195. return self.key == otherMutation.key && _fieldMask == *(otherMutation.fieldMask) &&
  196. self.value == otherMutation.value && self.precondition == otherMutation.precondition;
  197. }
  198. - (NSUInteger)hash {
  199. return Hash(self.key, self.precondition, _fieldMask, self.value);
  200. }
  201. - (NSString *)description {
  202. return [NSString stringWithFormat:@"<FSTPatchMutation key=%s mask=%s value=%s precondition=%@>",
  203. self.key.ToString().c_str(), _fieldMask.ToString().c_str(),
  204. self.value.ToString().c_str(), self.precondition.description()];
  205. }
  206. /**
  207. * Patches the data of document if available or creates a new document. Note that this does not
  208. * check whether or not the precondition of this patch holds.
  209. */
  210. - (ObjectValue)patchDocument:(nullable FSTMaybeDocument *)maybeDoc {
  211. ObjectValue data;
  212. if ([maybeDoc isKindOfClass:[FSTDocument class]]) {
  213. data = ((FSTDocument *)maybeDoc).data;
  214. } else {
  215. data = ObjectValue::Empty();
  216. }
  217. return [self patchObjectValue:data];
  218. }
  219. - (nullable FSTMaybeDocument *)applyToLocalDocument:(nullable FSTMaybeDocument *)maybeDoc
  220. baseDocument:(nullable FSTMaybeDocument *)baseDoc
  221. localWriteTime:(const Timestamp &)localWriteTime {
  222. [self verifyKeyMatches:maybeDoc];
  223. if (!self.precondition.IsValidFor(maybeDoc)) {
  224. return maybeDoc;
  225. }
  226. ObjectValue newData = [self patchDocument:maybeDoc];
  227. SnapshotVersion version = [self postMutationVersionForDocument:maybeDoc];
  228. return [FSTDocument documentWithData:newData
  229. key:self.key
  230. version:version
  231. state:DocumentState::kLocalMutations];
  232. }
  233. - (absl::optional<ObjectValue>)extractBaseValue:(nullable FSTMaybeDocument *)maybeDoc {
  234. return absl::nullopt;
  235. }
  236. - (FSTMaybeDocument *)applyToRemoteDocument:(nullable FSTMaybeDocument *)maybeDoc
  237. mutationResult:(FSTMutationResult *)mutationResult {
  238. [self verifyKeyMatches:maybeDoc];
  239. HARD_ASSERT(!mutationResult.transformResults, "Transform results received by FSTPatchMutation.");
  240. if (!self.precondition.IsValidFor(maybeDoc)) {
  241. // Since the mutation was not rejected, we know that the precondition matched on the backend.
  242. // We therefore must not have the expected version of the document in our cache and return a
  243. // FSTUnknownDocument with the known updateTime.
  244. return [FSTUnknownDocument documentWithKey:self.key version:mutationResult.version];
  245. }
  246. ObjectValue newData = [self patchDocument:maybeDoc];
  247. return [FSTDocument documentWithData:newData
  248. key:self.key
  249. version:mutationResult.version
  250. state:DocumentState::kCommittedMutations];
  251. }
  252. - (ObjectValue)patchObjectValue:(ObjectValue)objectValue {
  253. ObjectValue result = std::move(objectValue);
  254. for (const FieldPath &fieldPath : _fieldMask) {
  255. if (!fieldPath.empty()) {
  256. absl::optional<FieldValue> newValue = self.value.Get(fieldPath);
  257. if (newValue) {
  258. result = result.Set(fieldPath, *newValue);
  259. } else {
  260. result = result.Delete(fieldPath);
  261. }
  262. }
  263. }
  264. return result;
  265. }
  266. @end
  267. @implementation FSTTransformMutation {
  268. /** The field transforms to use when transforming the document. */
  269. std::vector<FieldTransform> _fieldTransforms;
  270. FieldMask _fieldMask;
  271. }
  272. - (instancetype)initWithKey:(DocumentKey)key
  273. fieldTransforms:(std::vector<FieldTransform>)fieldTransforms {
  274. // NOTE: We set a precondition of exists: true as a safety-check, since we always combine
  275. // FSTTransformMutations with a FSTSetMutation or FSTPatchMutation which (if successful) should
  276. // end up with an existing document.
  277. if (self = [super initWithKey:std::move(key) precondition:Precondition::Exists(true)]) {
  278. _fieldTransforms = std::move(fieldTransforms);
  279. std::set<FieldPath> fields;
  280. for (const auto &transform : _fieldTransforms) {
  281. fields.insert(transform.path());
  282. }
  283. _fieldMask = FieldMask(std::move(fields));
  284. }
  285. return self;
  286. }
  287. - (const std::vector<FieldTransform> &)fieldTransforms {
  288. return _fieldTransforms;
  289. }
  290. - (BOOL)isEqual:(id)other {
  291. if (other == self) {
  292. return YES;
  293. }
  294. if (![other isKindOfClass:[FSTTransformMutation class]]) {
  295. return NO;
  296. }
  297. FSTTransformMutation *otherMutation = (FSTTransformMutation *)other;
  298. return self.key == otherMutation.key && self.fieldTransforms == otherMutation.fieldTransforms &&
  299. self.precondition == otherMutation.precondition;
  300. }
  301. - (NSUInteger)hash {
  302. NSUInteger result = self.key.Hash();
  303. result = 31 * result + self.precondition.Hash();
  304. for (const auto &transform : self.fieldTransforms) {
  305. result = 31 * result + transform.Hash();
  306. }
  307. return result;
  308. }
  309. - (NSString *)description {
  310. std::string fieldTransforms;
  311. for (const auto &transform : self.fieldTransforms) {
  312. fieldTransforms += " " + transform.path().CanonicalString();
  313. }
  314. return [NSString stringWithFormat:@"<FSTTransformMutation key=%s transforms=%s precondition=%@>",
  315. self.key.ToString().c_str(), fieldTransforms.c_str(),
  316. self.precondition.description()];
  317. }
  318. - (nullable FSTMaybeDocument *)applyToLocalDocument:(nullable FSTMaybeDocument *)maybeDoc
  319. baseDocument:(nullable FSTMaybeDocument *)baseDoc
  320. localWriteTime:(const Timestamp &)localWriteTime {
  321. [self verifyKeyMatches:maybeDoc];
  322. if (!self.precondition.IsValidFor(maybeDoc)) {
  323. return maybeDoc;
  324. }
  325. // We only support transforms with precondition exists, so we can only apply it to an existing
  326. // document
  327. HARD_ASSERT([maybeDoc isMemberOfClass:[FSTDocument class]], "Unknown MaybeDocument type %s",
  328. [maybeDoc class]);
  329. FSTDocument *doc = (FSTDocument *)maybeDoc;
  330. std::vector<FieldValue> transformResults =
  331. [self localTransformResultsWithLocalDocument:maybeDoc
  332. baseDocument:baseDoc
  333. writeTime:localWriteTime];
  334. ObjectValue newData = [self transformObject:doc.data transformResults:transformResults];
  335. return [FSTDocument documentWithData:std::move(newData)
  336. key:doc.key
  337. version:doc.version
  338. state:DocumentState::kLocalMutations];
  339. }
  340. - (FSTMaybeDocument *)applyToRemoteDocument:(nullable FSTMaybeDocument *)maybeDoc
  341. mutationResult:(FSTMutationResult *)mutationResult {
  342. [self verifyKeyMatches:maybeDoc];
  343. HARD_ASSERT(mutationResult.transformResults,
  344. "Transform results missing for FSTTransformMutation.");
  345. if (!self.precondition.IsValidFor(maybeDoc)) {
  346. // Since the mutation was not rejected, we know that the precondition matched on the backend.
  347. // We therefore must not have the expected version of the document in our cache and return an
  348. // FSTUnknownDocument with the known updateTime.
  349. return [FSTUnknownDocument documentWithKey:self.key version:mutationResult.version];
  350. }
  351. // We only support transforms with precondition exists, so we can only apply it to an existing
  352. // document
  353. HARD_ASSERT([maybeDoc isMemberOfClass:[FSTDocument class]], "Unknown MaybeDocument type %s",
  354. [maybeDoc class]);
  355. FSTDocument *doc = (FSTDocument *)maybeDoc;
  356. HARD_ASSERT(mutationResult.transformResults.has_value());
  357. std::vector<FieldValue> transformResults =
  358. [self serverTransformResultsWithBaseDocument:maybeDoc
  359. serverTransformResults:*mutationResult.transformResults];
  360. ObjectValue newData = [self transformObject:doc.data transformResults:transformResults];
  361. return [FSTDocument documentWithData:newData
  362. key:self.key
  363. version:mutationResult.version
  364. state:DocumentState::kCommittedMutations];
  365. }
  366. - (absl::optional<ObjectValue>)extractBaseValue:(nullable FSTMaybeDocument *)maybeDoc {
  367. absl::optional<ObjectValue> base_object = absl::nullopt;
  368. for (const FieldTransform &transform : self.fieldTransforms) {
  369. absl::optional<FieldValue> existing_value;
  370. if ([maybeDoc isKindOfClass:[FSTDocument class]]) {
  371. existing_value = {[((FSTDocument *)maybeDoc) fieldForPath:transform.path()]};
  372. }
  373. absl::optional<FieldValue> coerced_value =
  374. transform.transformation().ComputeBaseValue(existing_value);
  375. if (coerced_value) {
  376. if (!base_object) {
  377. base_object = {ObjectValue::Empty()};
  378. }
  379. base_object = {base_object->Set(transform.path(), *coerced_value)};
  380. }
  381. }
  382. return base_object;
  383. }
  384. /**
  385. * Creates an array of "transform results" (a transform result is a field value representing the
  386. * result of applying a transform) for use after a FSTTransformMutation has been acknowledged by
  387. * the server.
  388. *
  389. * @param baseDocument The document prior to applying this mutation batch.
  390. * @param serverTransformResults The transform results received by the server.
  391. * @return The transform results array.
  392. */
  393. - (std::vector<FieldValue>)
  394. serverTransformResultsWithBaseDocument:(nullable FSTMaybeDocument *)baseDocument
  395. serverTransformResults:(const std::vector<FieldValue> &)serverTransformResults {
  396. std::vector<FieldValue> transformResults;
  397. HARD_ASSERT(self.fieldTransforms.size() == serverTransformResults.size(),
  398. "server transform result count (%s) should match field transforms count (%s)",
  399. serverTransformResults.size(), self.fieldTransforms.size());
  400. for (size_t i = 0; i < serverTransformResults.size(); i++) {
  401. const FieldTransform &fieldTransform = self.fieldTransforms[i];
  402. const TransformOperation &transform = fieldTransform.transformation();
  403. absl::optional<model::FieldValue> previousValue;
  404. if ([baseDocument isMemberOfClass:[FSTDocument class]]) {
  405. previousValue = [((FSTDocument *)baseDocument) fieldForPath:fieldTransform.path()];
  406. }
  407. transformResults.push_back(
  408. transform.ApplyToRemoteDocument(previousValue, serverTransformResults[i]));
  409. }
  410. return transformResults;
  411. }
  412. /**
  413. * Creates an array of "transform results" (a transform result is a field value representing the
  414. * result of applying a transform) for use when applying an FSTTransformMutation locally.
  415. *
  416. * @param maybeDocument The current state of the document after applying all previous mutations.
  417. * @param baseDocument The document prior to applying this mutation batch.
  418. * @param localWriteTime The local time of the transform mutation (used to generate
  419. * ServerTimestampValues).
  420. * @return The transform results array.
  421. */
  422. - (std::vector<FieldValue>)
  423. localTransformResultsWithLocalDocument:(nullable FSTMaybeDocument *)maybeDocument
  424. baseDocument:(nullable FSTMaybeDocument *)baseDocument
  425. writeTime:(const Timestamp &)localWriteTime {
  426. std::vector<FieldValue> transformResults;
  427. for (const FieldTransform &fieldTransform : self.fieldTransforms) {
  428. const TransformOperation &transform = fieldTransform.transformation();
  429. absl::optional<FieldValue> previousValue;
  430. if ([maybeDocument isMemberOfClass:[FSTDocument class]]) {
  431. previousValue = [((FSTDocument *)maybeDocument) fieldForPath:fieldTransform.path()];
  432. }
  433. if (!previousValue && [baseDocument isMemberOfClass:[FSTDocument class]]) {
  434. // If the current document does not contain a value for the mutated field, use the value
  435. // that existed before applying this mutation batch. This solves an edge case where a
  436. // FSTPatchMutation clears the values in a nested map before the FSTTransformMutation is
  437. // applied.
  438. previousValue = [((FSTDocument *)baseDocument) fieldForPath:fieldTransform.path()];
  439. }
  440. transformResults.push_back(transform.ApplyToLocalView(previousValue, localWriteTime));
  441. }
  442. return transformResults;
  443. }
  444. - (ObjectValue)transformObject:(ObjectValue)objectValue
  445. transformResults:(const std::vector<FieldValue> &)transformResults {
  446. HARD_ASSERT(transformResults.size() == self.fieldTransforms.size(),
  447. "Transform results length mismatch.");
  448. for (size_t i = 0; i < self.fieldTransforms.size(); i++) {
  449. const FieldTransform &fieldTransform = self.fieldTransforms[i];
  450. const FieldPath &fieldPath = fieldTransform.path();
  451. objectValue = objectValue.Set(fieldPath, transformResults[i]);
  452. }
  453. return objectValue;
  454. }
  455. @end
  456. #pragma mark - FSTDeleteMutation
  457. @implementation FSTDeleteMutation
  458. - (BOOL)isEqual:(id)other {
  459. if (other == self) {
  460. return YES;
  461. }
  462. if (![other isKindOfClass:[FSTDeleteMutation class]]) {
  463. return NO;
  464. }
  465. FSTDeleteMutation *otherMutation = (FSTDeleteMutation *)other;
  466. return self.key == otherMutation.key && self.precondition == otherMutation.precondition;
  467. }
  468. - (NSUInteger)hash {
  469. return Hash(self.key, self.precondition);
  470. }
  471. - (NSString *)description {
  472. return [NSString stringWithFormat:@"<FSTDeleteMutation key=%s precondition=%@>",
  473. self.key.ToString().c_str(), self.precondition.description()];
  474. }
  475. - (nullable FSTMaybeDocument *)applyToLocalDocument:(nullable FSTMaybeDocument *)maybeDoc
  476. baseDocument:(nullable FSTMaybeDocument *)baseDoc
  477. localWriteTime:(const Timestamp &)localWriteTime {
  478. [self verifyKeyMatches:maybeDoc];
  479. if (!self.precondition.IsValidFor(maybeDoc)) {
  480. return maybeDoc;
  481. }
  482. return [FSTDeletedDocument documentWithKey:self.key
  483. version:SnapshotVersion::None()
  484. hasCommittedMutations:NO];
  485. }
  486. - (FSTMaybeDocument *)applyToRemoteDocument:(nullable FSTMaybeDocument *)maybeDoc
  487. mutationResult:(FSTMutationResult *)mutationResult {
  488. [self verifyKeyMatches:maybeDoc];
  489. if (mutationResult) {
  490. HARD_ASSERT(!mutationResult.transformResults,
  491. "Transform results received by FSTDeleteMutation.");
  492. }
  493. // Unlike applyToLocalView, if we're applying a mutation to a remote document the server has
  494. // accepted the mutation so the precondition must have held.
  495. // We store the deleted document at the commit version of the delete. Any document version
  496. // that the server sends us before the delete was applied is discarded
  497. return [FSTDeletedDocument documentWithKey:self.key
  498. version:mutationResult.version
  499. hasCommittedMutations:YES];
  500. }
  501. - (absl::optional<ObjectValue>)extractBaseValue:(nullable FSTMaybeDocument *)maybeDoc {
  502. return absl::nullopt;
  503. }
  504. @end
  505. NS_ASSUME_NONNULL_END