如何在Firebase数据库中保存GeoFire坐标以及其他项目?

19

我正在开发一个应用程序,并在Firebase数据库中保存一些字符串,例如 postedAtTime postedBy postedOnDate 。我希望将GeoFire坐标保存在同一个节点中,以便稍后可以轻松地进行查询。

以下是我保存所有字符串的路径:

databaseReferenceHRequests = firebaseDatabase.getReferenceFromUrl("https://appName-e1a35.firebaseio.com/requests/");

这是我保存它的方式:

// in onButtonClicked method:
postNewRequest(null, imageUID, MainActivity.userName.getText().toString(), time, date, utcFormatDateTime, MainActivity.userEmail.getText().toString(), geoFire);

// the method:
public void postNewRequest(Bitmap bitmap, String imageUIDh, String postedBy, String postedAtTime, String postedOnDate, String utcFormatDateTime, String userEmail, GeoFire geoFire) {
    HRequest hRequest = new HelpRequest(null, imageUIDh, postedBy, postedAtTime, postedOnDate, utcFormatDateTime, userEmail, geoFire);
    databaseReferenceHRequests.push().setValue(hRequest);
}

以下是它在数据库中的保存方式:

database structure screenshot

我想要将GeoFire坐标保存在同一节点中,这里是-KLIoLUsI0SpQZGpV1h4。 这只是一个随机生成的推送ID。

我尝试通过提供以下引用来实现:

geoFire = new GeoFire(firebaseDatabase.getReferenceFromUrl("https://appName-e1a35.firebaseio.com/requests/"));

然后像上面展示的那样将它与其他项目一起推送。但是,这仅保存了GeoFire坐标,并未保存节点requests下的其他项目。

那么我的GeoFire引用应该是什么,以便它与同一节点中的所有数据一起保存?

这里出了什么问题?请告诉我。

4个回答

39

弗兰克的回答是正确的,但我想举个例子。你的数据库结构应该像这样。

{
"items" : {
    <itemId> : {
        "someData" : "someData",
        ...
    }
  },
"items_location" : {
    <itemId> : {
        <geofireData> ...
    }
  }
}
要获取数据,首先需要在items_location节点执行GeoQuery,然后在onKeyEntered方法中获取数据。参数key是我的示例中的itemId
geoFire = new GeoFire(FirebaseDatabase.getInstance().getReference().child("items_location");
geoQuery = geoFire.queryAtLocation(geoLocation), radius);
geoQuery.addGeoQueryEventListener(new GeoQueryEventListener() {
    @Override
    public void onKeyEntered(String key, GeoLocation location) {
         //retrieve data
    }
};    

希望这可以帮到你。

编辑 如何推送物品并设置geofire数据。

String itemId = ref.child("items").push().getKey();

ref.child("items").child(itemId).setValue(item);

geoFire = new GeoFire(ref.child("items_location"));
geoFire.setLocation(itemId, new GeoLocation(lattitude, longitude));

编辑:将项目数据和地理位置数据保存在一个API调用中

GeoHash geoHash = new GeoHash(new GeoLocation(latitude, longitude));
Map<String, Object> updates = new HashMap<>();
updates.put("items/" + itemId, item);
updates.put("items_location/" + itemId + "/g", geoHash.getGeoHashString());
updates.put("items_location/" + itemId + "/l", Arrays.asList(latitude, longitude));
ref.updateChildren(updates);

很抱歉翻出旧帖子,但我如何处理两个操作的原子性?我的意思是,我可以使用updateChildren,但geofire在内部处理插入。 - Leonardo
为什么我们需要在 "items_location/" 后面添加 "/g" 或 "/l"? - Andy Strife
@Wouter 悲伤地是的 :) - Wilik
@dmr07 GeoLocation 不会存储高度信息,所以是的,你需要将其保存在 items 下 :) - Wilik
1
跟进问题:“GeoFire是否强制要求item和item_location之间的键相同?”:https://stackoverflow.com/q/49379357/2327328 - philshem
显示剩余8条评论

13

当你使用Geofire时,你有两个数据列表:

  1. 一份包含常规属性的项目列表
  2. 一份包含geohash索引和它们关联的键的列表,可以通过Geofire进行查询

你使用这些键从Geoquery结果中获取常规项目。这就是为什么Geofire的事件被称为“Key Entered”、“Key Exited”等事件。

尝试将它们存储在一个节点中是不好的想法,因为你正在混合大多数静态数据(项目属性)与高度易变的数据(地理位置信息)。将两者分开会提高性能,这也是Geofire强制执行的原因。

虽然可能存在属性和地理数据同样动态/静态的用例,但GeoFire不支持将地理数据和其他属性存储在单个位置中。


谢谢您的回答,但我不确定我得到了我的问题的答案。我的意思是,我如何能够仅从数据库中检索那些距离用户0.5公里范围内的帖子?我还保存了发布帖子的纬度和经度。请更清楚地解释一下,先生。 - Hammad Nasir
Geofire会仅返回节点的键,然后您必须查询常规属性节点吗?还是Geofire也会返回常规属性数据? - Barry MSIH
@Frank,你能否解释一下如何存储geohash索引以及它的结构是什么? - Muhammad Younas

1

对于那些最近有同样问题的人来说,使用Geofirestore库是可行的,该库支持在基于Firebase Firestore数据库构建的应用程序中使用Geofire。


1
你可以使用Firebase函数,在每个新条目上为其输入。
let functions = require('firebase-functions');

let GeoFire = require('geofire');

exports.testLocation = functions.database.ref('/items/{item}').onWrite(event => {
let data = event.data.val();
   console.log(data);
   console.log(event.params.item);

   if (data.location && data.location.coords) {

       console.log('Update GeoFire');

       let ref = event.data.adminRef.parent.parent.child('/items_locations'));

       let key = event.params.test;
       let location = [data.location.coords.latitude, data.location.coords.longitude]);
       let geoFire = new GeoFire(ref);

       geoFire.set(key, location).then(() => {
          console.log('Update succesfull');
       }).catch(error => {
          console.log(error);
       });
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接