Skip to content

Commit bf6eda1

Browse files
feat(cloud_firestore): experimental setIndexConfiguration() API (#9928)
1 parent 3467483 commit bf6eda1

File tree

12 files changed

+404
-113
lines changed

12 files changed

+404
-113
lines changed

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

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -511,6 +511,29 @@ private Task<Map<String, Object>> aggregateQuery(Map<String, Object> arguments)
511511
return taskCompletionSource.getTask();
512512
}
513513

514+
private Task<Void> setIndexConfiguration(Map<String, Object> arguments) {
515+
TaskCompletionSource<Void> taskCompletionSource = new TaskCompletionSource<>();
516+
517+
cachedThreadPool.execute(
518+
() -> {
519+
try {
520+
FirebaseFirestore firestore =
521+
(FirebaseFirestore) Objects.requireNonNull(arguments.get("firestore"));
522+
523+
Tasks.await(
524+
firestore.setIndexConfiguration(
525+
(String) Objects.requireNonNull(arguments.get("indexConfiguration"))));
526+
527+
taskCompletionSource.setResult(null);
528+
529+
} catch (Exception e) {
530+
taskCompletionSource.setException(e);
531+
}
532+
});
533+
534+
return taskCompletionSource.getTask();
535+
}
536+
514537
@Override
515538
public void onMethodCall(MethodCall call, @NonNull final MethodChannel.Result result) {
516539
Task<?> methodCallTask;
@@ -592,6 +615,9 @@ public void onMethodCall(MethodCall call, @NonNull final MethodChannel.Result re
592615
case "AggregateQuery#count":
593616
methodCallTask = aggregateQuery(call.arguments());
594617
break;
618+
case "Firestore#setIndexConfiguration":
619+
methodCallTask = setIndexConfiguration(call.arguments());
620+
break;
595621
default:
596622
result.notImplemented();
597623
return;

packages/cloud_firestore/cloud_firestore/example/integration_test/instance_e2e.dart

Lines changed: 193 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -18,98 +18,106 @@ void runInstanceTests() {
1818
firestore = FirebaseFirestore.instance;
1919
});
2020

21-
test('snapshotsInSync()', () async {
22-
DocumentReference<Map<String, dynamic>> documentReference =
23-
firestore.doc('flutter-tests/insync');
24-
25-
// Ensure deleted
26-
await documentReference.delete();
27-
28-
StreamController controller = StreamController();
29-
StreamSubscription insync;
30-
StreamSubscription snapshots;
31-
32-
int inSyncCount = 0;
33-
34-
insync = firestore.snapshotsInSync().listen((_) {
35-
controller.add('insync=$inSyncCount');
36-
inSyncCount++;
37-
});
38-
39-
snapshots = documentReference.snapshots().listen((ds) {
40-
controller.add('snapshot-exists=${ds.exists}');
41-
});
42-
43-
// Allow the snapshots to trigger...
44-
await Future.delayed(const Duration(seconds: 1));
45-
46-
await documentReference.set({'foo': 'bar'});
47-
48-
await expectLater(
49-
controller.stream,
50-
emitsInOrder([
51-
'insync=0', // No other snapshots
52-
'snapshot-exists=false',
53-
'insync=1',
54-
'snapshot-exists=true',
55-
'insync=2',
56-
]),
57-
);
58-
59-
await controller.close();
60-
await insync.cancel();
61-
await snapshots.cancel();
62-
});
63-
64-
test('enableNetwork()', () async {
65-
if (kIsWeb) return;
66-
67-
// Write some data while online
68-
await firestore.enableNetwork();
69-
DocumentReference<Map<String, dynamic>> documentReference =
70-
firestore.doc('flutter-tests/enable-network');
71-
await documentReference.set({'foo': 'bar'});
72-
73-
// Disable the network
74-
await firestore.disableNetwork();
75-
76-
StreamController controller = StreamController();
77-
78-
// Set some data while offline
79-
// ignore: unawaited_futures
80-
documentReference.set({'foo': 'baz'}).then((_) async {
81-
// Only when back online will this trigger
82-
controller.add(true);
83-
});
21+
test(
22+
'snapshotsInSync()',
23+
() async {
24+
DocumentReference<Map<String, dynamic>> documentReference =
25+
firestore.doc('flutter-tests/insync');
26+
27+
// Ensure deleted
28+
await documentReference.delete();
29+
30+
StreamController controller = StreamController();
31+
StreamSubscription insync;
32+
StreamSubscription snapshots;
33+
34+
int inSyncCount = 0;
35+
36+
insync = firestore.snapshotsInSync().listen((_) {
37+
controller.add('insync=$inSyncCount');
38+
inSyncCount++;
39+
});
40+
41+
snapshots = documentReference.snapshots().listen((ds) {
42+
controller.add('snapshot-exists=${ds.exists}');
43+
});
44+
45+
// Allow the snapshots to trigger...
46+
await Future.delayed(const Duration(seconds: 1));
47+
48+
await documentReference.set({'foo': 'bar'});
49+
50+
await expectLater(
51+
controller.stream,
52+
emitsInOrder([
53+
'insync=0', // No other snapshots
54+
'snapshot-exists=false',
55+
'insync=1',
56+
'snapshot-exists=true',
57+
'insync=2',
58+
]),
59+
);
60+
61+
await controller.close();
62+
await insync.cancel();
63+
await snapshots.cancel();
64+
},
65+
skip: kIsWeb,
66+
);
8467

85-
// Go back online
86-
await firestore.enableNetwork();
68+
test(
69+
'enableNetwork()',
70+
() async {
71+
// Write some data while online
72+
await firestore.enableNetwork();
73+
DocumentReference<Map<String, dynamic>> documentReference =
74+
firestore.doc('flutter-tests/enable-network');
75+
await documentReference.set({'foo': 'bar'});
8776

88-
await expectLater(controller.stream, emits(true));
89-
await controller.close();
90-
});
77+
// Disable the network
78+
await firestore.disableNetwork();
9179

92-
test('disableNetwork()', () async {
93-
if (kIsWeb) return;
80+
StreamController controller = StreamController();
9481

95-
// Write some data while online
96-
await firestore.enableNetwork();
97-
DocumentReference<Map<String, dynamic>> documentReference =
98-
firestore.doc('flutter-tests/disable-network');
99-
await documentReference.set({'foo': 'bar'});
82+
// Set some data while offline
83+
// ignore: unawaited_futures
84+
documentReference.set({'foo': 'baz'}).then((_) async {
85+
// Only when back online will this trigger
86+
controller.add(true);
87+
});
10088

101-
// Disable the network
102-
await firestore.disableNetwork();
89+
// Go back online
90+
await firestore.enableNetwork();
10391

104-
// Get data from cache
105-
DocumentSnapshot<Map<String, dynamic>> documentSnapshot =
106-
await documentReference.get();
107-
expect(documentSnapshot.metadata.isFromCache, isTrue);
108-
expect(documentSnapshot.data()!['foo'], equals('bar'));
92+
await expectLater(controller.stream, emits(true));
93+
await controller.close();
94+
},
95+
skip: kIsWeb,
96+
);
10997

110-
// Go back online once test complete
111-
await firestore.enableNetwork();
112-
});
98+
test(
99+
'disableNetwork()',
100+
() async {
101+
// Write some data while online
102+
await firestore.enableNetwork();
103+
DocumentReference<Map<String, dynamic>> documentReference =
104+
firestore.doc('flutter-tests/disable-network');
105+
await documentReference.set({'foo': 'bar'});
106+
107+
// Disable the network
108+
await firestore.disableNetwork();
109+
110+
// Get data from cache
111+
DocumentSnapshot<Map<String, dynamic>> documentSnapshot =
112+
await documentReference.get();
113+
expect(documentSnapshot.metadata.isFromCache, isTrue);
114+
expect(documentSnapshot.data()!['foo'], equals('bar'));
115+
116+
// Go back online once test complete
117+
await firestore.enableNetwork();
118+
},
119+
skip: kIsWeb,
120+
);
113121

114122
test(
115123
'waitForPendingWrites()',
@@ -119,24 +127,108 @@ void runInstanceTests() {
119127
skip: kIsWeb,
120128
);
121129

122-
test('terminate() / clearPersistence()', () async {
123-
// Since the firestore instance has already been used,
124-
// calling `clearPersistence` will throw a native error.
125-
// We first check it does throw as expected, then terminate
126-
// the instance, and then check whether clearing succeeds.
127-
try {
130+
test(
131+
'terminate() / clearPersistence()',
132+
() async {
133+
// Since the firestore instance has already been used,
134+
// calling `clearPersistence` will throw a native error.
135+
// We first check it does throw as expected, then terminate
136+
// the instance, and then check whether clearing succeeds.
137+
try {
138+
await firestore.clearPersistence();
139+
fail('Should have thrown');
140+
} on FirebaseException catch (e) {
141+
expect(e.code, equals('failed-precondition'));
142+
} catch (e) {
143+
fail('$e');
144+
}
145+
146+
await firestore.terminate();
128147
await firestore.clearPersistence();
129-
fail('Should have thrown');
130-
} on FirebaseException catch (e) {
131-
expect(e.code, equals('failed-precondition'));
132-
} catch (e) {
133-
fail('$e');
134-
}
135-
136-
await firestore.terminate();
137-
await firestore.clearPersistence();
148+
},
149+
skip: kIsWeb,
150+
);
151+
152+
test('setIndexConfiguration()', () async {
153+
Index index1 = Index(
154+
collectionGroup: 'bar',
155+
queryScope: QueryScope.collectionGroup,
156+
fields: [
157+
IndexField(
158+
fieldPath: 'fieldPath',
159+
order: Order.ascending,
160+
arrayConfig: ArrayConfig.contains,
161+
)
162+
],
163+
);
164+
165+
Index index2 = Index(
166+
collectionGroup: 'baz',
167+
queryScope: QueryScope.collection,
168+
fields: [
169+
IndexField(
170+
fieldPath: 'foo',
171+
arrayConfig: ArrayConfig.contains,
172+
),
173+
IndexField(
174+
fieldPath: 'bar',
175+
order: Order.descending,
176+
arrayConfig: ArrayConfig.contains,
177+
),
178+
IndexField(
179+
fieldPath: 'baz',
180+
order: Order.descending,
181+
arrayConfig: ArrayConfig.contains,
182+
),
183+
],
184+
);
185+
186+
FieldOverrides fieldOverride1 = FieldOverrides(
187+
fieldPath: 'fieldPath',
188+
indexes: [
189+
FieldOverrideIndex(
190+
queryScope: 'foo',
191+
order: Order.ascending,
192+
arrayConfig: ArrayConfig.contains,
193+
),
194+
FieldOverrideIndex(
195+
queryScope: 'bar',
196+
order: Order.descending,
197+
arrayConfig: ArrayConfig.contains,
198+
),
199+
FieldOverrideIndex(
200+
queryScope: 'baz',
201+
order: Order.descending,
202+
),
203+
],
204+
collectionGroup: 'bar',
205+
);
206+
FieldOverrides fieldOverride2 = FieldOverrides(
207+
fieldPath: 'anotherField',
208+
indexes: [
209+
FieldOverrideIndex(
210+
queryScope: 'foo',
211+
order: Order.ascending,
212+
arrayConfig: ArrayConfig.contains,
213+
),
214+
FieldOverrideIndex(
215+
queryScope: 'bar',
216+
order: Order.descending,
217+
arrayConfig: ArrayConfig.contains,
218+
),
219+
FieldOverrideIndex(
220+
queryScope: 'baz',
221+
order: Order.descending,
222+
),
223+
],
224+
collectionGroup: 'collectiongroup',
225+
);
226+
227+
await firestore.setIndexConfiguration(
228+
indexes: [index1, index2],
229+
fieldOverrides: [fieldOverride1, fieldOverride2],
230+
);
138231
});
139232
},
140-
skip: kIsWeb,
141233
);
142234
}

packages/cloud_firestore/cloud_firestore/ios/Classes/FLTFirebaseFirestorePlugin.m

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,8 @@ - (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)flutter
227227
[self setupLoadBundleListener:call.arguments withMethodCallResult:methodCallResult];
228228
} else if ([@"AggregateQuery#count" isEqualToString:call.method]) {
229229
[self aggregateQuery:call.arguments withMethodCallResult:methodCallResult];
230+
} else if ([@"Firestore#setIndexConfiguration" isEqualToString:call.method]) {
231+
[self setIndexConfiguration:call.arguments withMethodCallResult:methodCallResult];
230232
} else {
231233
methodCallResult.success(FlutterMethodNotImplemented);
232234
}
@@ -256,6 +258,21 @@ - (NSString *_Nonnull)flutterChannelName {
256258

257259
#pragma mark - Firestore API
258260

261+
- (void)setIndexConfiguration:(id)arguments
262+
withMethodCallResult:(FLTFirebaseMethodCallResult *)result {
263+
FIRFirestore *firestore = arguments[@"firestore"];
264+
NSString *indexConfiguration = arguments[@"indexConfiguration"];
265+
266+
[firestore setIndexConfigurationFromJSON:indexConfiguration
267+
completion:^(NSError *_Nullable error) {
268+
if (error != nil) {
269+
result.error(nil, nil, nil, error);
270+
} else {
271+
result.success(nil);
272+
}
273+
}];
274+
}
275+
259276
- (void)setupSnapshotsInSyncListener:(id)arguments
260277
withMethodCallResult:(FLTFirebaseMethodCallResult *)result {
261278
result.success([self

0 commit comments

Comments
 (0)