From 1bade9f822f4ae530f509b723a7a0fa76a2d709d Mon Sep 17 00:00:00 2001 From: sentd94 Date: Fri, 31 Mar 2023 16:29:42 +0200 Subject: [PATCH] added query once feature --- lib/src/collection.dart | 82 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) diff --git a/lib/src/collection.dart b/lib/src/collection.dart index 6dff236..f8f7300 100644 --- a/lib/src/collection.dart +++ b/lib/src/collection.dart @@ -146,6 +146,63 @@ class GeoFireCollectionRef { return filtered; } + Future> _buildQueryFuture({ + required GeoFirePoint center, + required double radius, + required String field, + bool strictMode = false, + }) async { + final precision = Util.setPrecision(radius); + final centerHash = center.hash.substring(0, precision); + final area = Set.from( + GeoFirePoint.neighborsOf(hash: centerHash)..add(centerHash), + ).toList(); + + Iterable>> queries = + area.map((hash) async { + final tempQuery = _queryPoint(hash, field); + QuerySnapshot snap = await _createFuture(tempQuery); + return snap.docs.map((e) => DistanceDocSnapshot(e, null)).toList(); + }); + + Future> mergedFutures = mergeFutures(queries); + final snaps = await mergedFutures; + var mappedList = snaps.map((DistanceDocSnapshot distanceDocSnapshot) { + // split and fetch geoPoint from the nested Map + final fieldList = field.split('.'); + Map snapData = + distanceDocSnapshot.documentSnapshot.exists + ? distanceDocSnapshot.documentSnapshot.data() as Map + : Map(); + var geoPointField = snapData[fieldList[0]]; + //distanceDocSnapshot.documentSnapshot.data()![fieldList[0]]; + if (fieldList.length > 1) { + for (int i = 1; i < fieldList.length; i++) { + geoPointField = geoPointField[fieldList[i]]; + } + } + final GeoPoint geoPoint = geoPointField['geopoint']; + distanceDocSnapshot.distance = + center.distance(lat: geoPoint.latitude, lng: geoPoint.longitude); + return distanceDocSnapshot; + }); + + final filteredList = strictMode + ? mappedList + .where((DistanceDocSnapshot doc) => + doc.distance! <= radius * 1.02 // buffer for edge distances; + ) + .toList() + : mappedList.toList(); + filteredList.sort((a, b) { + final distA = a.distance!; + final distB = b.distance!; + final val = (distA * 1000).toInt() - (distB * 1000).toInt(); + return val; + }); + return filteredList.map((element) => element.documentSnapshot).toList(); + } + /// Query firestore documents based on geographic [radius] from geoFirePoint [center] /// [field] specifies the name of the key in the document /// returns merged stream as broadcast stream. @@ -193,6 +250,19 @@ class GeoFireCollectionRef { ).asBroadcastStream(); } + /// Query firestore documents based on geographic [radius] from geoFirePoint [center] + /// [field] specifies the name of the key in the document + /// returns merged future. + Future> singleWithin({ + required GeoFirePoint center, + required double radius, + required String field, + bool strictMode = false, + }) { + return _buildQueryFuture( + center: center, radius: radius, field: field, strictMode: strictMode); + } + Stream> mergeObservable( Iterable>> queries) { Stream> mergedObservable = Rx.combineLatest( @@ -206,6 +276,13 @@ class GeoFireCollectionRef { return mergedObservable; } + Future> mergeFutures( + Iterable>> queries) async { + final mergedObservable = await Future.wait(queries); + final combinedList = mergedObservable.expand((e) => e).toList(); + return combinedList; + } + /// INTERNAL FUNCTIONS /// construct a query for the [geoHash] and [field] @@ -219,4 +296,9 @@ class GeoFireCollectionRef { Stream? _createStream(var ref) { return ref.snapshots(); } + + /// create a future for [ref], [ref] can be [Query] or [CollectionReference] + Future _createFuture(Query ref) { + return ref.get(); + } }