I'm developing an app and saving some strings like postedAtTime, postedBy, postedOnDate in Firebase database. I want to save the GeoFire coordinates in the same node in which all the above string are saved, so that later I can do query, easily.
Here's the path to which I'm saving all the strings:
databaseReferenceHRequests = firebaseDatabase.getReferenceFromUrl("https://appName-e1a35.firebaseio.com/requests/");
This is how I'm saving it:
// 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);
}
Here's how it is getting saved in the database:
What I want is to save the GeoFire coordinates in the same node, which is -KLIoLUsI0SpQZGpV1h4 here. This is just a push ID and it gets generated randomly.
I tried it by giving this reference:
geoFire = new GeoFire(firebaseDatabase.getReferenceFromUrl("https://appName-e1a35.firebaseio.com/requests/"));
And then pushing it with other items as shown above. But, this saved only GeoFire coordinates and not the other items under the node requests.
So, what should be my GeoFire reference so that it gets saved along with all the data in the same node?
What is going wrong here? Please let me know.
Frank's answer is correct, but I want to give an example.
Your database structure should be like this.
{
"items" : {
<itemId> : {
"someData" : "someData",
...
}
},
"items_location" : {
<itemId> : {
<geofireData> ...
}
}
}
To get the data, first you need to do GeoQuery at items_location node and then get the data on the onKeyEntered method. The parameter key is itemId from my example.
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
}
};
Hope this helps.
EDIT
How to push the item and set the geofire data.
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));
EDIT Save the item data and geofire data in one API call
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);
When you use Geofire, you have two lists of data:
a list of items with their regular properties
a list of geohash indexes and their associated keys, which you query through Geofire
You use the keys to get from the Geoquery results to the regular items. That's why the events for Geofire are called "Key Entered", "Key Exited", etc.
Trying to store them in one node is a bad idea, since you're mixing mostly static data (the properties of your items) with highly volatile data (the geo-location information). Separating the two out leads to better performance, which is why Geofire enforces it.
While there may be use-cases where the properties and geo-data are equally dynamic/static, GeoFire does not support keeping the geo-data and other properties in a single location.
You can use Firebase functions to enter it for you on every new entry
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);
});
}
}
For those more recently coming to this post with the same question, this is possible with the Geofirestore library, which supports Geofire for apps built on top of the Firebase Firestore database.
I would like to be able to publish simultaneously in two directories of my Firebase database. I created a function for this, according to the example proposed in the "Update specific fields" section of the Firebase Javascript documentation:
function linkTwoUsers(user1, user2) {
// The two users are "connected".
var user1Data = {
userLink: user2
};
var user2Data = {
userLink: user1
};
var updates = {};
updates["/users/" + user1] = user1Data;
updates["/users/" + user2] = user2Data;
return database
.ref()
.update(updates)
.then(() => {
return res.status(200).end();
})
.catch(error => {
return res.status(500).send("Error: " + error.message);
});
}
The problem is that when I run the function, instead of uploading the directories, it replaces all the data present in it.
Here are the user directories before the function:
And then:
How do we make sure the data doesn't overwrite the others? Thank you for your help.
Try to narrow your path to just the property you are trying to update:
updates["/users/" + user1 + "/userLink/"] = user1;
updates["/users/" + user2 + "/userLink/"] = user2;
It seems as though you're creating an entirely new object when you set:
var userData = { someThing: stuff }
When you pass that in, it will override the original object. One way you might solve this (there might be a more efficient way) is to grab the objects from Firebase, add the new property and value to the object, then send the entire object back into Firebase.
In some javascript frameworks, you should be able to use the spread operator to set all of an object's props to another object like this:
var newObject = { ...originalObject }
newObject.userData = "something"
// then save newObject to firebase
I have a problem and I want to modify some information not all, for example I want to modify only the address and the nit (see image) but in doing so I delete the other fields, how could I modify it without eliminating the rest?
image
My code:
var uid = firebase.auth().currentUser.uid;
var modi = {
nombre: "Hello",
direccion: "Address"
}
var updates = {};
updates['/Users/' + uid] = modi;
firebase.database().ref().update(updates);
Thank, you!
Do you have access to that User object data?
If so, basically merge the updates into that and then save to Firebase. E.g.
// if we already have some reference to User data object
var updates = {...userObject, ...modi};
firebase.database().ref('/Users/' + uid).update(updates);
else you can always fetch that object, then do the merge and update procedure above. E.g.
// modi defined above
return firebase.database().ref('/Users/' + uid)
.once('value')
.then(function(snapshot) {
var user = snapshot.val()
updates = {...user, ...modi}
firebase.database().ref('/Users/' + uid).update(updates);
});
I've been hitting my head against the wall on this for about 2 hours and I think I've just lost sight of the problem a bit.
I have an incremental field saved as "index" that upon a file upload starting has it's value increase by 1.
I am able to query the database and pull the value for index to the console and receive the updated value.
I can't for the life of me work out how to insert the value I've created and subsequently logged to the console (definitely doesn't need to be logged just did this to prove to myself I wasn't going insane) into the uploads metadata at the next stage of the script. I have tried everything I can think of - I've watched about an hour of youtube videos, and I can safely say beyond a shadow of a doubt I could turn my app into a running counter of peoples file uploads but I can't add it to their upload metadata!
Help me stack overflow you're my only hope!
Code below hopefully outlines the issue - the query is going into the variable indexRef but the actual info I need is in the nested variable "key" which is just the data snapshot value. This seems like it should be so easy.
var indexRef = firebase.database().ref('index');
indexRef.once('value')
.then(function(snapshot){
var key = snapshot.val()
console.log(key)
})
var imagekey = firebase.database().ref('images/').push().key;
var downloadURL = uploadTask.snapshot.downloadURL;
var updates = {};
var postData = {
url: downloadURL,
score: 1500,
index: indexRef,
user: user.uid
};
updates ['/images/'+imagekey] = postData;
firebase.database().ref().update(updates);
Thanks in advance and I apologise if the answer to this is trivial and I've wasted someones time!
Remember the then method returns promises https://firebase.googleblog.com/2016/01/keeping-our-promises-and-callbacks_76.html
var indexRef = firebase.database().ref('index');
// Declare variables outside of block to access within
var imagekey = firebase.database().ref('images/').push().key;
var downloadURL = uploadTask.snapshot.downloadURL;
var updates = {};
indexRef.once('value')
.then(function(snapshot){
var key = snapshot.val()
// Return key variable for use
return key;
})
.then(function(key){
// You can now access the key variable in here
var postData = {
url: downloadURL,
score: 1500,
index: key,
user: user.uid
};
updates ['/images/'+imagekey] = postData;
firebase.database().ref().update(updates);
})
Hope this helps you
In my firebase app when a new user signs up I add their initial data like displayname, emai , photourl to the database under the top level users node. This works fine.
Now when a user post a status, I want to upload the the post to top level statuses node where all user statuses are kept. And simultaneously I want to upload the post to current user's posts node i.e users/currentuser/posts.
I am following the methods shown on official firebase site here.
The problem is when I hit the post button nothing happens and no data is posted to the database
My function that gets invoked when the post button is clicked:
function postStatus(){
var ref = firebase.database().ref("allstatuses");
var user = firebase.auth().currentUser;
var newStatusRef = ref.push();
var newStatusKey = newStatusRef.key();
var statusData = {
status: postInput.val(),
likes: 0,
dislikes: 0
};
var updateUserStatus = {};
updateUserStatus["users/" + user.uid + "/" + newStatusKey] = statusData;
updateUserStatus["allstatuses/" + newStatusKey] = statusData;
if(user){
firebase.database().ref().update(updateUserStatus);
}else{
alert("please login");
}
}
What am I doing wrong?
According to the API reference link it is key not key()
Change this
var newStatusKey = newStatusRef.key();
to
var newStatusKey = newStatusRef.key;