FSTMutation.mm 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625
  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 <utility>
  18. #import "FIRTimestamp.h"
  19. #import "Firestore/Source/Core/FSTSnapshotVersion.h"
  20. #import "Firestore/Source/Model/FSTDocument.h"
  21. #import "Firestore/Source/Model/FSTFieldValue.h"
  22. #import "Firestore/Source/Util/FSTAssert.h"
  23. #import "Firestore/Source/Util/FSTClasses.h"
  24. #include "Firestore/core/src/firebase/firestore/model/document_key.h"
  25. #include "Firestore/core/src/firebase/firestore/model/field_path.h"
  26. using firebase::firestore::model::DocumentKey;
  27. using firebase::firestore::model::FieldPath;
  28. NS_ASSUME_NONNULL_BEGIN
  29. #pragma mark - FSTFieldMask
  30. @implementation FSTFieldMask {
  31. std::vector<FieldPath> _fields;
  32. }
  33. - (instancetype)initWithFields:(std::vector<FieldPath>)fields {
  34. if (self = [super init]) {
  35. _fields = std::move(fields);
  36. }
  37. return self;
  38. }
  39. - (BOOL)isEqual:(id)other {
  40. if (other == self) {
  41. return YES;
  42. }
  43. if (![other isKindOfClass:[FSTFieldMask class]]) {
  44. return NO;
  45. }
  46. FSTFieldMask *otherMask = (FSTFieldMask *)other;
  47. return _fields == otherMask->_fields;
  48. }
  49. - (NSUInteger)hash {
  50. NSUInteger hashResult = 0;
  51. for (const FieldPath &field : _fields) {
  52. hashResult = hashResult * 31u + field.Hash();
  53. }
  54. return hashResult;
  55. }
  56. - (const std::vector<FieldPath> &)fields {
  57. return _fields;
  58. }
  59. @end
  60. #pragma mark - FSTServerTimestampTransform
  61. @implementation FSTServerTimestampTransform
  62. + (instancetype)serverTimestampTransform {
  63. static FSTServerTimestampTransform *sharedInstance = nil;
  64. static dispatch_once_t onceToken;
  65. dispatch_once(&onceToken, ^{
  66. sharedInstance = [[FSTServerTimestampTransform alloc] init];
  67. });
  68. return sharedInstance;
  69. }
  70. - (BOOL)isEqual:(id)other {
  71. if (other == self) {
  72. return YES;
  73. }
  74. return [other isKindOfClass:[FSTServerTimestampTransform class]];
  75. }
  76. - (NSUInteger)hash {
  77. // arbitrary number since all instances are equal.
  78. return 37;
  79. }
  80. @end
  81. #pragma mark - FSTFieldTransform
  82. @implementation FSTFieldTransform {
  83. FieldPath _path;
  84. }
  85. - (instancetype)initWithPath:(FieldPath)path transform:(id<FSTTransformOperation>)transform {
  86. self = [super init];
  87. if (self) {
  88. _path = std::move(path);
  89. _transform = transform;
  90. }
  91. return self;
  92. }
  93. - (BOOL)isEqual:(id)other {
  94. if (other == self) return YES;
  95. if (![[other class] isEqual:[self class]]) return NO;
  96. FSTFieldTransform *otherFieldTransform = other;
  97. return self.path == otherFieldTransform.path &&
  98. [self.transform isEqual:otherFieldTransform.transform];
  99. }
  100. - (NSUInteger)hash {
  101. NSUInteger hash = self.path.Hash();
  102. hash = hash * 31 + [self.transform hash];
  103. return hash;
  104. }
  105. - (const firebase::firestore::model::FieldPath &)path {
  106. return _path;
  107. }
  108. @end
  109. #pragma mark - FSTPrecondition
  110. @implementation FSTPrecondition
  111. + (FSTPrecondition *)preconditionWithExists:(BOOL)exists {
  112. FSTPreconditionExists existsEnum = exists ? FSTPreconditionExistsYes : FSTPreconditionExistsNo;
  113. return [[FSTPrecondition alloc] initWithUpdateTime:nil exists:existsEnum];
  114. }
  115. + (FSTPrecondition *)preconditionWithUpdateTime:(FSTSnapshotVersion *)updateTime {
  116. return [[FSTPrecondition alloc] initWithUpdateTime:updateTime exists:FSTPreconditionExistsNotSet];
  117. }
  118. + (FSTPrecondition *)none {
  119. static dispatch_once_t onceToken;
  120. static FSTPrecondition *noPrecondition;
  121. dispatch_once(&onceToken, ^{
  122. noPrecondition =
  123. [[FSTPrecondition alloc] initWithUpdateTime:nil exists:FSTPreconditionExistsNotSet];
  124. });
  125. return noPrecondition;
  126. }
  127. - (instancetype)initWithUpdateTime:(FSTSnapshotVersion *_Nullable)updateTime
  128. exists:(FSTPreconditionExists)exists {
  129. if (self = [super init]) {
  130. _updateTime = updateTime;
  131. _exists = exists;
  132. }
  133. return self;
  134. }
  135. - (BOOL)isValidForDocument:(FSTMaybeDocument *_Nullable)maybeDoc {
  136. if (self.updateTime) {
  137. return
  138. [maybeDoc isKindOfClass:[FSTDocument class]] && [maybeDoc.version isEqual:self.updateTime];
  139. } else if (self.exists != FSTPreconditionExistsNotSet) {
  140. if (self.exists == FSTPreconditionExistsYes) {
  141. return [maybeDoc isKindOfClass:[FSTDocument class]];
  142. } else {
  143. FSTAssert(self.exists == FSTPreconditionExistsNo, @"Invalid precondition");
  144. return maybeDoc == nil || [maybeDoc isKindOfClass:[FSTDeletedDocument class]];
  145. }
  146. } else {
  147. FSTAssert(self.isNone, @"Precondition should be empty");
  148. return YES;
  149. }
  150. }
  151. - (BOOL)isNone {
  152. return self.updateTime == nil && self.exists == FSTPreconditionExistsNotSet;
  153. }
  154. - (BOOL)isEqual:(id)other {
  155. if (self == other) {
  156. return YES;
  157. }
  158. if (![other isKindOfClass:[FSTPrecondition class]]) {
  159. return NO;
  160. }
  161. FSTPrecondition *otherPrecondition = (FSTPrecondition *)other;
  162. // Compare references to cover nil equality
  163. return (self.updateTime == otherPrecondition.updateTime ||
  164. [self.updateTime isEqual:otherPrecondition.updateTime]) &&
  165. self.exists == otherPrecondition.exists;
  166. }
  167. - (NSUInteger)hash {
  168. NSUInteger hash = [self.updateTime hash];
  169. hash = hash * 31 + self.exists;
  170. return hash;
  171. }
  172. - (NSString *)description {
  173. if (self.isNone) {
  174. return @"<FSTPrecondition <none>>";
  175. } else {
  176. NSString *existsString;
  177. switch (self.exists) {
  178. case FSTPreconditionExistsYes:
  179. existsString = @"yes";
  180. break;
  181. case FSTPreconditionExistsNo:
  182. existsString = @"no";
  183. break;
  184. default:
  185. existsString = @"<not-set>";
  186. break;
  187. }
  188. return [NSString stringWithFormat:@"<FSTPrecondition updateTime=%@ exists=%@>", self.updateTime,
  189. existsString];
  190. }
  191. }
  192. @end
  193. #pragma mark - FSTMutationResult
  194. @implementation FSTMutationResult
  195. - (instancetype)initWithVersion:(FSTSnapshotVersion *_Nullable)version
  196. transformResults:(NSArray<FSTFieldValue *> *_Nullable)transformResults {
  197. if (self = [super init]) {
  198. _version = version;
  199. _transformResults = transformResults;
  200. }
  201. return self;
  202. }
  203. @end
  204. #pragma mark - FSTMutation
  205. @implementation FSTMutation {
  206. DocumentKey _key;
  207. }
  208. - (instancetype)initWithKey:(DocumentKey)key precondition:(FSTPrecondition *)precondition {
  209. if (self = [super init]) {
  210. _key = std::move(key);
  211. _precondition = precondition;
  212. }
  213. return self;
  214. }
  215. - (nullable FSTMaybeDocument *)applyTo:(nullable FSTMaybeDocument *)maybeDoc
  216. baseDocument:(nullable FSTMaybeDocument *)baseDoc
  217. localWriteTime:(FIRTimestamp *)localWriteTime
  218. mutationResult:(nullable FSTMutationResult *)mutationResult {
  219. @throw FSTAbstractMethodException(); // NOLINT
  220. }
  221. - (nullable FSTMaybeDocument *)applyTo:(nullable FSTMaybeDocument *)maybeDoc
  222. baseDocument:(nullable FSTMaybeDocument *)baseDoc
  223. localWriteTime:(nullable FIRTimestamp *)localWriteTime {
  224. return
  225. [self applyTo:maybeDoc baseDocument:baseDoc localWriteTime:localWriteTime mutationResult:nil];
  226. }
  227. - (const DocumentKey &)key {
  228. return _key;
  229. }
  230. @end
  231. #pragma mark - FSTSetMutation
  232. @implementation FSTSetMutation
  233. - (instancetype)initWithKey:(DocumentKey)key
  234. value:(FSTObjectValue *)value
  235. precondition:(FSTPrecondition *)precondition {
  236. if (self = [super initWithKey:std::move(key) precondition:precondition]) {
  237. _value = value;
  238. }
  239. return self;
  240. }
  241. - (NSString *)description {
  242. return [NSString stringWithFormat:@"<FSTSetMutation key=%s value=%@ precondition=%@>",
  243. self.key.ToString().c_str(), self.value, self.precondition];
  244. }
  245. - (BOOL)isEqual:(id)other {
  246. if (other == self) {
  247. return YES;
  248. }
  249. if (![other isKindOfClass:[FSTSetMutation class]]) {
  250. return NO;
  251. }
  252. FSTSetMutation *otherMutation = (FSTSetMutation *)other;
  253. return [self.key isEqual:otherMutation.key] && [self.value isEqual:otherMutation.value] &&
  254. [self.precondition isEqual:otherMutation.precondition];
  255. }
  256. - (NSUInteger)hash {
  257. NSUInteger result = [self.key hash];
  258. result = 31 * result + [self.precondition hash];
  259. result = 31 * result + [self.value hash];
  260. return result;
  261. }
  262. - (nullable FSTMaybeDocument *)applyTo:(nullable FSTMaybeDocument *)maybeDoc
  263. baseDocument:(nullable FSTMaybeDocument *)baseDoc
  264. localWriteTime:(FIRTimestamp *)localWriteTime
  265. mutationResult:(nullable FSTMutationResult *)mutationResult {
  266. if (mutationResult) {
  267. FSTAssert(!mutationResult.transformResults, @"Transform results received by FSTSetMutation.");
  268. }
  269. if (![self.precondition isValidForDocument:maybeDoc]) {
  270. return maybeDoc;
  271. }
  272. BOOL hasLocalMutations = (mutationResult == nil);
  273. if (!maybeDoc || [maybeDoc isMemberOfClass:[FSTDeletedDocument class]]) {
  274. // If the document didn't exist before, create it.
  275. return [FSTDocument documentWithData:self.value
  276. key:self.key
  277. version:[FSTSnapshotVersion noVersion]
  278. hasLocalMutations:hasLocalMutations];
  279. }
  280. FSTAssert([maybeDoc isMemberOfClass:[FSTDocument class]], @"Unknown MaybeDocument type %@",
  281. [maybeDoc class]);
  282. FSTDocument *doc = (FSTDocument *)maybeDoc;
  283. FSTAssert([doc.key isEqual:self.key], @"Can only set a document with the same key");
  284. return [FSTDocument documentWithData:self.value
  285. key:doc.key
  286. version:doc.version
  287. hasLocalMutations:hasLocalMutations];
  288. }
  289. @end
  290. #pragma mark - FSTPatchMutation
  291. @implementation FSTPatchMutation
  292. - (instancetype)initWithKey:(DocumentKey)key
  293. fieldMask:(FSTFieldMask *)fieldMask
  294. value:(FSTObjectValue *)value
  295. precondition:(FSTPrecondition *)precondition {
  296. self = [super initWithKey:std::move(key) precondition:precondition];
  297. if (self) {
  298. _fieldMask = fieldMask;
  299. _value = value;
  300. }
  301. return self;
  302. }
  303. - (BOOL)isEqual:(id)other {
  304. if (other == self) {
  305. return YES;
  306. }
  307. if (![other isKindOfClass:[FSTPatchMutation class]]) {
  308. return NO;
  309. }
  310. FSTPatchMutation *otherMutation = (FSTPatchMutation *)other;
  311. return [self.key isEqual:otherMutation.key] && [self.fieldMask isEqual:otherMutation.fieldMask] &&
  312. [self.value isEqual:otherMutation.value] &&
  313. [self.precondition isEqual:otherMutation.precondition];
  314. }
  315. - (NSUInteger)hash {
  316. NSUInteger result = [self.key hash];
  317. result = 31 * result + [self.precondition hash];
  318. result = 31 * result + [self.fieldMask hash];
  319. result = 31 * result + [self.value hash];
  320. return result;
  321. }
  322. - (NSString *)description {
  323. return [NSString stringWithFormat:@"<FSTPatchMutation key=%s mask=%@ value=%@ precondition=%@>",
  324. self.key.ToString().c_str(), self.fieldMask, self.value,
  325. self.precondition];
  326. }
  327. - (nullable FSTMaybeDocument *)applyTo:(nullable FSTMaybeDocument *)maybeDoc
  328. baseDocument:(nullable FSTMaybeDocument *)baseDoc
  329. localWriteTime:(FIRTimestamp *)localWriteTime
  330. mutationResult:(nullable FSTMutationResult *)mutationResult {
  331. if (mutationResult) {
  332. FSTAssert(!mutationResult.transformResults, @"Transform results received by FSTPatchMutation.");
  333. }
  334. if (![self.precondition isValidForDocument:maybeDoc]) {
  335. return maybeDoc;
  336. }
  337. BOOL hasLocalMutations = (mutationResult == nil);
  338. if (!maybeDoc || [maybeDoc isMemberOfClass:[FSTDeletedDocument class]]) {
  339. // Precondition applied, so create the document if necessary
  340. const DocumentKey &key = maybeDoc ? maybeDoc.key : self.key;
  341. FSTSnapshotVersion *version = maybeDoc ? maybeDoc.version : [FSTSnapshotVersion noVersion];
  342. maybeDoc = [FSTDocument documentWithData:[FSTObjectValue objectValue]
  343. key:key
  344. version:version
  345. hasLocalMutations:hasLocalMutations];
  346. }
  347. FSTAssert([maybeDoc isMemberOfClass:[FSTDocument class]], @"Unknown MaybeDocument type %@",
  348. [maybeDoc class]);
  349. FSTDocument *doc = (FSTDocument *)maybeDoc;
  350. FSTAssert([doc.key isEqual:self.key], @"Can only patch a document with the same key");
  351. FSTObjectValue *newData = [self patchObjectValue:doc.data];
  352. return [FSTDocument documentWithData:newData
  353. key:doc.key
  354. version:doc.version
  355. hasLocalMutations:hasLocalMutations];
  356. }
  357. - (FSTObjectValue *)patchObjectValue:(FSTObjectValue *)objectValue {
  358. FSTObjectValue *result = objectValue;
  359. for (const FieldPath &fieldPath : self.fieldMask.fields) {
  360. FSTFieldValue *newValue = [self.value valueForPath:fieldPath];
  361. if (newValue) {
  362. result = [result objectBySettingValue:newValue forPath:fieldPath];
  363. } else {
  364. result = [result objectByDeletingPath:fieldPath];
  365. }
  366. }
  367. return result;
  368. }
  369. @end
  370. @implementation FSTTransformMutation
  371. - (instancetype)initWithKey:(DocumentKey)key
  372. fieldTransforms:(NSArray<FSTFieldTransform *> *)fieldTransforms {
  373. // NOTE: We set a precondition of exists: true as a safety-check, since we always combine
  374. // FSTTransformMutations with a FSTSetMutation or FSTPatchMutation which (if successful) should
  375. // end up with an existing document.
  376. if (self = [super initWithKey:std::move(key)
  377. precondition:[FSTPrecondition preconditionWithExists:YES]]) {
  378. _fieldTransforms = fieldTransforms;
  379. }
  380. return self;
  381. }
  382. - (BOOL)isEqual:(id)other {
  383. if (other == self) {
  384. return YES;
  385. }
  386. if (![other isKindOfClass:[FSTTransformMutation class]]) {
  387. return NO;
  388. }
  389. FSTTransformMutation *otherMutation = (FSTTransformMutation *)other;
  390. return [self.key isEqual:otherMutation.key] &&
  391. [self.fieldTransforms isEqual:otherMutation.fieldTransforms] &&
  392. [self.precondition isEqual:otherMutation.precondition];
  393. }
  394. - (NSUInteger)hash {
  395. NSUInteger result = [self.key hash];
  396. result = 31 * result + [self.precondition hash];
  397. result = 31 * result + [self.fieldTransforms hash];
  398. return result;
  399. }
  400. - (NSString *)description {
  401. return [NSString stringWithFormat:@"<FSTTransformMutation key=%s transforms=%@ precondition=%@>",
  402. self.key.ToString().c_str(), self.fieldTransforms,
  403. self.precondition];
  404. }
  405. - (nullable FSTMaybeDocument *)applyTo:(nullable FSTMaybeDocument *)maybeDoc
  406. baseDocument:(nullable FSTMaybeDocument *)baseDoc
  407. localWriteTime:(FIRTimestamp *)localWriteTime
  408. mutationResult:(nullable FSTMutationResult *)mutationResult {
  409. if (mutationResult) {
  410. FSTAssert(mutationResult.transformResults,
  411. @"Transform results missing for FSTTransformMutation.");
  412. }
  413. if (![self.precondition isValidForDocument:maybeDoc]) {
  414. return maybeDoc;
  415. }
  416. // We only support transforms with precondition exists, so we can only apply it to an existing
  417. // document
  418. FSTAssert([maybeDoc isMemberOfClass:[FSTDocument class]], @"Unknown MaybeDocument type %@",
  419. [maybeDoc class]);
  420. FSTDocument *doc = (FSTDocument *)maybeDoc;
  421. FSTAssert([doc.key isEqual:self.key], @"Can only patch a document with the same key");
  422. BOOL hasLocalMutations = (mutationResult == nil);
  423. NSArray<FSTFieldValue *> *transformResults =
  424. mutationResult
  425. ? mutationResult.transformResults
  426. : [self localTransformResultsWithBaseDocument:baseDoc writeTime:localWriteTime];
  427. FSTObjectValue *newData = [self transformObject:doc.data transformResults:transformResults];
  428. return [FSTDocument documentWithData:newData
  429. key:doc.key
  430. version:doc.version
  431. hasLocalMutations:hasLocalMutations];
  432. }
  433. /**
  434. * Creates an array of "transform results" (a transform result is a field value representing the
  435. * result of applying a transform) for use when applying an FSTTransformMutation locally.
  436. *
  437. * @param baseDocument The document prior to applying this mutation batch.
  438. * @param localWriteTime The local time of the transform mutation (used to generate
  439. * FSTServerTimestampValues).
  440. * @return The transform results array.
  441. */
  442. - (NSArray<FSTFieldValue *> *)localTransformResultsWithBaseDocument:
  443. (FSTMaybeDocument *_Nullable)baseDocument
  444. writeTime:(FIRTimestamp *)localWriteTime {
  445. NSMutableArray<FSTFieldValue *> *transformResults = [NSMutableArray array];
  446. for (FSTFieldTransform *fieldTransform in self.fieldTransforms) {
  447. if ([fieldTransform.transform isKindOfClass:[FSTServerTimestampTransform class]]) {
  448. FSTFieldValue *previousValue = nil;
  449. if ([baseDocument isMemberOfClass:[FSTDocument class]]) {
  450. previousValue = [((FSTDocument *)baseDocument) fieldForPath:fieldTransform.path];
  451. }
  452. [transformResults
  453. addObject:[FSTServerTimestampValue serverTimestampValueWithLocalWriteTime:localWriteTime
  454. previousValue:previousValue]];
  455. } else {
  456. FSTFail(@"Encountered unknown transform: %@", fieldTransform);
  457. }
  458. }
  459. return transformResults;
  460. }
  461. - (FSTObjectValue *)transformObject:(FSTObjectValue *)objectValue
  462. transformResults:(NSArray<FSTFieldValue *> *)transformResults {
  463. FSTAssert(transformResults.count == self.fieldTransforms.count,
  464. @"Transform results length mismatch.");
  465. for (NSUInteger i = 0; i < self.fieldTransforms.count; i++) {
  466. FSTFieldTransform *fieldTransform = self.fieldTransforms[i];
  467. id<FSTTransformOperation> transform = fieldTransform.transform;
  468. FieldPath fieldPath = fieldTransform.path;
  469. if ([transform isKindOfClass:[FSTServerTimestampTransform class]]) {
  470. objectValue = [objectValue objectBySettingValue:transformResults[i] forPath:fieldPath];
  471. } else {
  472. FSTFail(@"Encountered unknown transform: %@", transform);
  473. }
  474. }
  475. return objectValue;
  476. }
  477. @end
  478. #pragma mark - FSTDeleteMutation
  479. @implementation FSTDeleteMutation
  480. - (BOOL)isEqual:(id)other {
  481. if (other == self) {
  482. return YES;
  483. }
  484. if (![other isKindOfClass:[FSTDeleteMutation class]]) {
  485. return NO;
  486. }
  487. FSTDeleteMutation *otherMutation = (FSTDeleteMutation *)other;
  488. return [self.key isEqual:otherMutation.key] &&
  489. [self.precondition isEqual:otherMutation.precondition];
  490. }
  491. - (NSUInteger)hash {
  492. NSUInteger result = [self.key hash];
  493. result = 31 * result + [self.precondition hash];
  494. return result;
  495. }
  496. - (NSString *)description {
  497. return [NSString stringWithFormat:@"<FSTDeleteMutation key=%s precondition=%@>",
  498. self.key.ToString().c_str(), self.precondition];
  499. }
  500. - (nullable FSTMaybeDocument *)applyTo:(nullable FSTMaybeDocument *)maybeDoc
  501. baseDocument:(nullable FSTMaybeDocument *)baseDoc
  502. localWriteTime:(FIRTimestamp *)localWriteTime
  503. mutationResult:(nullable FSTMutationResult *)mutationResult {
  504. if (mutationResult) {
  505. FSTAssert(!mutationResult.transformResults,
  506. @"Transform results received by FSTDeleteMutation.");
  507. }
  508. if (![self.precondition isValidForDocument:maybeDoc]) {
  509. return maybeDoc;
  510. }
  511. if (maybeDoc) {
  512. FSTAssert([maybeDoc.key isEqual:self.key], @"Can only delete a document with the same key");
  513. }
  514. return [FSTDeletedDocument documentWithKey:self.key version:[FSTSnapshotVersion noVersion]];
  515. }
  516. @end
  517. NS_ASSUME_NONNULL_END