/users:
- user1
- name: user1name
- /contacts
- user2
- user3
.....
- user2
- name: user2name
....
This is my current firebase database. When user1 is logged in I check which contacts he has and get their UIDs. Now that I have their UIDs I'm trying to read the names of his contacts. How can I get the names for each of his contacts? Do I have to read the entire /users directory or can I use a query that retrieves only the data that I want. (In this case: the name of user2 and user3)
You'd do a nested read. So retrieve /users/$uid/contacts, then loop over the keys in there and for each key load /users/$key.
Supposing that you already have the user selected, you can go about it in two ways:
1) Use .child(String user); in your database reference.
2) You may have declared a URL for your database. Add + "/" + String user; to your URL.
You can further use these ways to parse through contacts of that user.
Finally, to retrieve contacts, you have to loop through.
Here is an example code:
mRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot snapshot) {
for (DataSnapshot msgSnapshot : snapshot.getChildren()) {
mDataset.add(msgSnapshot.getKey());
mAdapter.notifyDataSetChanged();
}
}
This snippet fetches data and adds it to your adapter (which can be used to populate list view, grid view etc)
Related
I am trying to check uid from firebase Realtime database, but its only returns the current uid and when I try to check uid with previous uid then also it only returns the current uid. I had tried many ways and searched but I can't get the exact solution, please if any one could help.
here what I am using to check the uid
FirebaseAuth.getInstance().getCurrentUser().getUid();
here is my code how I am trying to check
String userEnteredUserName = binding.textLoginUserName.getEditText().getText().toString().trim();
String userEnteredPassword = binding.textPassword.getEditText().getText().toString().trim();
DatabaseReference reference = FirebaseDatabase.getInstance().getReference("Users");
Query checkUser = reference.child(FirebaseAuth.getInstance().getCurrentUser().getUid()).orderByChild("userName").equalTo(userEnteredUserName);
Log.d("uids", "uid: " + uid);
checkUser.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot snapshot) {
binding.textLoginUserName.setError(null);
binding.textLoginUserName.setErrorEnabled(false);
Log.d("users", "Username: " + snapshot.toString());
if (snapshot.exists()) {
String passwordFromDB = snapshot.child(userEnteredUserName).child("password").getValue(String.class);
if (passwordFromDB.equals(userEnteredPassword)) {
//next activity
}else{
Toast.makeText(Login.this, "Error", Toast.LENGTH_SHORT).show();
}
}
#Override
public void onCancelled(#NonNull DatabaseError error) {
Toast.makeText(Login.this, "Error", Toast.LENGTH_SHORT).show();
}
});
In log what I get,
2022-02-06 13:13:06.173 18093-18093/abc C/uids: uid: OFtpR6bfISP3Odd9K1oGWCQmeEf2
Here is my firebase data, "aisha12" is working which is under the current uid but when I try to check "vgbb" it returns only the current uid
If I understand correctly, you are trying to allow a user to register a username and ensuring that the username is unique. Your current data structure doesn't allow you to do that query though.
Firebase Realtime Database can only order/filter on a value that is at a fixed path under each child node of the location you query. So in your current code:
reference.child(FirebaseAuth.getInstance().getCurrentUser().getUid()).orderByChild("userName").equalTo(userEnteredUserName);
You are querying the direct child nodes of /Users/$uid looking for the username under there. Since you're specifying the UID in that path, you're only searching under that specific user.
There is no way with your current data structure to search across all /Users, since the property you are looking for is under /Users/$uid/$username/userName, so with two dynamic keys in there, and the database can only handle one dynamic key.
To allow the query, you will need to change the data structure and remove the $userName level from it, so that you get:
Users: {
"3l6Rm....": {
"userName": "vgbb",
...
},
"OftpR...": {
"userName": "aisha12",
...
}
}
Now you can search for the username with:
DatabaseReference reference = FirebaseDatabase.getInstance().getReference("Users");
Query checkUser = reference.orderByChild("userName").equalTo(userEnteredUserName);
I also recommend checking out these previous questions on allowing a user to register a unique username:
How do you prevent duplicate user properties in Firebase?
Firebase security rules to check unique value of a child #AskFirebase
How to check if usernames are unique in Firebase
unique property in Firebase
How to give unique usernames from a list of usernames to the users in the firebase
Check value already exists or not in Firebase?
I have the following denormalized data structure:
A Contact can associate with multiple Records. A Record can have multiple associated Contacts (many<->many relationship). To keep track of their relationship, an int value to indicates the contact's role in a particular record, and store the role value in two separate references
Contact
- Contact1:data
- Contact2:data
- Contact3:data
Record
- Record1:data
- Record2:data
Record_Role_Ref
- Record1
-- Contact1: roleA
-- Contact2: roleA
-- Contact3: roleD
- Record2
-- Contact1: roleB
Contact_Role_Ref
- Contact1
-- Record1: roleA
-- Record2: roleB
I'm using FirebaseIndexRecyclerAdapter is to show a list of associated Contacts to a particular Record id. So for the key reference I would use Record_Role_Ref/record_id, and for the data reference I would use Contact, like so:
// Setup the reference to the all the associated contact list in record_role_ref, using the record id as key
Query mRecordRoleRef = firebaseDatabase.getReference().child(DB_RECORD_ROLE_REF).child(mRecordId);
// Reference the Contact data ref
Query mContactRef = firebaseDatabase.getReference().child(DB_CONTACT);
FirebaseIndexRecyclerAdapter mContactAdapter = new FirebaseIndexRecyclerAdapter<Contact, ContactViewHolder>(Contact.class,
R.layout.item_contact,
ContactViewHolder.class,
mRecordRoleRef, // The Firebase database location containing the keys associated contacts to this record
mContactRef)// The Firebase database location to watch for data changes. Each key key found at keyRef's location represents a list item in the RecyclerView.
Limitation(s): I don't want to store the role value in each contact and record object because each time a role is changed, both the contact and record's entire object would have fetched and updated. Users want to delete, modify, move both contact and records, and change roles.
Problem(s):
The contact's role value is stored as value of the key in the mRecordRoleRef. Is it possible/how to get the value from the key reference in on-go with FirebaseIndexRecyclerAdapter? What is the good/best practice in this kind of situation?
Thanks In Advance :)
As of now, I just form another data read request inside the populateViewHolder callback method. Since the data read request is itself also async, I'm not yet sure if this would work for a large list and when the view recycles. The viewHolder returned by the populateViewHolder is set to final.
Query mRecordContactRoleRef = firebaseDatabase.getReference().child(DB_RECORD_CONTACT_ROLE_REF).child(mRecordId).child(mContact.getContactId());
mRecordContactRoleRef.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
// Getting the role int base on record type
Long roleNum = (Long) dataSnapshot.getValue();
viewHolder.setContactRoleTv("hi, the role is " + roleNum);
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
My Firebase Database is like this
When the coding below was run:
String loc=(snapshot.child("loc").getvalue()).tostring();
The output I get has different sequence with the database:
Why is that so?
Firebase data is stored as JSON and is inherently unordered.
If you want to access the data in a specific order, you should specify an orderBy clause for your query and access the data using the snapshot's getChildren() method.
Say you want to log the items by ascending key:
DatabaseReference rootRef = FirebaseDatabase.getInstance().getRef();
Query locations = rootRef.orderByKey();
locations.addListenerForSingleValueEvent(new ValueEventListener() {
public void onDataChange(DataSnapshot snapshot) {
for (DataSnapshot locSnapshot: snapshot.getChildren()) {
System.out.println(locSnapshot.getKey() + ": " + locSnapshot.getValue(String.class));
}
}
public void onCancelled(DatabaseError databaseError) {
Log.w(TAG, "loadPost:onCancelled", databaseError.toException());
// ...
}
});
This sample comes (modified) from the Firebase documentation on reading lists of data.
Frank beat me to my edit, check out his correct solution using orderBy....
You need to use forEach rather than the child method (or child.foreach)
Here is a snippet from the doc:
Because of the way JavaScript Objects work, the ordering of data in
the JavaScript Object returned by val() is not guaranteed to match the
ordering on the server nor the ordering of child_added events. That is
where forEach() comes in handy. It guarantees the children of a
DataSnapshot will be iterated in their query-order.
Source: https://firebase.google.com/docs/reference/js/firebase.database.DataSnapshot#forEach
I have a database in Firebase for Android and I have an object with the attributes you see in the image. An object is stored in the database with the following code:
FirebaseUser user = mAuth.getCurrentUser();
String videoId = getIntent().getStringExtra("VIDEO_ID");
minuto = player.getCurrentTimeMillis();
Watching watching = new Watching(user.getUid(), videoId, String.valueOf(minuto));
DatabaseReference mRef = database.getReference().child("Watching").push();
mRef.setValue(watching);
The problem I have is as I am using push() to store the nodes I am having duplicate data as you can see in the image.
Is there any way to avoid storing duplicate data? Knowing that I don't have my own ID to store.
Any help ?
It depends on how you define a duplicate.
A common case where people have this question is when they're storing users. There the definition of a duplicate is simple: if two user objects have the same value for uid, they're the same user. So in that case you should store the users under their uid, instead of under a push ID.
The same applies for your situation. If a single user can only watch a single video, store the nodes under Watching by the uid of the user:
Watching
"NX7...A33"
"idVideo": "b13...o4s"
"minute": "0"
But if it's the combination of uid + idVideo that is unique, store the nodes under a combined key:
Watching
"NX7...A33_b13...o4s": "0"
Now you can easily prevent duplicates, by using setValue() instead of push():
String key = user.getUid() + "_" + videoId;
ref.child("Watching").child(key).setValue(String.valueOf(minuto));
I was facing same problem like you and this is how i figure it out. I m checking email address that user entered with the one saved in my db.if any match found then show toast that email already exists otherwise save with new key.
mFirebaseDatabase.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(final DataSnapshot dataSnapshot) {
for (DataSnapshot data : dataSnapshot.getChildren()) {
//If email exists then toast shows else store the data on new key
if (!data.getValue(User.class).getEmail().equals(email)) {
mFirebaseDatabase.child(mFirebaseDatabase.push().getKey()).setValue(new User(name, email));
} else {
Toast.makeText(ChatListActivity.this, "E-mail already exists.", Toast.LENGTH_SHORT).show();
}
}
}
#Override
public void onCancelled(final DatabaseError databaseError) {
}
});
I have recently started switching my app from Parse to Firebase. Everything is going great so far, but I have not been able to find a Firebase equivalent method to Parse's whereContainedIn(String key, Collection values).
This was a very useful method that allowed me to pass in an array of ids and it would return all of the rows that matched that id. So far with Firebase, I have it returning all all of the rows in the database then looping through them to see if that row's id is contained in my array of ids. The other way is to query for each id individually, which doesn't work well with Firebase's asynchronous behavior. This is what I am doing now for the first approach:
List<String> userIds = new ArrayList<>();
userIds.add("2");
userIds.add("32");
userIds.add("12545");
userIds.add("187");
DatabaseReference firebaseRef = FirebaseDatabase.getInstance().getReference();
Query queryRef = firebaseRef.child("videos");
queryRef.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
List<Video> usersVideos = new ArrayList<Video>();
for (DataSnapshot videoSnapshot : dataSnapshot.getChildren()) {
Video video = videoSnapshot.getValue(Video.class);
if (userIds.contains(video.userId)) {
usersVideos.add(video);
}
}
// usersVideos is now populated with the videos for the users
}
#Override
public void onCancelled(DatabaseError databaseError) {
Log.d(TAG, "CANCELLED");
}
});
Neither of these methods seem reasonable since my table will contain hundreds of thousands of records.
Any help would be greatly appreciated!
Make another reference that can be looked up by userId and have it return all videoIds that this user has. From there you can query the videos. That means each time you save you will need to save in two spots. Sort of a pain, but it is what Firebase recommends.
So your reference will look more like:
videos -
| - <videoId> -
| - <video stuffs>
| - userId
videosByUser -
| - <userId> -
| 0 - <videoIdA>
| 1 - <videoIdB>
| - <userId2> -
| 0 - <videoIdA>
can't you just perhaps do a for loop over your ids and inside the for loop write a valueventlistener that will be called for each iterated item`?