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 related uid key for these nodes (or at least add it as a child-node), in order to be able to look up within the users Reference. it's a 1:1 relation, in between two References.

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.