Geofire - not found locations
you pass value 8f
(float) as the radius
there, while the the radius
should rather be 8.0d
or Double.valueOf(8.0)
, where MAX_SUPPORTED_RADIUS
equals 8587
kilometers.
while the actual problem is, that GeoFire
already would need to know of .child("location")
, but it is not possible to represent that with a Reference
; only DataSnapshot
has getChildren()
.
the bottom line is:
you'd have to create a separate locations
Reference
, in order to avoid the nesting. nevertheless you still can use the relateduid
key for these nodes (or at least add it as a child-node), in order to be able to look up within the usersReference
. it's a1:1
relation, in between twoReference
s.
so here's a working Java
example, just because ...
we're assuming the following structure (as described above):
{
"locations" : {
"CR88aa9gnDfJYYGq5ZTMwwC38C12" : {
".priority" : "9q8yywdgue",
"g" : "9q8yywdgue",
"l" : [ 37.7853889, -122.4056973 ]
}
},
"users" : {
"CR88aa9gnDfJYYGq5ZTMwwC38C12" : {
"displayName" : "user 01",
...
}
}
}
the database rules should have .indexOn
for locations
field g
set:
{
"rules": {
...
"locations": {
".indexOn": "g"
}
}
}
the dependencies in the module's build.gradle
:
dependencies {
...
implementation "com.firebase:geofire-android:2.3.1"
}
and this demonstrates, how to obtain a user's snapshot by a GeoQuery
result;
notice the GeoQueryEventListener
instead of the GeoQueryDataEventListener
:
public class GeofireActivity extends AppCompatActivity {
private static final String LOG_TAG = GeofireActivity.class.getSimpleName();
private DatabaseReference refBase = null;
private DatabaseReference refLocation = null;
private DatabaseReference refUser = null;
private GeoFire geoFire = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.setContentView(R.layout.fragment_geofire);
this.setReferences();
}
private void setReferences() {
this.refBase = FirebaseDatabase.getInstance().getReference();
this.refUser = refBase.child("users");
this.refLocation = refBase.child("locations");
this.geoFire = new GeoFire(this.refLocation);
}
private void searchNearby(double latitude, double longitude, double radius) {
this.searchNearby(new GeoLocation(latitude, longitude), radius);
}
private void searchNearby(GeoLocation location, double radius) {
GeoQuery geoQuery = this.geoFire.queryAtLocation(location, radius);
geoQuery.addGeoQueryEventListener(new GeoQueryEventListener() {
@Override
public void onKeyEntered(String key, GeoLocation location) {
String loc = String.valueOf(location.latitude) + ", " + String.valueOf(location.longitude);
Log.d(LOG_TAG, "onKeyEntered: " + key + " @ " + loc);
/* once the key is known, one can lookup the associated record */
refUser.child(key).addListenerForSingleValueEvent(new ValueEventListener() {
@Override
public void onDataChange(@NonNull DataSnapshot dataSnapshot) {
Log.d(LOG_TAG, "onDataChange: " + dataSnapshot.toString());
}
@Override
public void onCancelled(@NonNull DatabaseError firebaseError) {
Log.e(LOG_TAG, "onCancelled: " + firebaseError.getMessage());
}
});
}
@Override
public void onKeyExited(String key) {
Log.d(LOG_TAG, "onKeyExited: " + key);
}
@Override
public void onKeyMoved(String key, GeoLocation location) {
Log.d(LOG_TAG, "onKeyMoved: " + key);
}
@Override
public void onGeoQueryReady() {
Log.d(LOG_TAG, "onGeoQueryReady");
}
@Override
public void onGeoQueryError(DatabaseError error) {
Log.e(LOG_TAG, "onGeoQueryError" + error.getMessage());
}
});
}
}
in order to maintain the integrity, one would need to remove the associated location record, when a user record is being removed - else it would result in keys, which cannot be looked up anymore.
As it stands right now geofire
sort of serves as an index to make geoqueries on, and provides the key of the document you want (which would be stored in a separate "collection").
You should be using geofire
and a separate "collection" (call it usersLocations)
DatabaseReference ref = FirebaseDatabase.getInstance().getReference("usersLocations");
GeoFire geoFire = new GeoFire(ref);
Now you can use it as an index for your users, and can add items to it like so.
geoFire.setLocation('QymlMpC0Zc', new GeoLocation(40.2334983, -3.7185183));
Your Firebase RTDB will look like this now:
{
'users': {
'QymlMpC0Zc': {
// All your data here
}
},
'usersLocations': {
'QymlMpC0Zc': {
'g': 'ezjkgkk305',
'l': {
'0': 40.2334983,
'1': -3.7185183
}
}
}
}
So finally when you do a query on your geoFire
you'll end up being firing whatever listeners you have.
As a small note... I am not a Java developer, but I do use/know geofire
in general. Hopefully my bits of advice/thoughts will be helpful.
The Problem is When You Passed Radius According to Issue https://github.com/firebase/geofire-java/issues/72
double radius = 8589; // Fails
// double radius = 8587.8; //Passes
try to pass value like this this may helps
//GeoQuery geoQuery = geoFire.queryAtLocation(geoLocation, 8f);
GeoQuery geoQuery = geoFire.queryAtLocation(geoLocation, radius);
passing value 8f (float) as the radius, while the the radius should rather be 8.0d or Double.valueOf(8.0), where MAX_SUPPORTED_RADIUS equals 8587 kilometers.