My main goal is to set a RecyclerView item as "favorited" based on a user's previous actions. I'm able to store and delete this data on click, but I'm having difficulty showing it in the right place at the right time.
I have two different nodes that I'm using for this to happen:
"quotes" : {
"0" : {
"Name" : "Foo",
"Source" : "Bar" },
"1" : {
"Name" : "Foo",
"Source" : "Bar" },
"2" : {
"Name" : "Foo",
"Source" : "Bar" }
},
"favorites" : {
"blah#blah,com" : {
"uid0" : "0"
"uid1" : "2" }}}
So basically what I'm trying to do:
Show all the quotes in a RecyclerView, and if their ids show up in the favorites for that unique user, set it visually as favorited. I've included some code here that doesn't seem to be working for me.
private void bindHeart(final ItemHeartCardBinding binding) {
if (inProgress) {
animateProgress(binding);
} else {
binding.favorite.setImageResource(R.drawable.favorite_state_list);
}
//Loop through users favorites to see if current item exists
FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser();
DatabaseReference mRootRef = FirebaseDatabase.getInstance().getReference();
String email = "";
if (user != null) {
email = user.getEmail();
email = email.replace(".", ",");
}
final DatabaseReference favoriteRef = mRootRef.child("favorites/" + email);
//quoteKeyStringResId is passed in here as each RecyclerView item is being created. It's the uid of each quote.
final Query queryRef = favoriteRef.orderByValue().startAt(quoteKeyStringResId).endAt(quoteKeyStringResId);
queryRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if (dataSnapshot.getValue().toString().equals(quoteKeyStringResId)) {
binding.favorite.setChecked(checked);
} else {
binding.favorite.setChecked(!checked);
}
}
I dug through the Firebase database samples and found a better way to model my data. Including all the relevant data in the same node allows me to query all at the same time and not have to worry about keeping RecyclerView items in the same order. Things aren't as shallow in this way, but I think that's okay:
"quotes" : {
"0" : {
"Name" : "Foo",
"Source" : "Bar",
//Moved this from a separate node into here
"Favorites" : {
//Each user will be listed if favorited, and removed if unfavorited, making it easy to track and get totals
"userUid" : "true",
"userUid4" : "true" }},
"1" : {
"Name" : "Foo",
"Source" : "Bar" },
"2" : {
"Name" : "Foo",
"Source" : "Bar",
"Favorites" : {
"userUid" : "true" }
}}
Related
I want to retrieve only that post where postCategory matches the user interest.
How can I achieve this type of query. Any Help??
Below is my json file of database..
"Posts" : {
"-MAKobGjdhMfJFwZ1kol" : {
"postCategory" : "Animals",
"postDesc" : "still",
"postImage" : "Image Link",
"postPersonName" : "Name",
"postPersonUid" : "CLQA22uZpLS1ErIrnoBTrC3xqBw2",
"postTitle" : "ek"
},
"Users" : {
"CLQA22uZpLS1ErIrnoBTrC3xqBw2" : {
"email" : "email",
"interest" : {
"-MA14zFcNfs1JAjux129" : {
"name" : "Developer"
},
"-MA14zFgzhL-MOBB7D6h" : {
"name" : "Shopping"
}
},
"name" : "Name",
"uid" : "CLQA22uZpLS1ErIrnoBTrC3xqBw2"
}
In firebase database you cannot have such queries.
You should use Firestore (https://firebase.google.com/docs/firestore) for such advanced queries.
firestore/query-data
firestore
To get all posts with postCategory equal to Animals, you'd do:
DatabaseReference ref = FirebaseDatabase.getInstance().getReference("Posts");
Query query = ref.orderBy("postCategory").equalTo("Animals");
query.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot postSnapshot: dataSnapshot.getChildren()) {
Log.i(TAG, postSnapshot.getKey()+": "+postSnapshot.child("postTitle").getValue(String.class));
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
throw databaseError.toException();
}
}
If you want to retrieve posts for multiple categories, you'll have to perform a separate query for each of those and merge the results on your client.
I'd also recommend changing how you store the categories for a user. The idiomatic way is:
"Users" : {
"CLQA22uZpLS1ErIrnoBTrC3xqBw2" : {
"email" : "email",
"interest" : {
"Developer": true,
"Shopping": true
}
},
The reasons this is more common is that it automatically ensures that each value can occur only once in the interest object, since property names are by definition unique.
I want to retrieve data from the database and insert it into a recycleview.
The problem is that I don't know who I can get to the data inside the dataset.
First, take a look at my dataset structure: Json added below:
"Places" : {
"-M11-OIIHbKTVqCJ-Swv" : {
"name" : "Farmacia Pagani Dr. Roberto",
"others" : {
"01" : {
"Nome" : "Ambilify",
"Price" : 100
},
"02" : {
"Nome" : "Acetaminophoen",
"Price" : 120
}
}
},
"-M11-OJJCGAPdJUknLXX" : {
"name" : "FARMACIA DR. TUMIATTI MARIANO",
"others" : {
"01" : {
"Nome" : "Ambilify",
"Price" : 100
},
"02" : {
"Nome" : "Acetaminophen",
"Price" : 120
}
}
}
},
So what I want is: since I have the key "-M11-OIIHbKTVqCJ-Swv" I want to perform a search inside the object with this key. To do so I need to access Places/key object(-M11-OIIHbKTVqCJ-Swv)/others and then to perform a search based on Nome(means name in Italian). I want to return Nome: Ambilify Price : 100
This is what I did :
private void firebaseUserSearch(String searchText) {
DatabaseReference searchforData = FirebaseDatabase.getInstance().getReference("Places");
Toast.makeText(FirebaseSearch.this, "Started Search", Toast.LENGTH_LONG).show();
System.out.println("keyNameOfFarmacy"+keyNameOfFarmacy);
Query firebaseSearchQuery = searchforData.orderByChild(keyNameOfFarmacy).orderByChild("others").orderByChild("Nome").startAt(searchText).endAt(searchText + "\uf8ff");
FirebaseRecyclerAdapter<DataDetails, UsersViewHolder> firebaseRecyclerAdapter = new FirebaseRecyclerAdapter<DataDetails, UsersViewHolder>(
DataDetails.class,
R.layout.list_layout,
UsersViewHolder.class,
firebaseSearchQuery
) {
#Override
protected void populateViewHolder(UsersViewHolder viewHolder, DataDetails model, int i) {
viewHolder.getDetails(model.getNome(), model.getPrice());
viewHolder.setDetails(model.getNome(), model.getPrice());
}
};
mResultList.setAdapter(firebaseRecyclerAdapter);
}
I have a problem with this part of the code because: orderByChild can be used just once.
Can anyone tell me how can I get the data please, I lost the last 2 3 h searching how to do it and I didn't find anything. Thank you in advance.
Change this:
Query firebaseSearchQuery = searchforData.orderByChild(keyNameOfFarmacy).orderByChild("others").orderByChild("Nome").startAt(searchText).endAt(searchText + "\uf8ff");
into this:
Query firebaseSearchQuery = searchforData.child(keyNameOfFarmacy).child("others").orderByChild("Nome").startAt(searchText).endAt(searchText + "\uf8ff");
You already have access to others and to the key so just use child for that and orderByChild for Nome.
when I use OrderByChild or key with startAt or equalTo it returns wrong value
This is with equalTo
String diy = intent.getStringExtra(PARCELABLE_KEY);
Query qr = dr.child(AddDIYFragment.DIY_CHILD).orderByChild("title").equalTo(diy);
This is with startAt
Query searchQueryNum2 = reference.child(AddDIYFragment.DIY_CHILD).orderByChild("title").startAt(text);
searchQueryNum2.addListenerForSingleValueEvent(this);
I try orderByKey but it also doesnt work
EDIT JSON TREE EXAMPLE(EDited but I cand add more)
{
"gg0" : {
"category" : "lifestyle",
"eq" : "SMTH",
"title" : "gg"
},
"hdhshs5" : {
"category" : "Sports",
"eq" : "hshsj",
"title" : "hdhshs"
},
"hdhshsjsjs6" : {
"category" : "Sports",
"eq" : "eooe",
"title" : "hdhshsjsjs"
},
"java1" : {
"category" : "Sports",
"eq" : "hshjs",
"title" : "java"
},
"jshsjs7" : {
"category" : "Sports",
"eq" : "hdhshsjjs",
"title" : "jshsjs"
},
"jsjsjsjs9" : {
"category" : "Sports",
"eq" : "hshshwhw",
"title" : "jsjsjsjs"
},
"papajs8" : {
"category" : "Sports",
"eq" : "hdhshhajasjjs",
"title" : "papajs"
},
"sjs4" : {
"category" : "Sports",
"eq" : "hdhs",
"title" : "sjs"
}
}
EDIT onDataChange:-
this is a simple startAt
Query searchQueryNum2 = reference.child(AddDIYFragment.DIY_CHILD).orderByChild("title").startAt("gg");
Log.d(TAG, "query is made");
searchQueryNum2.addListenerForSingleValueEvent(this);
And onDataChange
private ArrayList<Prod> list = new ArrayList<>();
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
for (DataSnapshot temp : dataSnapshot.getChildren()) {
list.add(temp.getValue(Prod.class));
Log.d(TAG, "list added something " + list.get(list.size() - 1));
}
}
EDIT
I set the child to be orderd by startat "gg" and as you see in the top we have gg but it return also (hdhshs,java,jsjsjsjs,papajs ,sjs, etc)
Your query is:
...orderByChild("title").startAt("gg")
This is executed by Firebase in the following way:
Order all children of the reference by their title.
Find the child whose title start with gg, or the first one after that if no child starts with gg.
Start returning results from there, until we run out of results.
I think you're looking for a startsWith(...) operator, which Firebase doesn't natively have, but can easily be created by combining startAt() and endAt().
...orderByChild("title").startAt("gg").endAt("gg\uf8ff")
With this, you're adding an extra step to the recipe above:
Stop returning results once we reach a title that is greater than gg\uf8ff.
Let's consider a "UserBean" with a property "emails" that is an ArrayList<EmailBean>.
When saved in Firebase it's like :
"users" : {
"$userID" : {
"emails" : [
"0" : {
"type" : "first",
"address" : "google#google.com",
"private" : true
},
"1" : {
"type" : "second",
"address" : "firebase#firebase.com",
"private" : false"
}
],
"name" : "Mitch"
}
}
So is it possible to achieve something like this :
"users" : {
"$userID" : {
"emails" : [
"first" : {
"address" : "google#google.com",
"private" : true
},
"second" : {
"address" : "firebase#firebase.com",
"private" : false
}
],
"name" : "Mitch"
}
}
I would prefer to not having to save each address one at a time.
Maybe there's an annotation to tells Firebase to use this property as a key ?
Regards.
I recomend you using the code below:
DatabaseReference rootRef = FirebaseDatabase.getInstance().getReference();
DatabaseReference emailsRef = rootRef.child("users").child(userId).child("emails");
ValueEventListener eventListener = new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for(DataSnapshot ds : dataSnapshot.getChildren()) {
String type = ds.child("type").getValue(String.class);
ds.child("type").getRef().getParent().setValue(type);
}
}
#Override
public void onCancelled(DatabaseError databaseError) {}
};
emailsRef.addListenerForSingleValueEvent(eventListener);
What have i tried with this code? So in order to achieve what you want, first of all we got the value of your type key. Then we get a step up in the tree and set the partent with the value of your type key for each particular node.
Having the value of type in stead of 0, 1 and so on, to get the work done, just remove the type key, from each node using removeValue() method directly on the reference.
I am creating an android app and I am stuck at a problem. In order to explain the problem, I would like to show my database structure
{
"EXlzg1COUbOhQjwPCGbS1NRdp5H3" : {
"Contacts" : {
"Contact1" : value (Contact number)
"Contact2" : value
"Contact3" : value
},
"name" : "Sagar Khan",
"phone" : 7276273667
},
"OLm7VWsMcGQpterECyhJ8YTSPna2" : {
"Contacts" : {
"Contact1" : value
"Contact2" : value
"Contact3" : value
},
"name" : "Sameer",
"phone" : 8412914728
},
"TXanCkqtB5PdEogtv8Dzw8y1ngw1" : {
""Contacts" : {
"Contact1" : value
"Contact2" : value
"Contact3" : value
},
"name" : "Harish",
"phone" : 7020743544
},
"qnDVoc72nXa8XvOH1L39VvqFzKL2" : {
"Contacts" : {
"Contact1" : value
"Contact2" : value
"Contact3" : value
},
"name" : "Harish Shinde"
"phone": 8149870822
}
}
This is very short structure the actual one is to big
Now what I want to do is I want to fetch only those users whose contact number is present in current user node and display those users in a Android list.
For example:
User 1 with id EXlzg1COUbOhQjwPCGbS1NRdp5H3 is having 3 contacts
"EXlzg1COUbOhQjwPCGbS1NRdp5H3" : {
"Contacts" : {
"Sameer" : 8412914728 (Contact number)
"Contact2" : value
"Contact3" : value
},
"name" : "Sagar Khan",
"phone" : 7276273667
},
Now when I will fetch the list of users from my database and show it in my Android app list, I want only those users to be added whose contact number is present in User 1 contacts.
My problem is that I literally don't know how to do this as I am new to Android development and Firebase. I am clear with the read and write basics of Firebase, but such operations, I have no idea how to do it.
I have searched a lot, but cannot find any solutions or examples. A detailed description will be very good for me. ;) Thanks in advance.
Here's the change I propose:
This is the new database structure for Users node:
"Users" : {
"EXlzg1COUbOhQjwPCGbS1NRdp5H3" : {
"name" : "Sagar Khan",
"phone" : 7276273667
},
..
}
And the contacts of the user "Sagar Khan" will be stored in another node called Contacts under that user's UserID (EXlzg1COUbOhQjwPCGbS1NRdp5H3) :
"Contacts" : {
"EXlzg1COUbOhQjwPCGbS1NRdp5H3" : {
"Contact1":{
"name" : "Sameer",
"phone" : 7276273000
},
"Contact2":{
"name" : "Alice",
"phone" : 7276273600
},
...
},
//other user id with their contacts
...
}
I suspect that the user ID (eg: EXlzg1COUbOhQjwPCGbS1NRdp5H3) is taken from a logged in user, using:
mAuth.getCurrentUser().getUid();
Therefore, to get the contacts of a logged in user you can query the database like this:
DatabaseReference mUserContactsRef = FirebaseDatabase.getInstance().getReference().child("Contacts").child(mAuth.getCurrentUser().getUid(););
mUserContactsRef.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for(DataSnapshot userContact : dataSnapshot.getChildren()){
Contact contact = userContact.getValue(Contact.class);
//add each contact to an arraylist and use it to populate a list of contacts
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
You can add each contact object to an ArrayList and use it to populate a list. The key is the User ID, you can access anything related to a user using the UserID.
This is the approach I recommend and what I have used in the past successfully for similar scenarios.