Firebase Firestore get data from collection - java

I want to get data from my Firebase Firestore database. I have a collection called user and every user has collection of some objects of the same type (My Java custom object). I want to fill my ArrayList with these objects when my Activity is created.
private static ArrayList<Type> mArrayList = new ArrayList<>();;
In onCreate():
getListItems();
Log.d(TAG, "onCreate: LIST IN ONCREATE = " + mArrayList);
*// it logs empty list here
Method called to get items to list:
private void getListItems() {
mFirebaseFirestore.collection("some collection").get()
.addOnSuccessListener(new OnSuccessListener<QuerySnapshot>() {
#Override
public void onSuccess(QuerySnapshot documentSnapshots) {
if (documentSnapshots.isEmpty()) {
Log.d(TAG, "onSuccess: LIST EMPTY");
return;
} else {
for (DocumentSnapshot documentSnapshot : documentSnapshots) {
if (documentSnapshot.exists()) {
Log.d(TAG, "onSuccess: DOCUMENT" + documentSnapshot.getId() + " ; " + documentSnapshot.getData());
DocumentReference documentReference1 = FirebaseFirestore.getInstance().document("some path");
documentReference1.get().addOnSuccessListener(new OnSuccessListener<DocumentSnapshot>() {
#Override
public void onSuccess(DocumentSnapshot documentSnapshot) {
Type type= documentSnapshot.toObject(Type.class);
Log.d(TAG, "onSuccess: " + type.toString());
mArrayList.add(type);
Log.d(TAG, "onSuccess: " + mArrayList);
/* these logs here display correct data but when
I log it in onCreate() method it's empty*/
}
});
}
}
}
}
}).addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
Toast.makeText(getApplicationContext(), "Error getting data!!!", Toast.LENGTH_LONG).show();
}
});
}

The get() operation returns a Task<> which means it is an asynchronous operation. Calling getListItems() only starts the operation, it does not wait for it to complete, that's why you have to add success and failure listeners.
Although there's not much you can do about the async nature of the operation, you can simplify your code as follows:
private void getListItems() {
mFirebaseFirestore.collection("some collection").get()
.addOnSuccessListener(new OnSuccessListener<QuerySnapshot>() {
#Override
public void onSuccess(QuerySnapshot documentSnapshots) {
if (documentSnapshots.isEmpty()) {
Log.d(TAG, "onSuccess: LIST EMPTY");
return;
} else {
// Convert the whole Query Snapshot to a list
// of objects directly! No need to fetch each
// document.
List<Type> types = documentSnapshots.toObjects(Type.class);
// Add all to your list
mArrayList.addAll(types);
Log.d(TAG, "onSuccess: " + mArrayList);
}
})
.addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
Toast.makeText(getApplicationContext(), "Error getting data!!!", Toast.LENGTH_LONG).show();
}
});
}

Try this..Working fine.Below function will get Realtime Updates from firebse as well..
db = FirebaseFirestore.getInstance();
db.collection("dynamic_menu").addSnapshotListener(new EventListener<QuerySnapshot>() {
#Override
public void onEvent(QuerySnapshot documentSnapshots, FirebaseFirestoreException e) {
if (e !=null)
{
}
for (DocumentChange documentChange : documentSnapshots.getDocumentChanges())
{
String isAttendance = documentChange.getDocument().getData().get("Attendance").toString();
String isCalender = documentChange.getDocument().getData().get("Calender").toString();
String isEnablelocation = documentChange.getDocument().getData().get("Enable Location").toString();
}
}
});
More reference
:https://firebase.google.com/docs/firestore/query-data/listen
If You do not want realtime updates refer Below Document
https://firebase.google.com/docs/firestore/query-data/get-data

Here is a simplified example:
Create a collection "DownloadInfo" in Firebase.
And add a few documents with these fields inside it:
file_name (string),
id (string),
size (number)
Create your class:
public class DownloadInfo {
public String file_name;
public String id;
public Integer size;
}
Code to get list of objects:
FirebaseFirestore db = FirebaseFirestore.getInstance();
db.collection("DownloadInfo")
.get()
.addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
#Override
public void onComplete(#NonNull Task<QuerySnapshot> task) {
if (task.isSuccessful()) {
if (task.getResult() != null) {
List<DownloadInfo> downloadInfoList = task.getResult().toObjects(DownloadInfo.class);
for (DownloadInfo downloadInfo : downloadInfoList) {
doSomething(downloadInfo.file_name, downloadInfo.id, downloadInfo.size);
}
}
}
} else {
Log.w(TAG, "Error getting documents.", task.getException());
}
}
});

db.collection("users").get().then((querySnapshot) => {
querySnapshot.forEach((doc) => {
console.log(`${doc.id} => ${doc.data()}`);
});
source:-
https://firebase.google.com/docs/firestore/quickstart

This is the code to get the list.
Since this is an async task, it takes time that's why the list size shows empty at first.
But including the source for the cache data will enable the previous list(and also its size) to be in memory until next task is performed.
Source source = Source.CACHE;
firebaseFirestore
.collection("collectionname")
.get(source)
.addOnSuccessListener(new OnSuccessListener<QuerySnapshot>() {
#Override
public void onSuccess(QuerySnapshot documentSnapshots) {
if (documentSnapshots.isEmpty()) {
return;
} else {
// Convert the whole Query Snapshot to a list
// of objects directly! No need to fetch each
// document.
List<ModelClass> types = documentSnapshots.toObjects(ModelClass.class);
// Add all to your list
mArrayList.addAll(types);
}
}
})
.addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
}
});

Related

How do I retrieve data under UID for the signed in user with Firebase

I saved some data under a node with the currently signed in user UID, along with the post timestamp.I successfully manage to retrieve all the data under this node, but I would only like to retrieve the data for the currently signed in user, I am trying to create something similar to how amazon stores an item in the cart and when the user clicks the cart it shows the items they have added there, not all the items for every user. Can someone assist me with this problem?
public void addItemToCart(){
SimpleDateFormat formatter = new SimpleDateFormat(" HH:mm:ss ");
Date date = new Date(System.currentTimeMillis());
timeStamp = loggedInUserId + formatter.format(date);
userDictionary.put("itemPrice", selectedPrice);
userDictionary.put("productname",productNameText);
userDictionary.put("description", selectedStringProductDescrtipon);
userDictionary.put("uid",loggedInUserId);
userDictionary.put("productImage", selectedImage);
userDictionary.put("datee",date.toString());
userDictionary.put("name",loggedInUserName);
userDictionary.put("timestamp",timeStamp);
uploadPostRef.child("Cart").child(timeStamp).setValue(userDictionary).addOnSuccessListener(new OnSuccessListener<Void>() {
#Override
public void onSuccess(Void unused) {
Log.i("sucessfully added","sucesssfully added to cart..");
getLoggedInUserData();
}
});
numberInCartIv.setVisibility(View.VISIBLE);
public void downloadCartData(){
cartDb.child("Cart").addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot data : dataSnapshot.getChildren()) {
if (data.exists()) {
Log.i("data",data.toString());
// cartModel = data.getValue(CartModel.class);
// cartModelArrayList.add(cartModel);
// LinearLayoutManager horizontalLayoutManager = new LinearLayoutManager(getContext(), LinearLayoutManager.VERTICAL, false);
// cartRecyleView.setLayoutManager(horizontalLayoutManager);
// cartAdapter = new CartAdapter(cartModelArrayList, getContext());
// cartRecyleView.setAdapter(cartAdapter);
} else {
Log.i("error", "Error Loading JSON data..");
}
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
String error = databaseError.getDetails();
Log.i("error", error);
}
});
}
First of all, you need to change your DB json to this one.
Cart
-userUid
-cartUid
-datee
-description
-andSoOn
That's mean, when you are storing the item into Cart. You need to include the userUid.
//Getting userUid
final FirebaseUser firebaseUser = FirebaseAuth.getInstance().getCurrentUser();
if (firebaseUser != null) {
final String userUid = firebaseUser.getUid();
uploadPostRef.child("Cart").child(userUid).child(timeStamp).setValue(userDictionary).addOnSuccessListener(new OnSuccessListener() {
#Override
public void onSuccess(Void unused) {
Log.i("sucessfully added","sucesssfully added to cart..");
getLoggedInUserData();
}
});
}
To retrieve the Cart according to the user. You just call them like this.
public void downloadCartData(){
cartDb.child("Cart").child(userUid).addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot data : dataSnapshot.getChildren()) {
if (data.exists()) {
Log.i("data",data.toString());
} else {
Log.i("error", "Error Loading JSON data..");
}
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
String error = databaseError.getDetails();
Log.i("error", error);
}
});
}

Selecting in RecyclerView and delete from firebase in java

I want to delete my document from firebase. But first I need to determine the document id. I tried to get document id:
docId = queryDocumentSnapshots.getDocuments().get(pos).getId();
Then, I just wanted to delete my document. But firebase works async so code doesnt work in 'if' statement. When we first click the button, docId variable is null or it takes the docId which was clicked before till the async code part done.
#Override
public void onBindViewHolder(#NonNull AdvertisementHolder holder, int position) {
imgUrl = publishedAdvertisements.get(position).getImgUrl();
holder.petName.setText(publishedAdvertisements.get(position).getPetName());
holder.petCategory.setText(publishedAdvertisements.get(position).getPetCategory());
Picasso.get().load(publishedAdvertisements.get(position).getImgUrl()).into(holder.petImage);
holder.btnDelete.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
firebaseFirestore = FirebaseFirestore.getInstance();
firebaseFirestore.collection("Pets").get().addOnSuccessListener(new OnSuccessListener<QuerySnapshot>() {
#Override
public void onSuccess(#NonNull QuerySnapshot queryDocumentSnapshots) {
if (!queryDocumentSnapshots.isEmpty()) {
System.out.println("bos döndü");
docId = queryDocumentSnapshots.getDocuments().get(pos).getId();
}
}
});
System.out.println(docId);
if (docId != null) {
FirebaseFirestore db = FirebaseFirestore.getInstance();
db.collection("Pets").document(docId)
.delete()
.addOnSuccessListener(new OnSuccessListener<Void>() {
#Override
public void onSuccess(Void aVoid) {
Log.d(TAG, "DocumentSnapshot successfully deleted!");
publishedAdvertisements.clear();
getPublishedAnimals();
}
})
.addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
Log.w(TAG, "Error deleting document", e);
}
});
}
notifyDataSetChanged();
}
});
You should structure your code so that any logic that depends on your asynchronous operation is executed or triggered within the response callback.
You can do something like this:
#Override
public void onBindViewHolder(#NonNull AdvertisementHolder holder, int position) {
imgUrl = publishedAdvertisements.get(position).getImgUrl();
holder.petName.setText(publishedAdvertisements.get(position).getPetName());
holder.petCategory.setText(publishedAdvertisements.get(position).getPetCategory());
Picasso.get().load(publishedAdvertisements.get(position).getImgUrl()).into(holder.petImage);
holder.btnDelete.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
firebaseFirestore = FirebaseFirestore.getInstance();
firebaseFirestore.collection("Pets").get().addOnSuccessListener(new OnSuccessListener<QuerySnapshot>() {
#Override
public void onSuccess(#NonNull QuerySnapshot queryDocumentSnapshots) {
// The asynchronous operation has successfully completed
// and returned a value to our 'onSuccess()' callback.
if (!queryDocumentSnapshots.isEmpty()) {
System.out.println("bos döndü");
docId = queryDocumentSnapshots.getDocuments().get(pos).getId();
System.out.println(docId);
// We can now use the value of docId.
if (docId != null) {
FirebaseFirestore db = FirebaseFirestore.getInstance();
db.collection("Pets").document(docId)
.delete()
.addOnSuccessListener(new OnSuccessListener<Void>() {
#Override
public void onSuccess(Void aVoid) {
Log.d(TAG, "DocumentSnapshot successfully deleted!");
publishedAdvertisements.clear();
getPublishedAnimals();
// (1)
}
})
.addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
Log.w(TAG, "Error deleting document", e);
}
});
}
// I'm not sure how your RecyclerView is set up
// but I'm guessing you might want to move this call
// to 'notifyDataSetChanged()' to the section marked (1)
notifyDataSetChanged();
}
}
});
}
});
}

What is the proper way to set a value listener in Firebase Cloud database?

I wrote the following code:
connectedGroup.collection("users").document(connectedEmail).get()
.addOnSuccessListener(new OnSuccessListener<DocumentSnapshot>() {
#Override
public void onSuccess(DocumentSnapshot documentSnapshot) {
Log.d(this.getClass().getName(), "addOnSuccessListener:success");
if (documentSnapshot.exists()) {
connectedFullName = documentSnapshot.getString("fullName");
connectedImageURL = documentSnapshot.getString("image");
}
setMenuAvatarImage();
setMenuHeaderMessage();
}
}).addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
Log.d(this.getClass().getName(), "addOnSuccessListener:failed");
// Set default values in the menu header
tryRedirectActivity(getIntent());
setMenuAvatarImage();
setMenuHeaderMessage();
}
});
It fetches the user's full name and image and set them in the menu. I want to set a listener to those two fields so every time there is a change in the database, it will update connectedFullName and connectedImageURL and call the setMenuAvatarImage, setMenuHeaderMessage methods. How can I do it?
What you're describing happens when you use Firestore's
connectedGroup.collection("users").document(connectedEmail)
.addSnapshotListener(new EventListener<DocumentSnapshot>() {
#Override
public void onEvent(#Nullable DocumentSnapshot snapshot,
#Nullable FirebaseFirestoreException e) {
if (e != null) {
Log.w(TAG, "Listen failed.", e);
return;
}
if (snapshot != null && snapshot.exists()) {
Log.d(TAG, "Current data: " + snapshot.getData());
... TODO: do other things with the data here...
} else {
Log.d(TAG, "Current data: null");
}
}
});
Immediately after attaching the listener, your onEvent will get called with the current snapshot. And then whenever the document changes, it will get called again with the updated snaspshot.
I recommend studying the documentation in listening for realtime updates, as it covers this and much more.

How to rewrite code correctly with Firebase in cloud Firestore?

I wrote to connect to Firebase and now I want to transfer everything to cloud Firestore
1) the first method is written to get "Comment" from firebase
private void iniRvComment() {
RvComment.setLayoutManager(new LinearLayoutManager(this));
DatabaseReference commentRef = firebaseDatabase.getReference(COMMENT_KEY).child(postKey);
commentRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
listComment = new ArrayList<>();
for(DataSnapshot snapshot: dataSnapshot.getChildren()){
Comment comment = snapshot.getValue(Comment.class);
listComment.add(comment);
}
commentAdapter = new CommentAdapter(getApplicationContext(), listComment);
RvComment.setAdapter(commentAdapter);
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
}
2) and how to rewrite this code to get "Comment" from the cloud firestore. What I wrote below is not correct
private void iniRvComment() {
RwComment.setLayoutManager(new LinearLayoutManager(this));
DocumentReference docRef = firestore.collection("Comment").document(postKey);
docRef.collection("Comment").addSnapshotListener(new EventListener<QuerySnapshot>() {
#Override
public void onEvent(#Nullable QuerySnapshot documentSnapshot, #Nullable FirebaseFirestoreException e) {
if (documentSnapshot != null && !documentSnapshot.getDocuments().isEmpty()) {
listComment = new ArrayList<>();
List<DocumentSnapshot> documents = documentSnapshot.getDocuments();
for (DocumentSnapshot value : documents) {
Comment comment = value.toObject(Comment.class);
listComment.add(comment);
}
commentAdapter = new CommentAdapter(getApplicationContext(), listComment);
RwComment.setAdapter(commentAdapter);
}
}
});
}
I recommend flattenning out your comments into a single, top-level Comments collection, rather than storing them under the post. This will allow you to perform many useful search operations such as searching all comments by user or post, or even creating a "recently active posts" feed.
To achieve, this you will need to change your database structure so that all comments are stored with the post they are attached to.
{
content: "...",
timestamp: 1234534568425,
postId: "...",
uid: "...",
uimg: "...",
uname: "..."
}
Once you have done that, you can now query comments by post using:
private final int PAGE_SIZE = 10;
private void iniRvComment() {
RvComment.setLayoutManager(new LinearLayoutManager(this));
firestore.collection("Comments") // this is a top level collection
.whereEqualTo("postId", postKey) // select comments for given post
.orderBy("timestamp", Query.Direction.DESCENDING) // order newest to oldest
.limit(PAGE_SIZE) // fetch up to PAGE_SIZE recent comments
.addSnapshotListener(new EventListener<QuerySnapshot>() {
#Override
public void onEvent(#Nullable QuerySnapshot results,
#Nullable FirebaseFirestoreException e) {
if (e != null) {
Log.w(TAG, "Listen failed.", e);
return;
}
listComment = new ArrayList<>(PAGE_SIZE);
for (QueryDocumentSnapshot commentDoc : results) {
Comment commentObj = commentDoc.toObject(Comment.class);
listComment.add(commentObj);
}
commentAdapter = new CommentAdapter(getApplicationContext(), listComment);
RvComment.setAdapter(commentAdapter);
Log.d(TAG, "Retrieved " + results.size() + " recent comments.");
}
});
}

Firebase : Firestore : What is the equivalent of ChildEventListener found in Realtime Database

I have this for Firestore.
FirebaseFirestore db = FirebaseFirestore.getInstance();
CollectionReference ref = db.collection("app/appdata/notifications");
ref.addSnapshotListener((snapshot, e) -> {
if (e != null) {
Log.w(TAG, "Listen failed.", e);
return;
}
for (DocumentSnapshot x : snapshot.getDocuments()) {
System.out.println(x.getData());
}
});
But I don't want to use that loop, instead I need to only get the new children. I'd like something like the following as seen in the Realtime Db.
ref.addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String prevChildKey) {
Post newPost = dataSnapshot.getValue(Post.class);
System.out.println("Author: " + newPost.author);
System.out.println("Title: " + newPost.title);
System.out.println("Previous Post ID: " + prevChildKey);
}
#Override
public void onChildChanged(DataSnapshot dataSnapshot, String prevChildKey) {}
#Override
public void onChildRemoved(DataSnapshot dataSnapshot) {}
#Override
public void onChildMoved(DataSnapshot dataSnapshot, String prevChildKey) {}
#Override
public void onCancelled(DatabaseError databaseError) {}
});
You need to use .getDocumentChanges() on the QuerySnapshot object to get the list of changes since the last snapshot. This is equivalent to the child-change events in Realtime Database. For example:
FirebaseFirestore db = FirebaseFirestore.getInstance();
CollectionReference ref = db.collection("app/appdata/notifications");
ref.addSnapshotListener((snapshot, e) -> {
if (e != null) {
Log.w(TAG, "Listen failed.", e);
return;
}
for (DocumentChange dc : snapshots.getDocumentChanges()) {
switch (dc.getType()) {
case ADDED:
// handle added documents...
break;
case MODIFIED:
// handle modified documents...
break;
case REMOVED:
// handle removed documents...
break;
}
}
}
});
See https://firebase.google.com/docs/firestore/query-data/listen#view_changes_between_snapshots for more details.
This is how I achieved it.
First, init a DocumentReference as:
DocumentReference mDocRef = FirebaseFirestore.getInstance().document("yourData/notifications");
Now using your mDocRef, call addSnapshotLisetener() creating new EventListener for the DocumentSnapshot like this:
mDocRef.addSnapshotListener(new EventListener<DocumentSnapshot>() {
#Override
public void onEvent(DocumentSnapshot documentSnapshot, FirebaseFirestoreException e) {
//todo your code
}
});
So this will grab your data first time you set it up and also every time the data gets updated.
Also if you pass your Activity as a context, it will automatically detach when your activity stops.
..addSnapshotListener(this, new EventListener<DocumentSnapshot>()...

Categories