Skip to content

Commit d40e7f1

Browse files
author
Brian Chen
authored
Replace usage of transform with updateTransforms (#2262)
1 parent ee74cd0 commit d40e7f1

23 files changed

+593
-517
lines changed

firebase-firestore/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@
33
This more closely aligns the SDK with the Firestore backend, which has always
44
accepted null and NaN for all operators, even though this isn't necessarily
55
useful.
6+
- [changed] A write to a document that contains FieldValue transforms is no
7+
longer split up into two separate operations. This reduces the number of
8+
writes the backend performs and allows each WriteBatch to hold 500 writes
9+
regardless of how many FieldValue transformations are attached.
610

711
# (22.0.0)
812
- [changed] Removed the deprecated `timestampsInSnapshotsEnabled` setting.

firebase-firestore/src/main/java/com/google/firebase/firestore/DocumentReference.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
import com.google.firebase.firestore.util.Assert;
4343
import com.google.firebase.firestore.util.Executors;
4444
import com.google.firebase.firestore.util.Util;
45+
import java.util.Collections;
4546
import java.util.Map;
4647
import java.util.concurrent.ExecutionException;
4748
import java.util.concurrent.Executor;
@@ -166,7 +167,7 @@ public Task<Void> set(@NonNull Object data, @NonNull SetOptions options) {
166167
: firestore.getUserDataReader().parseSetData(data);
167168
return firestore
168169
.getClient()
169-
.write(parsed.toMutationList(key, Precondition.NONE))
170+
.write(Collections.singletonList(parsed.toMutation(key, Precondition.NONE)))
170171
.continueWith(Executors.DIRECT_EXECUTOR, voidErrorTransformer());
171172
}
172173

@@ -230,7 +231,7 @@ public Task<Void> update(
230231
private Task<Void> update(@NonNull ParsedUpdateData parsedData) {
231232
return firestore
232233
.getClient()
233-
.write(parsedData.toMutationList(key, Precondition.exists(true)))
234+
.write(Collections.singletonList(parsedData.toMutation(key, Precondition.exists(true))))
234235
.continueWith(Executors.DIRECT_EXECUTOR, voidErrorTransformer());
235236
}
236237

firebase-firestore/src/main/java/com/google/firebase/firestore/UserDataReader.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,7 @@ public Value parseQueryValue(Object input, boolean allowArrays) {
208208
}
209209

210210
/** Converts a POJO to native types and then parses it into model types. */
211-
private Value convertAndParseFieldData(Object input, ParseContext context) {
211+
public Value convertAndParseFieldData(Object input, ParseContext context) {
212212
Object converted = CustomClassMapper.convertToPlainJavaTypes(input);
213213
return parseData(converted, context);
214214
}

firebase-firestore/src/main/java/com/google/firebase/firestore/WriteBatch.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ public WriteBatch set(
8888
options.isMerge()
8989
? firestore.getUserDataReader().parseMergeData(data, options.getFieldMask())
9090
: firestore.getUserDataReader().parseSetData(data);
91-
mutations.addAll(parsed.toMutationList(documentRef.getKey(), Precondition.NONE));
91+
mutations.add(parsed.toMutation(documentRef.getKey(), Precondition.NONE));
9292
return this;
9393
}
9494

@@ -163,7 +163,7 @@ private WriteBatch update(
163163
@NonNull DocumentReference documentRef, @NonNull ParsedUpdateData updateData) {
164164
firestore.validateReference(documentRef);
165165
verifyNotCommitted();
166-
mutations.addAll(updateData.toMutationList(documentRef.getKey(), Precondition.exists(true)));
166+
mutations.add(updateData.toMutation(documentRef.getKey(), Precondition.exists(true)));
167167
return this;
168168
}
169169

firebase-firestore/src/main/java/com/google/firebase/firestore/core/Transaction.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ public Task<List<MaybeDocument>> lookup(List<DocumentKey> keys) {
103103

104104
/** Stores a set mutation for the given key and value, to be committed when commit() is called. */
105105
public void set(DocumentKey key, ParsedSetData data) {
106-
write(data.toMutationList(key, precondition(key)));
106+
write(Collections.singletonList(data.toMutation(key, precondition(key))));
107107
writtenDocs.add(key);
108108
}
109109

@@ -113,7 +113,7 @@ public void set(DocumentKey key, ParsedSetData data) {
113113
*/
114114
public void update(DocumentKey key, ParsedUpdateData data) {
115115
try {
116-
write(data.toMutationList(key, preconditionForUpdate(key)));
116+
write(Collections.singletonList(data.toMutation(key, preconditionForUpdate(key))));
117117
} catch (FirebaseFirestoreException e) {
118118
lastWriteError = e;
119119
}

firebase-firestore/src/main/java/com/google/firebase/firestore/core/UserData.java

Lines changed: 27 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@
2626
import com.google.firebase.firestore.model.mutation.PatchMutation;
2727
import com.google.firebase.firestore.model.mutation.Precondition;
2828
import com.google.firebase.firestore.model.mutation.SetMutation;
29-
import com.google.firebase.firestore.model.mutation.TransformMutation;
3029
import com.google.firebase.firestore.model.mutation.TransformOperation;
3130
import com.google.firebase.firestore.util.Assert;
3231
import java.util.ArrayList;
@@ -335,23 +334,32 @@ public static class ParsedSetData {
335334
this.fieldTransforms = fieldTransforms;
336335
}
337336

338-
public List<Mutation> toMutationList(DocumentKey key, Precondition precondition) {
339-
ArrayList<Mutation> mutations = new ArrayList<>();
337+
public ObjectValue getData() {
338+
return data;
339+
}
340+
341+
@Nullable
342+
public FieldMask getFieldMask() {
343+
return fieldMask;
344+
}
345+
346+
public List<FieldTransform> getFieldTransforms() {
347+
return fieldTransforms;
348+
}
349+
350+
public Mutation toMutation(DocumentKey key, Precondition precondition) {
340351
if (fieldMask != null) {
341-
mutations.add(new PatchMutation(key, data, fieldMask, precondition));
352+
return new PatchMutation(key, data, fieldMask, precondition, fieldTransforms);
342353
} else {
343-
mutations.add(new SetMutation(key, data, precondition));
344-
}
345-
if (!fieldTransforms.isEmpty()) {
346-
mutations.add(new TransformMutation(key, fieldTransforms));
354+
return new SetMutation(key, data, precondition, fieldTransforms);
347355
}
348-
return mutations;
349356
}
350357
}
351358

352359
/** The result of parsing "update" data (i.e. for an updateData call). */
353360
public static class ParsedUpdateData {
354361
private final ObjectValue data;
362+
// The fieldMask does not include document transforms.
355363
private final FieldMask fieldMask;
356364
private final List<FieldTransform> fieldTransforms;
357365

@@ -361,17 +369,20 @@ public static class ParsedUpdateData {
361369
this.fieldTransforms = fieldTransforms;
362370
}
363371

372+
public ObjectValue getData() {
373+
return data;
374+
}
375+
376+
public FieldMask getFieldMask() {
377+
return fieldMask;
378+
}
379+
364380
public List<FieldTransform> getFieldTransforms() {
365381
return fieldTransforms;
366382
}
367383

368-
public List<Mutation> toMutationList(DocumentKey key, Precondition precondition) {
369-
ArrayList<Mutation> mutations = new ArrayList<>();
370-
mutations.add(new PatchMutation(key, data, fieldMask, precondition));
371-
if (!fieldTransforms.isEmpty()) {
372-
mutations.add(new TransformMutation(key, fieldTransforms));
373-
}
374-
return mutations;
384+
public Mutation toMutation(DocumentKey key, Precondition precondition) {
385+
return new PatchMutation(key, data, fieldMask, precondition, fieldTransforms);
375386
}
376387
}
377388
}

firebase-firestore/src/main/java/com/google/firebase/firestore/local/LocalSerializer.java

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,12 @@
2929
import com.google.firebase.firestore.model.mutation.Mutation;
3030
import com.google.firebase.firestore.model.mutation.MutationBatch;
3131
import com.google.firebase.firestore.remote.RemoteSerializer;
32+
import com.google.firestore.v1.DocumentTransform.FieldTransform;
33+
import com.google.firestore.v1.Write;
34+
import com.google.firestore.v1.Write.Builder;
3235
import com.google.protobuf.ByteString;
3336
import java.util.ArrayList;
37+
import java.util.Collections;
3438
import java.util.List;
3539

3640
/** Serializer for values stored in the LocalStore. */
@@ -171,11 +175,35 @@ MutationBatch decodeMutationBatch(com.google.firebase.firestore.proto.WriteBatch
171175
for (int i = 0; i < baseMutationsCount; i++) {
172176
baseMutations.add(rpcSerializer.decodeMutation(batch.getBaseWrites(i)));
173177
}
174-
int mutationsCount = batch.getWritesCount();
175-
List<Mutation> mutations = new ArrayList<>(mutationsCount);
176-
for (int i = 0; i < mutationsCount; i++) {
177-
mutations.add(rpcSerializer.decodeMutation(batch.getWrites(i)));
178+
179+
List<Mutation> mutations = new ArrayList<>(batch.getWritesCount());
180+
181+
// Squash old transform mutations into existing patch or set mutations. The replacement of
182+
// representing `transforms` with `update_transforms` on the SDK means that old `transform`
183+
// mutations stored in IndexedDB need to be updated to `update_transforms`.
184+
// TODO(b/174608374): Remove this code once we perform a schema migration.
185+
for (int i = batch.getWritesCount() - 1; i >= 0; --i) {
186+
Write mutation = batch.getWrites(i);
187+
if (mutation.hasTransform()) {
188+
hardAssert(
189+
i >= 1 && !batch.getWrites(i - 1).hasTransform() && batch.getWrites(i - 1).hasUpdate(),
190+
"TransformMutation should be preceded by a patch or set mutation");
191+
Write mutationToJoin = batch.getWrites(i - 1);
192+
Builder newMutationBuilder = Write.newBuilder(mutationToJoin);
193+
for (FieldTransform fieldTransform : mutation.getTransform().getFieldTransformsList()) {
194+
newMutationBuilder.addUpdateTransforms(fieldTransform);
195+
}
196+
mutations.add(rpcSerializer.decodeMutation(newMutationBuilder.build()));
197+
--i;
198+
} else {
199+
mutations.add(rpcSerializer.decodeMutation(mutation));
200+
}
178201
}
202+
203+
// Reverse the mutations to preserve the original ordering since the above for-loop iterates in
204+
// reverse order. We use reverse() instead of prepending the elements into the mutations array
205+
// since prepending to a List is O(n).
206+
Collections.reverse(mutations);
179207
return new MutationBatch(batchId, localWriteTime, baseMutations, mutations);
180208
}
181209

firebase-firestore/src/main/java/com/google/firebase/firestore/local/LocalStore.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,7 @@ public LocalWriteResult writeLocally(List<Mutation> mutations) {
219219
List<Mutation> baseMutations = new ArrayList<>();
220220
for (Mutation mutation : mutations) {
221221
ObjectValue baseValue =
222-
mutation.extractBaseValue(existingDocuments.get(mutation.getKey()));
222+
mutation.extractTransformBaseValue(existingDocuments.get(mutation.getKey()));
223223
if (baseValue != null) {
224224
// NOTE: The base state should only be applied if there's some existing
225225
// document to override, so use a Precondition of exists=true

firebase-firestore/src/main/java/com/google/firebase/firestore/model/ServerTimestamps.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,8 @@
2828
* `__previous_value__` and `__local_write_time__` fields respectively.
2929
*
3030
* <p>Notes:
31-
* <li>ServerTimestamp Values are created as the result of applying a TransformMutation (see
32-
* TransformMutation.applyTo()). They can only exist in the local view of a document. Therefore
33-
* they do not need to be parsed or serialized.
31+
* <li>ServerTimestamp Values are created as the result of applying a transform. They can only exist
32+
* in the local view of a document. Therefore they do not need to be parsed or serialized.
3433
* <li>When evaluated locally (e.g. via DocumentSnapshot data), they evaluate to null.
3534
* <li>They sort after all Timestamp Values. With respect to other ServerTimestamp Values, they sort
3635
* by their localWriteTime.

firebase-firestore/src/main/java/com/google/firebase/firestore/model/mutation/DeleteMutation.java

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121
import com.google.firebase.firestore.model.DocumentKey;
2222
import com.google.firebase.firestore.model.MaybeDocument;
2323
import com.google.firebase.firestore.model.NoDocument;
24-
import com.google.firebase.firestore.model.ObjectValue;
2524
import com.google.firebase.firestore.model.SnapshotVersion;
2625

2726
/** Represents a Delete operation */
@@ -83,10 +82,4 @@ public MaybeDocument applyToLocalView(
8382

8483
return new NoDocument(getKey(), SnapshotVersion.NONE, /*hasCommittedMutations=*/ false);
8584
}
86-
87-
@Nullable
88-
@Override
89-
public ObjectValue extractBaseValue(@Nullable MaybeDocument maybeDoc) {
90-
return null;
91-
}
9285
}

0 commit comments

Comments
 (0)