Firebase - query on nested key? - java

I'm running an Android app that handles some data on Events and Users who have been invited to them. I have a page that needs to show all the events that a user has been invited to, but I'm relatively new to Firebase and I'm not sure how the query syntax would work for my current data structure.
An example of my data-structure (with dummy keys) can be seen below:
events
event1_key
eventName: "test event"
eventAdmin: "userAdmin"
eventInvites
user1_key
attending: true
responseTimestamp: 1525416294951
user2_key
//...
event2_key
//...
eventInvites
user2_key
//...
user3_key
//...
So using this example, if I was looking for all the events that user1_key had been invited to, the query should only return the event1_key record, because that one has a nested eventInvite for user1_key.
If I'm running that query in Java for Android Studio, does anybody know what it should look like?

I think you need to replicate your data in another node in this case. Right now, you have a single node which keeps all the events (i.e. events node). I would suggest your create a separate table (i.e. separate node) for users as well.
That node structure might look like the following.
users
user1_key
events1_key
events2_key
...
user2_key
events5_key
events2_key
....
....
I this way you will not require any nested lookup. Just replicate the data here in this node as well when you are inserting the data under events node.
Hope that helps!
Update
If you are considering replicating your table, then the check for user is attending an event will be fairly simple like the following.
userRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
boolean attending = false;
for (DataSnapshot eventSnapshot : dataSnapshot.getChildren()) {
if(eventSnapshot.getKey().equals(THE_DESIRED_EVENT)) {
attending = true;
break;
}
}
if(attending) doSomethingAsUserIsFoundToBeAttendingTheEvent();
else userIsNotAttending();
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
The code is not tested, I just have the pseudo implementation. Please modify as per your need.

To achieve what you want, I recommend add another node in your database structure that looks like this:
Firebase-root
|
--- userEvents
|
--- user1_key
|
--- event1_key: true
|
--- event2_key: true
In this way you'll be able to read all your events that correspond to a particular user at once without the need to download the enitire events object. To get those ids, please use the following code:
DatabaseReference rootRef = FirebaseDatabase.getInstance().getReference();
DatabaseReference userKeyRef = rootRef.child("userEvents").child(userKey);
ValueEventListener valueEventListener = new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for(DataSnapshot ds : dataSnapshot.getChildren()) {
String eventKey = ds.getKey();
Log.d("TAG", eventKey);
}
}
#Override
public void onCancelled(DatabaseError databaseError) {}
};
userKeyRef.addListenerForSingleValueEvent(valueEventListener);

Related

What is the best way to update a record getting the key from another table?

I am learning how realtime databases work, so be patient.
To better explain my doubt, I take an image showing the relevant part of the realtime database
https://prnt.sc/p0wmvs
All I want is the best way to update the field "name" of a record "grps".
The starting point is the "usrs" table where I can reference
mFD.getReference("usrs/"+user.getUid()+"/asAdm/grps/"+grpId)
My function to update "name" of group name
public void editGroupFromUser(long grpID, Group group, final DataStatus dataStatus) {
DatabaseReference mFRUser = mFirebaseDatabase.getReference(
"usrs/"+user.getUid()+"/asAdm/gprs/"+grpID
);
mFRUser.child("grp").addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
String groupKey = (String) dataSnapshot.getValue();
DatabaseReference mFRGroup = mFirebaseDatabase.getReference("grps");
mFRGroup.child( groupKey ).setValue(group).addOnSuccessListener(aVoid -> {
dataStatus.DataIsUpdated();
});
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
The need to receive the group key to then send the group update. This does not seem efficient at all.
Also, the need to send the user Uid key when the user is identified in firebase does not seem safe at all.
Is there any better way to do this?

how to add a new "key-value" pair for existing child in firebase

I've connected my android app to firebase and enabled email and password authentication. My current database status is like :
Now I want to add an extra field (key:"number")for all users i have so that my final database looks like:
so please tell me how can I do this ?
One more important thing is I wrote code such that I first authenticate them, if their registration is succesfull then I take user to new Activity where they will enter this "key" after collecting the key I should add this to user node as shown in the second picture.
So please tell me how to do this?
To write a value in the Firebase Realtime Database, you must know the complete path to that value. So in your case that means that you first have to load all data from the database, to be able to determine the dynamic keys (C76w3..., FjsZ..., etc).
Something like this:
DatabaseReference ref = FirebaseDatabase.getInstance().getReference();
ref.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot snapshot: dataSnapshot.getChildren()) {
snapshot.getRef().child("key").setValue("hisoka");
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
throw databaseError.toException();
}
}

How to get children keys from Firebase without downloading their children?

I have a Firebase database with a node "items" that has many children and I would like to import a list of the children keys. Since each child contains quite some data that I am not interested in for this purpose, I would like to download only the children keys (to minimize the amount of data transferred). For illustration, suppose I have the following data structure:
Then I would like to get the id's of all the users, so 5Aoz6ZaB9QS7o9M4lsdbh07VGA02 etc. But without downloading the other user data such as the messages, name, proposed and score.
To get the ids of those users, please use the following code:
DatabaseReference rootRef = FirebaseDatabase.getInstance().getReference();
DatabaseReference usersRef = rootRef.child("users");
ValueEventListener valueEventListener = new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for(DataSnapshot ds : dataSnapshot.getChildren()) {
String uid = ds.getKey();
Log.d("TAG", uid);
}
}
#Override
public void onCancelled(DatabaseError databaseError) {}
};
usersRef.addListenerForSingleValueEvent(valueEventListener);
The output will be:
5Aoz6ZaB9QS7o9M4lsdbh07VGA02
// the other ids
When you attach a listener on a specific node, you get all the data beneath that node. This is how Firebase Realtime database works and unfortanately this behaviour cannot be changed. But there is an workaround in which you can create another separate node in which you can add all those ids separately. The new node should look something like this:
Firebase-root
|
--- userIds
|
--- 5Aoz6ZaB9QS7o9M4lsdbh07VGA02: true
|
--- //other ids
In this case, if you attach a listener on userIds node, you'll get only the desired data and nothing more. The code to get those ids is simmilar with the code above, but instead of using .child("users") you need to use .child("userIds"). The output will be the same.
If you want another behaviour, you can consider using Cloud Firestore where this behaviour isn't an issue anymore.
You can use shallow=true parameter. It will return only the first layer of results (keys), follower by true.
A request to /users.json?shallow=true
would return the following:
{
5Aoz6ZaB9QS7o9M4lsdbh07VGA02: true,
900xwQqVBMOOBUBFbQTeyjwhFsc2: true,
RqwVZgV5cWd1fwkWm2ibuSX0Zfe2: true,
bnDyoXg2sXPJoSAuvdHjkFBLdao2: true,
fitCihcRwYQOpza7nsdawCHJ6Bn1: true,
}
check more in: https://firebase.google.com/docs/database/rest/retrieve-data

Perform key Specific Query in Firebase

I have structure for database in this manner
-... location
-... messages
-... id1_id2
-... id3_id4
-... id5_id6
.
.
.
How can I access a object in messages where I just know id1 which is the key.
TIA.
Change your database to this:
Messages
userid1
userid2
message: hello
then to retrieve message, do this:
DatabaseReference ref = FirebaseDatabase.getInstance().getReference("Messages").child(userid1);
ref.addListenerForSingleValueEvent(new ValueEventListener(){
#Override
public void onDataChange(DataSnapshot dataSnapshot){
for (DataSnapshot ds : dataSnapshot.getChildren()) {
String messages=ds.child("message").getValue().toString();
}
}
});
Try this one
DatabaseReference ref = FirebaseDatabase.getInstance().getReference("location/messages");
ref.addListenerForSingleValueEvent(new ValueEventListener(){
#Override
public void onDataChange(DataSnapshot dataSnapshot){
for (DataSnapshot ds : dataSnapshot.getChildren()) {
if(ds.getKey().contains("bob")){
//Do something with the date
break;
}
}
}
});
Suppose your structure is like this:
You can do something like this, this will grab all nodes from messages that start with id1:
String id1 = "id1";
DatabaseReference ref = FirebaseDatabase.getInstance().getReference("messages");
ref.orderByKey().startAt(id1).endAt(id1 + "\uf8ff").addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot snap : dataSnapshot.getChildren()) {
Log.d("SNAP", snap.getValue(String.class));
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
I took the idea from the javascript implementation, but it also worked in Android, I've run a test on my device/database. Note that this method will work to grab data that starts with the given id.
EDIT:
The idea of querying firebase data that contains a certain string is discussed in this official post. Also, this question as well. The bottom line is that the api doesn't support these types of queries, the approach I mentioned above is the closest you can get of implementing a "SQL LIKE" in firebase. Currently, there's no way of searching for strings that END with another string. The endAt doesn't mean the string ends with id1, but rather that the range of values I want to retrieve finishes at (id1 + "\uf8ff"), that means any string starting with id1.
Your options are either change the schema or grab all messages and search locally (the suggestions of the other two answers).

Firebase Android get value by child key

I have the following database structure in Firebase:
I would like to retrieve all objects from "chats" if in the "users" object contains the key "user2".
Is it possible to download the data as written with no change of structure Firebase?
My actually code to get data:
DatabaseReference chatReference = FirebaseDatabase.getInstance().getReference("conversationsNew");
chatReference.orderByChild("users").addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
Log.d("TAG", dataSnapshot.getChildrenCount());
for (DataSnapshot chat : dataSnapshot.getChildren()) {
Log.d("TAG", chat.toString());
Chat chat = chat.getValue(Chat.class);
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
Thank you very much.
That's quite possible:
DatabaseReference chatReference = FirebaseDatabase.getInstance().getReference("chats");
chatReference.orderByChild("users/user1").equalTo(true).addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot chat: dataSnapshot.getChildren()) {
Log.d("TAG", chat.getKey());
Chat chat = chat.getValue(Chat.class);
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
throw databaseError.toException(); // don't ignore errors
}
});
This will print:
chat1
But this approach will not scale, since you'll need to add an index for each user. To prevent this, you'll need a data structure that more closely reflects the use-case. So: if you want to show a list of the chats that the current user is in, you'll need to store precisely that: a list of the chats for each user. Alex' answer shows the most common way of storing such data.
No, there is no possibility for achieving this using your code. What can you do instead, is to change a little bit your database structure, by adding a new node like this:
Firebase-root
--- users
--- userId1
--- userName: John
--- chats
--- chat1: true
Having this, you can query your database for getting the chats in which your user belong. To verify for existens you only need to use exists() method on the dataSnapshot object.
Hope it helps.

Categories