FSTMutation.mm 21 KB

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