Skip to content

Commit b195de0

Browse files
committed
feat(firestore): add support for FieldPath in update transactions
1 parent 9919bf0 commit b195de0

30 files changed

Lines changed: 276 additions & 320 deletions

File tree

packages/cloud_firestore/cloud_firestore/android/src/main/java/io/flutter/plugins/firebase/firestore/FlutterFirebaseFirestorePlugin.java

Lines changed: 47 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -885,38 +885,62 @@ public void writeBatchCommit(
885885
GeneratedAndroidFirebaseFirestore.PigeonTransactionType type =
886886
Objects.requireNonNull(write.getType());
887887
String path = Objects.requireNonNull(write.getPath());
888-
Map<String, Object> data = write.getData();
889-
890888
DocumentReference documentReference = firestore.document(path);
891889

892890
switch (type) {
893891
case DELETE_TYPE:
894892
batch = batch.delete(documentReference);
895893
break;
896894
case UPDATE:
897-
batch = batch.update(documentReference, Objects.requireNonNull(data));
898-
break;
899-
case SET:
900-
GeneratedAndroidFirebaseFirestore.PigeonDocumentOption options =
901-
Objects.requireNonNull(write.getOption());
902-
903-
if (options.getMerge() != null && options.getMerge()) {
895+
{
896+
Map<Object, Object> rawData = Objects.requireNonNull(write.getData());
897+
Map<FieldPath, Object> updateData = new HashMap<>();
898+
for (Object key : rawData.keySet()) {
899+
if (key instanceof String) {
900+
updateData.put(FieldPath.of((String) key), rawData.get(key));
901+
} else if (key instanceof FieldPath) {
902+
updateData.put((FieldPath) key, rawData.get(key));
903+
}
904+
}
905+
FieldPath firstFieldPath = updateData.keySet().iterator().next();
906+
Object firstObject = updateData.get(firstFieldPath);
907+
ArrayList<Object> flattenData = new ArrayList<>();
908+
for (FieldPath fieldPath : updateData.keySet()) {
909+
if (fieldPath.equals(firstFieldPath)) {
910+
continue;
911+
}
912+
flattenData.add(fieldPath);
913+
flattenData.add(updateData.get(fieldPath));
914+
}
904915
batch =
905-
batch.set(
906-
documentReference, Objects.requireNonNull(data), SetOptions.merge());
907-
} else if (options.getMergeFields() != null) {
908-
List<FieldPath> fieldPathList =
909-
PigeonParser.parseFieldPath(
910-
Objects.requireNonNull(options.getMergeFields()));
911-
batch =
912-
batch.set(
913-
documentReference,
914-
Objects.requireNonNull(data),
915-
SetOptions.mergeFieldPaths(fieldPathList));
916-
} else {
917-
batch = batch.set(documentReference, Objects.requireNonNull(data));
916+
batch.update(
917+
documentReference, firstFieldPath, firstObject, flattenData.toArray());
918+
break;
919+
}
920+
case SET:
921+
{
922+
@SuppressWarnings("unchecked")
923+
Map<String, Object> setData =
924+
(Map<String, Object>) (Map<?, ?>) Objects.requireNonNull(write.getData());
925+
GeneratedAndroidFirebaseFirestore.PigeonDocumentOption options =
926+
Objects.requireNonNull(write.getOption());
927+
928+
if (options.getMerge() != null && options.getMerge()) {
929+
batch = batch.set(documentReference, setData, SetOptions.merge());
930+
} else if (options.getMergeFields() != null) {
931+
List<FieldPath> fieldPathList =
932+
PigeonParser.parseFieldPath(
933+
Objects.requireNonNull(options.getMergeFields()));
934+
batch =
935+
batch.set(
936+
documentReference,
937+
setData,
938+
SetOptions.mergeFieldPaths(fieldPathList));
939+
} else {
940+
batch = batch.set(documentReference, setData);
941+
}
942+
break;
918943
}
919-
break;
920944
}
921945
}
922946

packages/cloud_firestore/cloud_firestore/android/src/main/java/io/flutter/plugins/firebase/firestore/GeneratedAndroidFirebaseFirestore.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1023,13 +1023,13 @@ public void setPath(@NonNull String setterArg) {
10231023
this.path = setterArg;
10241024
}
10251025

1026-
private @Nullable Map<String, Object> data;
1026+
private @Nullable Map<Object, Object> data;
10271027

1028-
public @Nullable Map<String, Object> getData() {
1028+
public @Nullable Map<Object, Object> getData() {
10291029
return data;
10301030
}
10311031

1032-
public void setData(@Nullable Map<String, Object> setterArg) {
1032+
public void setData(@Nullable Map<Object, Object> setterArg) {
10331033
this.data = setterArg;
10341034
}
10351035

@@ -1062,9 +1062,9 @@ public static final class Builder {
10621062
return this;
10631063
}
10641064

1065-
private @Nullable Map<String, Object> data;
1065+
private @Nullable Map<Object, Object> data;
10661066

1067-
public @NonNull Builder setData(@Nullable Map<String, Object> setterArg) {
1067+
public @NonNull Builder setData(@Nullable Map<Object, Object> setterArg) {
10681068
this.data = setterArg;
10691069
return this;
10701070
}
@@ -1103,7 +1103,7 @@ public ArrayList<Object> toList() {
11031103
Object path = list.get(1);
11041104
pigeonResult.setPath((String) path);
11051105
Object data = list.get(2);
1106-
pigeonResult.setData((Map<String, Object>) data);
1106+
pigeonResult.setData((Map<Object, Object>) data);
11071107
Object option = list.get(3);
11081108
pigeonResult.setOption(
11091109
(option == null) ? null : PigeonDocumentOption.fromList((ArrayList<Object>) option));

packages/cloud_firestore/cloud_firestore/android/src/main/java/io/flutter/plugins/firebase/firestore/streamhandler/TransactionStreamHandler.java

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import io.flutter.plugins.firebase.firestore.GeneratedAndroidFirebaseFirestore;
2424
import io.flutter.plugins.firebase.firestore.utils.ExceptionConverter;
2525
import io.flutter.plugins.firebase.firestore.utils.PigeonParser;
26+
import java.util.ArrayList;
2627
import java.util.HashMap;
2728
import java.util.List;
2829
import java.util.Map;
@@ -102,9 +103,30 @@ public void onListen(Object arguments, EventSink events) {
102103
transaction.delete(documentReference);
103104
break;
104105
case UPDATE:
105-
transaction.update(
106-
documentReference, Objects.requireNonNull(command.getData()));
107-
break;
106+
{
107+
Map<Object, Object> rawData = Objects.requireNonNull(command.getData());
108+
Map<FieldPath, Object> updateData = new HashMap<>();
109+
for (Object key : rawData.keySet()) {
110+
if (key instanceof String) {
111+
updateData.put(FieldPath.of((String) key), rawData.get(key));
112+
} else if (key instanceof FieldPath) {
113+
updateData.put((FieldPath) key, rawData.get(key));
114+
}
115+
}
116+
FieldPath firstFieldPath = updateData.keySet().iterator().next();
117+
Object firstObject = updateData.get(firstFieldPath);
118+
ArrayList<Object> flattenData = new ArrayList<>();
119+
for (FieldPath fieldPath : updateData.keySet()) {
120+
if (fieldPath.equals(firstFieldPath)) {
121+
continue;
122+
}
123+
flattenData.add(fieldPath);
124+
flattenData.add(updateData.get(fieldPath));
125+
}
126+
transaction.update(
127+
documentReference, firstFieldPath, firstObject, flattenData.toArray());
128+
break;
129+
}
108130
case SET:
109131
{
110132
GeneratedAndroidFirebaseFirestore.PigeonDocumentOption options =
@@ -121,7 +143,10 @@ public void onListen(Object arguments, EventSink events) {
121143
setOptions = SetOptions.mergeFieldPaths(fieldPathList);
122144
}
123145

124-
Map<String, Object> data = Objects.requireNonNull(command.getData());
146+
@SuppressWarnings("unchecked")
147+
Map<String, Object> data =
148+
(Map<String, Object>)
149+
(Map<?, ?>) Objects.requireNonNull(command.getData());
125150

126151
if (setOptions == null) {
127152
transaction.set(documentReference, data);

packages/cloud_firestore/cloud_firestore/example/integration_test/transaction_e2e.dart

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -326,6 +326,29 @@ void runTransactionTests() {
326326
expect(snapshot.data()!['bar'], equals(2));
327327
expect(snapshot.data()!['foo'], equals('bar'));
328328
});
329+
330+
test('should update a document using FieldPath keys', () async {
331+
DocumentReference<Map<String, dynamic>> documentReference =
332+
await initializeTest('transaction-update-field-path');
333+
334+
await documentReference.set({
335+
'nested': {'field': 'old_value'},
336+
'top': 'value',
337+
});
338+
339+
await firestore.runTransaction((Transaction transaction) async {
340+
await transaction.get(documentReference);
341+
transaction.update(documentReference, {
342+
FieldPath(const ['nested', 'field']): 'new_value',
343+
});
344+
});
345+
346+
DocumentSnapshot<Map<String, dynamic>> snapshot =
347+
await documentReference.get();
348+
expect(snapshot.exists, isTrue);
349+
expect(snapshot.data()!['nested']['field'], equals('new_value'));
350+
expect(snapshot.data()!['top'], equals('value'));
351+
});
329352
});
330353

331354
group('Transaction.set()', () {

packages/cloud_firestore/cloud_firestore/example/integration_test/write_batch_e2e.dart

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,28 @@ void runWriteBatchTests() {
7070
expect(snapshot.exists, false);
7171
});
7272

73+
test('should update a document using FieldPath keys', () async {
74+
CollectionReference<Map<String, dynamic>> collection =
75+
await initializeTest('write-batch-field-path');
76+
DocumentReference<Map<String, dynamic>> doc = collection.doc('doc1');
77+
78+
await doc.set({
79+
'nested': {'field': 'old_value'},
80+
'top': 'value',
81+
});
82+
83+
WriteBatch batch = firestore.batch();
84+
batch.update(doc, {
85+
FieldPath(const ['nested', 'field']): 'new_value',
86+
});
87+
await batch.commit();
88+
89+
DocumentSnapshot<Map<String, dynamic>> snapshot = await doc.get();
90+
expect(snapshot.exists, isTrue);
91+
expect(snapshot.data()!['nested']['field'], equals('new_value'));
92+
expect(snapshot.data()!['top'], equals('value'));
93+
});
94+
7395
test('performs batch operations', () async {
7496
CollectionReference<Map<String, dynamic>> collection =
7597
await initializeTest('write-batch-ops');

packages/cloud_firestore/cloud_firestore/example/ios/Flutter/AppFrameworkInfo.plist

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,5 @@
2020
<string>????</string>
2121
<key>CFBundleVersion</key>
2222
<string>1.0</string>
23-
<key>MinimumOSVersion</key>
24-
<string>12.0</string>
2523
</dict>
2624
</plist>
Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1 @@
1-
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
21
#include "Generated.xcconfig"
Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1 @@
1-
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
21
#include "Generated.xcconfig"

packages/cloud_firestore/cloud_firestore/example/ios/Podfile

Lines changed: 0 additions & 64 deletions
This file was deleted.

0 commit comments

Comments
 (0)