Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(firestore): supports Iterable in queries instead of List #10411

Merged
merged 7 commits into from
Feb 14, 2023
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -367,6 +367,40 @@ void runQueryTests() {
expect(snapshot2.docs[1].id, equals('doc2'));
});

test('ends at string field paths with Iterable', () async {
CollectionReference<Map<String, dynamic>> collection =
await initializeTest('endAt-string');
await Future.wait([
collection.doc('doc1').set({
'foo': 1,
'bar': {'value': 1}
}),
collection.doc('doc2').set({
'foo': 2,
'bar': {'value': 2}
}),
collection.doc('doc3').set({
'foo': 3,
'bar': {'value': 3}
}),
]);

QuerySnapshot<Map<String, dynamic>> snapshot = await collection
.orderBy('bar.value', descending: true)
.endAt({2}).get();

expect(snapshot.docs.length, equals(2));
expect(snapshot.docs[0].id, equals('doc3'));
expect(snapshot.docs[1].id, equals('doc2'));

QuerySnapshot<Map<String, dynamic>> snapshot2 =
await collection.orderBy('foo').endAt([2]).get();

expect(snapshot2.docs.length, equals(2));
expect(snapshot2.docs[0].id, equals('doc1'));
expect(snapshot2.docs[1].id, equals('doc2'));
});

test('ends at field paths', () async {
CollectionReference<Map<String, dynamic>> collection =
await initializeTest('endAt-field-path');
Expand Down Expand Up @@ -497,6 +531,40 @@ void runQueryTests() {
expect(snapshot2.docs[1].id, equals('doc3'));
});

test('starts at string field paths with Iterable', () async {
CollectionReference<Map<String, dynamic>> collection =
await initializeTest('startAt-string');
await Future.wait([
collection.doc('doc1').set({
'foo': 1,
'bar': {'value': 1}
}),
collection.doc('doc2').set({
'foo': 2,
'bar': {'value': 2}
}),
collection.doc('doc3').set({
'foo': 3,
'bar': {'value': 3}
}),
]);

QuerySnapshot<Map<String, dynamic>> snapshot = await collection
.orderBy('bar.value', descending: true)
.startAt([2]).get();

expect(snapshot.docs.length, equals(2));
expect(snapshot.docs[0].id, equals('doc2'));
expect(snapshot.docs[1].id, equals('doc1'));

QuerySnapshot<Map<String, dynamic>> snapshot2 =
await collection.orderBy('foo').startAt({2}).get();

expect(snapshot2.docs.length, equals(2));
expect(snapshot2.docs[0].id, equals('doc2'));
expect(snapshot2.docs[1].id, equals('doc3'));
});

test('starts at field paths', () async {
CollectionReference<Map<String, dynamic>> collection =
await initializeTest('startAt-field-path');
Expand Down Expand Up @@ -627,6 +695,40 @@ void runQueryTests() {
expect(snapshot2.docs[1].id, equals('doc2'));
});

test('ends before string field paths with Iterable', () async {
CollectionReference<Map<String, dynamic>> collection =
await initializeTest('endBefore-string');
await Future.wait([
collection.doc('doc1').set({
'foo': 1,
'bar': {'value': 1}
}),
collection.doc('doc2').set({
'foo': 2,
'bar': {'value': 2}
}),
collection.doc('doc3').set({
'foo': 3,
'bar': {'value': 3}
}),
]);

QuerySnapshot<Map<String, dynamic>> snapshot = await collection
.orderBy('bar.value', descending: true)
.endBefore({1}).get();

expect(snapshot.docs.length, equals(2));
expect(snapshot.docs[0].id, equals('doc3'));
expect(snapshot.docs[1].id, equals('doc2'));

QuerySnapshot<Map<String, dynamic>> snapshot2 =
await collection.orderBy('foo').endBefore([3]).get();

expect(snapshot2.docs.length, equals(2));
expect(snapshot2.docs[0].id, equals('doc1'));
expect(snapshot2.docs[1].id, equals('doc2'));
});

test('ends before field paths', () async {
CollectionReference<Map<String, dynamic>> collection =
await initializeTest('endBefore-field-path');
Expand Down Expand Up @@ -1256,7 +1358,41 @@ void runQueryTests() {
});
});

test('returns with in filter', () async {
test('returns with in filter using Iterable', () async {
CollectionReference<Map<String, dynamic>> collection =
await initializeTest('where-in-iterable');

await Future.wait([
collection.doc('doc1').set({
'status': 'Ordered',
}),
collection.doc('doc2').set({
'status': 'Ready to Ship',
}),
collection.doc('doc3').set({
'status': 'Ready to Ship',
}),
collection.doc('doc4').set({
'status': 'Incomplete',
}),
]);

QuerySnapshot<Map<String, dynamic>> snapshot = await collection
.where(
'status',
// To force the list to be an iterable
whereIn: ['Ready to Ship', 'Ordered'].map((e) => e),
)
.get();

expect(snapshot.docs.length, equals(3));
snapshot.docs.forEach((doc) {
String status = doc.data()['status'];
expect(status == 'Ready to Ship' || status == 'Ordered', isTrue);
});
});

test('returns with in filter using Set', () async {
CollectionReference<Map<String, dynamic>> collection =
await initializeTest('where-in');

Expand All @@ -1276,7 +1412,7 @@ void runQueryTests() {
]);

QuerySnapshot<Map<String, dynamic>> snapshot = await collection
.where('status', whereIn: ['Ready to Ship', 'Ordered']).get();
.where('status', whereIn: {'Ready to Ship', 'Ordered'}).get();

expect(snapshot.docs.length, equals(3));
snapshot.docs.forEach((doc) {
Expand Down Expand Up @@ -1314,6 +1450,35 @@ void runQueryTests() {
});
});

test('returns with not-in filter with Iterable', () async {
CollectionReference<Map<String, dynamic>> collection =
await initializeTest('where-not-in');

await Future.wait([
collection.doc('doc1').set({
'status': 'Ordered',
}),
collection.doc('doc2').set({
'status': 'Ready to Ship',
}),
collection.doc('doc3').set({
'status': 'Ready to Ship',
}),
collection.doc('doc4').set({
'status': 'Incomplete',
}),
]);

QuerySnapshot<Map<String, dynamic>> snapshot = await collection
.where('status', whereNotIn: {'Ready to Ship', 'Ordered'}).get();

expect(snapshot.docs.length, equals(1));
snapshot.docs.forEach((doc) {
String status = doc.data()['status'];
expect(status == 'Incomplete', isTrue);
});
});

test('returns with array-contains-any filter', () async {
CollectionReference<Map<String, dynamic>> collection =
await initializeTest('where-array-contains-any');
Expand Down Expand Up @@ -1342,6 +1507,34 @@ void runQueryTests() {
expect(snapshot.docs.length, equals(3));
});

test('returns with array-contains-any filter using Set', () async {
CollectionReference<Map<String, dynamic>> collection =
await initializeTest('where-array-contains-any');

await Future.wait([
collection.doc('doc1').set({
'category': ['Appliances', 'Housewares', 'Cooking'],
}),
collection.doc('doc2').set({
'category': ['Appliances', 'Electronics', 'Nursery'],
}),
collection.doc('doc3').set({
'category': ['Audio/Video', 'Electronics'],
}),
collection.doc('doc4').set({
'category': ['Beauty'],
}),
]);

QuerySnapshot<Map<String, dynamic>> snapshot = await collection.where(
'category',
arrayContainsAny: {'Appliances', 'Electronics'},
).get();

// 2nd record should only be returned once
expect(snapshot.docs.length, equals(3));
});

// When documents have a key with a '.' in them, only a [FieldPath]
// can access the value, rather than a raw string
test('returns where FieldPath', () async {
Expand Down Expand Up @@ -1643,6 +1836,27 @@ void runQueryTests() {
);
});

test('endAt with Iterable', () async {
final collection = await initializeTest('foo');

final converted = collection.withConverter<int>(
fromFirestore: (snapshots, _) => snapshots.data()!['value']! as int,
toFirestore: (value, _) => {'value': value},
);

await converted.add(1);
await converted.add(2);
await converted.add(3);

expect(
await converted.orderBy('value').endAt({2}).get().then((d) => d.docs),
[
isA<DocumentSnapshot<int>>().having((e) => e.data(), 'data', 1),
isA<DocumentSnapshot<int>>().having((e) => e.data(), 'data', 2),
],
);
});

test(
'endAtDocument',
() async {
Expand Down Expand Up @@ -1694,6 +1908,28 @@ void runQueryTests() {
);
});

test('endBefore with Iterable', () async {
final collection = await initializeTest('foo');

final converted = collection.withConverter<int>(
fromFirestore: (snapshots, _) => snapshots.data()!['value']! as int,
toFirestore: (value, _) => {'value': value},
);

await converted.add(1);
await converted.add(2);
await converted.add(3);

expect(
await converted
.orderBy('value')
.endBefore({2})
.get()
.then((d) => d.docs),
[isA<DocumentSnapshot<int>>().having((e) => e.data(), 'data', 1)],
);
});

test(
'endBeforeDocument',
() async {
Expand Down Expand Up @@ -1749,6 +1985,35 @@ void runQueryTests() {
timeout: const Timeout.factor(3),
);

test(
'startAt with Iterable',
() async {
final collection = await initializeTest('foo');

final converted = collection.withConverter<int>(
fromFirestore: (snapshots, _) => snapshots.data()!['value']! as int,
toFirestore: (value, _) => {'value': value},
);

await converted.add(1);
await converted.add(2);
await converted.add(3);

expect(
await converted
.orderBy('value')
.startAt({2})
.get()
.then((d) => d.docs),
[
isA<DocumentSnapshot<int>>().having((e) => e.data(), 'data', 2),
isA<DocumentSnapshot<int>>().having((e) => e.data(), 'data', 3),
],
);
},
timeout: const Timeout.factor(3),
);

test(
'startAtDocument',
() async {
Expand Down Expand Up @@ -1804,6 +2069,32 @@ void runQueryTests() {
timeout: const Timeout.factor(3),
);

test(
'startAfter with Iterable',
() async {
final collection = await initializeTest('foo');

final converted = collection.withConverter<int>(
fromFirestore: (snapshots, _) => snapshots.data()!['value']! as int,
toFirestore: (value, _) => {'value': value},
);

await converted.add(1);
await converted.add(2);
await converted.add(3);

expect(
await converted
.orderBy('value')
.startAfter({2})
.get()
.then((d) => d.docs),
[isA<DocumentSnapshot<int>>().having((e) => e.data(), 'data', 3)],
);
},
timeout: const Timeout.factor(3),
);

test(
'startAfterDocument',
() async {
Expand Down
Loading