Search child nodes in firebase database using android - android

Hello guys here is my firebase database:
I want to get list of all medicines with particular symptoms.
Here is my code i.e what i have done
public void initializeMedicineListener(String node,String type,String value){
mDatabase=mFirebaseInstance.getReference();
Query query = mDatabase.child("Medicine").child("symptoms").orderByChild("name").equalTo("Neck pain");
query.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
medicineList=new ArrayList<Medicine>();
if (dataSnapshot.exists()) {
for (DataSnapshot medicine : dataSnapshot.getChildren()) {
Medicine data = medicine.getValue(Medicine.class);
medicineList .add(data);
}
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
But i am getting null results.
Please guide me guys.Am i doing something wrong??

When you run a query at a location, Firebase check each child node at that location for the property that you order on and the range/condition you filter on.
mDatabase.child("Medicine").child("symptoms").orderByChild("name").equalTo("Neck pain");
So this checks the children of /Medicine/symptoms for their name property and only returns them if they have a value equal to Neck pain.
There are two problems with this:
Your JSON doesn't have a /Medicine/symptoms. Instead you have a Medicine, where each child node has a symptoms node.
Even if it did, your symptoms child doesn't have a value Neck pain. Instead you have an array, where each value may be Neck pain.
The closest you can now get to the query you want is:
mDatabase.child("Medicine").orderByChild("symptoms/0/name").equalTo("Neck pain");
This query returns medicines for which the first symptom name is equal to Neck pain. Firebase cannot perform a query across all array members to see if it contains a specific value in any position.
As usual with NoSQL databases: if you can't perform the use-case you want with your current data structure, you can typically change/expand your data structure to allow the use-case. And usually this is done by very directly mapping what you want to show on your screen to the structure in the database.
Your current data structures allows you to efficiently look up the symptoms (and other data) for a given medicine. That's great. It does however not allow you to efficiently look up the medicines for a given symptom. To allow that you can for example add a structure that maps each specific symptom back to its medicines:
symptoms
"Neck pain"
-L6hb2...bRb0: true
-L6rW...Fuxkf: true
With this additional structure (known as an inverted index or reverse index) you can now look up the medicines for Neck pain by simple loading /symptoms/Neck pain.
For more on this approach to categorization, see my answer here: Firebase query if child of child contains a value

You can't work with firebase database as relational databases .. instead of what you looking for .. you can create new node like this
medicineSymp
---neckpain
-------medicine1ID
-------medicine1ID
-------medicine1ID
this way you could get all medicines much faster and easier

Related

How do I retrieve firebase data based on the key in a nested child?

So suppose I am building an app that lets users manage trips. When a trip is created , any number of users can be added in it. I want this data to be stored in a single place and then for each user be able to retrieve the trips that that person is included in. My data looks like this:
-trips
-<trip-id>
-title
-budget
-etc
-people
-<person-id>
-name
-uid
-<person-id>
-name
-uid
-<trip-id>
-
-
-
This trip will contain all the trips created by all the users. To show any person their trips, I want to retrieve only the lists that person exists in.
This is what I've tried to do including other similar approaches.
rootReference.child("trips").orderByChild("uid").equalTo(FirebaseAuth.instance.currentUser.uid).addValueEventListener(object:ValueEventListener){
override fun onDataChange(snapshot: DataSnapshot) {
//this should only return the trips that current user exists in.
}
}
I have checked the documentation for searching and filtering on firebase but there is nothing that show filtering based nested keys. One particular example is this. I understand it perfectly. If for example I try to filter my trips based on the main attributes like title, budget, it works, but not when I use an attribute of a nested child.
What other approach can I use to filter based to nested keys or should I structure the data differently? Any help is greatly appreciated.
Firebase Realtime Database queries operate on a flat list of child nodes directly under the path that you query.
So the value you order/filter on has to be at a fixex path under each immediate child node. Since that isn't the case for your uid, you can't query across all trips for the UID of all users of those trips.
You can query across one trip for a UID of a user (and then get back that user), or you can query across all trips for properties of the trip itself, such as its title or budget.
If you want to query across all users on all trips, consider keeping an additional list where you have the UID of the user as the key, and then all their trips under there:
"user_trips": {
"uid1": {
"tripid1": true,
"tripid2": true
},
"uid2": {
"tripid2": true,
"tripid3": true
}
}
Also see:
Firebase Query Double Nested
Firebase query if child of child contains a value
Many to Many relationship in Firebase

Displaying latest into custom Info window by timestamp from Firebase [duplicate]

I am developing an Android chat application in which I need to order the conversation details by the date. My firebase data structure is mentioned below.
Now I want to retrieve and show the data on the latest date on my RecyclerView from Firebase Realtime Database based on timestamp.
I have tried the following approaches.
final DatabaseReference nm =
FirebaseDatabase.getInstance().getReference("Transaction");
Query query = nm.orderByChild("Date").limitToFirst(5);
;
query.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
listData.clear();
if (dataSnapshot.exists()) {
for (DataSnapshot npsnapshot : dataSnapshot.getChildren()) {
Transaction ld = npsnapshot.getValue(Transaction.class);
listData.add(ld);
}
Tadapter = new TransactionAdapter(listData);
rv.setAdapter(Tadapter);
Log.d(TAG, "Total Count" + Tadapter.getItemCount());
}
}
}
I am developing an android chat application in which I need to order the conversation details by the date.
As I see in your screenshot, your Date property is of type String. This means that you cannot call:
.orderByChild("Date")
And expect to behave as it was a Timestamp. When you order String elements, the order that you get is always lexicographically. This means that Strings doesn't consider any sort of numeric values when sorting, especially when it comes to the dates, even if the dates contain numbers, as your first element does:
Date: "30/7/2021"
So using String values when querying your database it's not an option. However, I see you already have a Timestamp property. Maybe on that property, it was supposed to do the ordering. If that was not the case, I suggest you change the type of the Date property from String to Timestamp, as explained in my answer from the following post:
How to save the current date/time when I add new value to Firebase Realtime Database
Now I want to retrieve and show the data on the latest date on my RecyclerView
This means that most likely you need to reverse the order, meaning that all your transactions have to be displayed in your RecyclerView descending. In this case, there are a few options that you have, either on the server or on the client.
Assuming that you have changed the type of your Date property from String to Timestamp, then you can simply consider storing an inverted Timestamp value like this:
Firebase-root
|
--- transactions
|
--- 1
|
--- Date: 1627714194
|
--- invertedDate: -1627714194
See, the invertedDate property holds a negative value. Since by default, the elements are ordered ascending, to be able to order the transaction desecendiong, you should simply use:
Query query = nm.orderByChild("invertedDate").limitToFirst(5);
On the other hand, there are some workarounds that can be made to achieve the same thing on the client, as explained in my answer from the following post:
How to arrange firebase database data in ascending or descending order?
Query query = nm.orderByChild("Date").limitToFirst(5);
Firebase realtime database sorts in ascending order that means those 5 nodes that you'll receive will be the oldest.
I want to retrieve and show the data in latest date
Try using limitToLast instead which will return the last 5 documents after ordering the nodes by Date field i.e. the 5 latest nodes.
Query query = nm.orderByChild("Date").limitToLast(5);
You can read more about that at sorting and filtering data.

How to create index on a child node nested child key?

Let's say my current structure firebase realtime database:
{
chatRooms:{
chat1:{
participants:{
user1-id:true,
user2-id:true
}
participantsCount:2,
lastTimeUpdated: {TIMESTAMP},
chatRoomName:"A chatroom",
maxParticipantsCount:10
}
}
users:{
user1-id:{
name:user1-name,
email:user1-email
}
user2-id:{
name:user2-name,
email:user2-email
}
}
}
I need to display all chatrooms that contains a specific user along with all its information, and my solution now is:
#userId is currently authenticated, firebase user.
ref.child("chats").orderByChild("/participants/"+userId).equalTo(true)
which returns the list of chats containing $userId which equals to true, along with additional information to display to the user. Is there any possible way to change this query AND/OR database structure to support optimal indexing? If I wanted to add indices, it would be something like:
chatrooms:{
participants:{
indexOn:"user1-id","user2-id"
# number of ids grows as participants increase
# Not only that, I will have to add one by one manually.
}
}
How can I add indices and also at the same time do a query in Android that will display all chatrooms that a user belongs to along with their information?
AFAIK, I cannot "loop" over the keys of the child in Android
To loop over the children of a snapshot in Android do:
for (DataSnapshot childSnapshot: dataSnapshot.getChildren()) {
System.out.println(childSnapshot.getKey());
}
Relevant section in the docs: https://firebase.google.com/docs/database/android/lists-of-data#listen_for_value_events
If you're asking how to display the information about each chat room that a user is in, that will take a client-side join like this:
firebase.database().ref("userChatrooms").child(firebase.auth().currentUser.uid).once(function(roomsSnapshot) {
roomsSnapshot.forEach(function(roomKey) {
firebase.database().ref("chatrooms").child(roomKey.key).once(function(roomSnapshot) {
console.log(roomSnapshot.child("name").val());
})
})
})
This type of client-side join is not nearly as slow as most developers expect, since Firebase pipelines the nested requests.

Retriving Data From Firebase In DataSnapshot ( Not In Sequence )

I saved the data into Fire base but when I retrieve it. The data is not in the sequence.
Here Data Is Saved In Accurate Sequence:
But when I retrieve data lost its sequence:
Here is my code for retrieving Data
DatabaseReference ref = FirebaseDatabase.getInstance().getReference("Users").child(Autho_User.getUid()).child("Data");
ref.addListenerForSingleValueEvent(
new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
System.out.println(" Value = "+dataSnapshot.toString());
}
(Printed it just to check values) Data is not in the sequence even I get it through
dataSnapshot.getvalue();
Hope so you got my question. I need the data in sequence
Firebase stores JSON data. By definition the children under a node in JSON are unordered. It is only when the data is displayed or retrieved that it gets an order. So the first screenshot that you show is just the order in which the Firebase Database console display it.
If you want to get the data in a specific order in your application, you need to do two things:
Execute a query that returns the data in that order.
Ensure that your code maintains that order.
The code you shared does neither 1 nor 2, so the order in which the data is printed is anybody's guess. Usually it will be in lexicographical order of the keys, but it is undefined.
To learn how to order/filter data, read the Firebase documentation on ordering and filtering. To learn how to maintain the order of items when you use a ValueEventListener, read the Firebase documentation on listening for value events. When you combine these two, you get:
DatabaseReference ref = FirebaseDatabase.getInstance().getReference("Users").child(Autho_User.getUid()).child("Data");
Query dataOrderedByKey = ref.orderByKey();
dataOrderedByKey.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot childSnapshot: dataSnapshot.getChildren()) {
System.out.println("Key = "+childSnapshot.getKey()+" Value = "+childSnapshot.toString());
}
}
...
This is an incredibly common mistake/question, so here are some previous questions for reference:
How to Convert Firebase data to Java Object...?
Ordering of data with Firebase Android
Firebase returning keys of child node in different orders on different devices/Android versions
How to sort by children key value in firebase?
Firebase .getvalue not in the same order as database
Order by date in negative timestamp is not working in Firebase

Lazy loading on firebase?

I am trying out firebase database and I was checking the recommended way of modeling many to many or one to many relationships.
Say I have this relationship between post and user:
user has many posts.
This is my approach for the model design:
class User{
List<String> postIds;
...
}
class Post{
String userId;
....
}
This is according to the firebase documentation here.
I like the design instead of embedding posts created by a user under user collection like the mongodb style this design is flat; so later time if we want to fetch only users on the system we dont have to pull the posts under the users as well.
But my doubt with this design is even embedding the ids within the posts could be a problem later on; imagine I have 100 users with 1000 posts each. If I want to show list of users I have to pull 100 users which means I have to pull 100,000 post ids.
Is there any lazy loading concept on firebase? ie when I fetch a user entity the postIds should not be loaded automatically; it should be loaded on demand.
There is no "lazy loading" or "load on demand" construct present in Firebase at the moment. All the data under a location is read and transferred on access. One thing you can do is separate out the User and UserInfo to two different branches so you can access users separately without pulling in the excessive data.
class User {
String id;
String name;
String gender etc.
}
class UserInfo {
List<String> postIds;
Other info maintained per user
}
In this way, you can read the users without pulling extra information.
An important rule in Firebase is to have the data as flatten as possible. You can take a look at this post, Structuring your Firebase Data correctly for a Complex App, for a better understanding.
Because there is no way in the Firebase Database to download just one property of each node, the single way to achieve this is to use a new node that hosts all those ids. So if you want an efficient way to verify the existens of an id or to count all the users, download just the list of ids. For good measure you should keep precisely that list of ids in the database.
To get all those ids, you can use this code:
DatabaseReference postRef = FirebaseDatabase.getInstance().getReference().child("Post");
postRef.addValueEventListener(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) {
throw databaseError.toException(); // don't ignore errors
}
});
Hope it helps.

Categories

Resources