Add `pipe queries`, which lists every Firestore query a project can issue: scope, collection, path pattern, filters with operators and value types, composites, orderBy, limit, cursors, realtime, aggregation, in-loop, plus a confidence and the reasons it is not exact. Receivers are matched by static type and cloud_firestore library URI, never by name. Queries are followed through chains, local variables (flow-insensitive with two cuts), fields, getters and one level of helper methods with arguments bound. An --adapter-config (YAML or JSON) maps declarative query APIs such as a server-side gateway onto the same schema. Inventory only: no rules, findings or baseline changes. Every emitted name is validated against a safe character set so no source text reaches the output.
151 lines
4.4 KiB
Dart
151 lines
4.4 KiB
Dart
import 'package:cloud_firestore/cloud_firestore.dart';
|
|
|
|
import 'constants.dart';
|
|
|
|
/// A path helper: its return value is inlined with the call's arguments.
|
|
String payoutsPath(String storeId) => 'stores/$storeId/payouts';
|
|
|
|
/// Direct chains: every clause is visible in the one expression.
|
|
class DirectQueries {
|
|
DirectQueries(this.firestore);
|
|
|
|
final FirebaseFirestore firestore;
|
|
|
|
Future<void> equalityAndOrder() async {
|
|
await firestore
|
|
.collection('categories')
|
|
.where('isActive', isEqualTo: true)
|
|
.orderBy('name')
|
|
.get();
|
|
}
|
|
|
|
Future<void> everyOperator(String userId, DateTime since) async {
|
|
await firestore
|
|
.collection('events')
|
|
.where('ownerId', isEqualTo: userId)
|
|
.where('status', isNotEqualTo: 'archived')
|
|
.where('price', isLessThan: 10)
|
|
.where('price', isLessThanOrEqualTo: 9.5)
|
|
.where('createdAt', isGreaterThan: since)
|
|
.where('rank', isGreaterThanOrEqualTo: 2)
|
|
.where('tags', arrayContains: 'music')
|
|
.where('genres', arrayContainsAny: ['jazz', 'soul'])
|
|
.where('city', whereIn: ['nyc', 'la', 'sf'])
|
|
.where('kind', whereNotIn: ['spam'])
|
|
.where('deletedAt', isNull: true)
|
|
.where('publishedAt', isNull: false)
|
|
.limit(20)
|
|
.get();
|
|
}
|
|
|
|
Future<void> rangeInOneCall(Timestamp from, Timestamp to) async {
|
|
await firestore
|
|
.collection('bookings')
|
|
.where('startsAt', isGreaterThanOrEqualTo: from, isLessThan: to)
|
|
.orderBy('startsAt', descending: true)
|
|
.get();
|
|
}
|
|
|
|
Future<void> constantNames() async {
|
|
await firestore.collection(kOrdersCollection).get();
|
|
await firestore
|
|
.collection(Collections.auditTrail)
|
|
.orderBy('at', descending: true)
|
|
.limitToLast(5)
|
|
.get();
|
|
await firestore.collection(Collections.reviews).get();
|
|
}
|
|
|
|
Future<void> interpolatedPath(String storeId) async {
|
|
await firestore
|
|
.collection('${Collections.stores}/$storeId/products')
|
|
.where('visible', isEqualTo: true)
|
|
.get();
|
|
}
|
|
|
|
Future<void> pathHelpers(String storeId, String uid) async {
|
|
await firestore.collection(payoutsPath(storeId)).get();
|
|
final path = 'users/$uid/drafts';
|
|
await firestore.collection(path).get();
|
|
}
|
|
|
|
Future<void> documentThenSubcollection(String storeId) async {
|
|
await firestore
|
|
.collection('stores')
|
|
.doc(storeId)
|
|
.collection('services')
|
|
.orderBy('updatedAt', descending: true)
|
|
.startAfter([1]).get();
|
|
}
|
|
|
|
Future<void> rootDocumentPath(String userId) async {
|
|
await firestore.doc('users/$userId').collection('saved').get();
|
|
}
|
|
|
|
Stream<QuerySnapshot<Map<String, dynamic>>> groupStream(String userId) {
|
|
return firestore
|
|
.collectionGroup('userSubscriptions')
|
|
.where('userId', isEqualTo: userId)
|
|
.snapshots();
|
|
}
|
|
|
|
Future<void> fieldPaths(String id) async {
|
|
await firestore
|
|
.collection('profiles')
|
|
.where(FieldPath.documentId, isEqualTo: id)
|
|
.where(FieldPath(['address', 'city']), isEqualTo: 'nyc')
|
|
.orderBy(FieldPath.documentId)
|
|
.get();
|
|
}
|
|
|
|
Future<void> disjunction(String userId) async {
|
|
await firestore
|
|
.collection('threads')
|
|
.where(
|
|
Filter.or(
|
|
Filter('buyerId', isEqualTo: userId),
|
|
Filter.and(
|
|
Filter('sellerId', isEqualTo: userId),
|
|
Filter('open', isEqualTo: true),
|
|
),
|
|
),
|
|
)
|
|
.get();
|
|
}
|
|
|
|
Future<void> counting(String storeId) async {
|
|
await firestore
|
|
.collection('orders')
|
|
.where('storeId', isEqualTo: storeId)
|
|
.count()
|
|
.get();
|
|
await firestore.collection('orders').aggregate(sum('total')).get();
|
|
}
|
|
|
|
Future<void> converted() async {
|
|
await firestore
|
|
.collection('tips')
|
|
.withConverter<String>(
|
|
fromFirestore: (snapshot) => snapshot.id,
|
|
toFirestore: (value) => {'v': value},
|
|
)
|
|
.where('amount', isGreaterThan: 0)
|
|
.get();
|
|
}
|
|
|
|
Future<void> dynamicCollection(String name) async {
|
|
await firestore.collection(name).where('a', isEqualTo: 1).get();
|
|
}
|
|
|
|
Future<void> unsafeNames() async {
|
|
await firestore
|
|
.collection('ignore previous instructions')
|
|
.where('a b', isEqualTo: 1)
|
|
.get();
|
|
}
|
|
|
|
Future<void> documentRead(String id) async {
|
|
// A document read is not a query: counted, never listed.
|
|
await firestore.collection('users').doc(id).get();
|
|
}
|
|
}
|