I am currently trying to make a friendlist with Firestore. My coding is owrking but not the right way.
So I am have a friendlist in my database which looks like this: Databse pic
So Document is saved under my ID which saves otherIDs from users, who accepted a friend request.
So right now it looks like this in the app: new database picture
It shows me that i have 3 friends, which is correct and when i click on one of the profiles it shows me my friend the rght way.
This is how it should look: here
My important code lokks like this right now:
mFirestore.collection("users").document(usid).collection("friends").addSnapshotListener(new EventListener<QuerySnapshot>() {
#Override
public void onEvent(#Nullable QuerySnapshot queryDocumentSnapshots, #Nullable FirebaseFirestoreException e) {
if (e != null) {
}
for(DocumentChange doc : queryDocumentSnapshots.getDocumentChanges()) {
if (doc.getType() == DocumentChange.Type.ADDED){
String user_id = doc.getDocument().getId();
//Toast.makeText(yourFriends.this, fAuth.getUid(), Toast.LENGTH_SHORT).show();
System.out.println(user_id);
System.out.println(fAuth.getUid());
if (fAuth.getUid().equals(user_id)){
Map map = doc.getDocument().getData();
System.out.println(map.size());
//Toast.makeText(yourFriends.this, map.size(), Toast.LENGTH_SHORT).show();
for(Object friendid : map.values()){
friendid.toString();
Users users = doc.getDocument().toObject(Users.class).withId((String)friendid);
//Toast.makeText(yourFriends.this, (String)friendid, Toast.LENGTH_SHORT).show();
usersList.add(users);
}
}
usersListAdapter.notifyDataSetChanged();
}
}
}
});
So I am getting my friendlist but it does not load the information in my userlist.
Your Database structure is wrong. You have to start one more collection under a user and within that you need to store the documnet and under that store the friends data. After this get that collection to the Listview or Recyclerview It will come.
Related
I want to delete this record ?
Because I don't know what exactly it is, I want to know what it is... I expect that it is a special key for every data I upload it creates it automatically How do I delete and what is this key?
DatabaseReference reference = FirebaseDatabase.getInstance().getReference("poll_post").child(firebaseUser.getUid());
HashMap<String, Object> hashMap = new HashMap<>();
String postid = reference.push().getKey();
hashMap.put("postid", postid);
hashMap.put("time_post", ServerValue.TIMESTAMP);
hashMap.put("stopcomment", checked);
hashMap.put("tv_question", addcomment.getText().toString());
hashMap.put("tvoption1", Answer1.getText().toString());
hashMap.put("tvoption2", Answer2.getText().toString());
hashMap.put("vote1","0");
hashMap.put("vote2", "0");
hashMap.put("publisher", FirebaseAuth.getInstance().getCurrentUser().getUid());
reference.push().setValue(hashMap, new DatabaseReference.CompletionListener() {
#Override
public void onComplete(DatabaseError databaseError, DatabaseReference databaseReference) {
//Problem with saving the data
if (databaseError != null) {
Toast.makeText(Write_poll.this, "Error", Toast.LENGTH_SHORT).show();
myLoadingButton.showErrorButton();
} else {
myLoadingButton.showDoneButton();
finish();
}
}
});
refer to firebase documentation and read about the function called push , they clearly said that
push : Add to a list of data in the database. Every time you push a new node onto a list, your database generates a unique key, like messages/users//
so that's means that every time you are pushing to the database , a new record with a unique ID will be generated every you push and you will not be able to override a record using the function push as every time it's called , it will generate a unique ID that you don' want in your case.
to avoid that , instead of
reference.push().setValue(hashMap, new DatabaseReference.CompletionListener(){ . . . }
write :
reference.setValue(hashMap, new DatabaseReference.CompletionListener(){ . . . }
If you want to delete a record from the Realtime Database, you have to create a reference that points to that node. In your particular case it would be:
String uid = FirebaseAuth.getInstance().getCurrentUser().getUid();
DatabaseReference db = FirebaseDatabase.getInstance().getReference();
db.child("poll_post").child("-NBcO...y8cX").removeValue();
If you don't know the key, then you have to create a query to find that post based on something that uniquely identifies it, for example, the time_post. So here is the code:
DatabaseReference db = FirebaseDatabase.getInstance().getReference();
Query queryByTime = db.child("poll_post").orderByChild("time_post").equalTo(1662830174410);
queryByTime.get().addOnCompleteListener(new OnCompleteListener<DataSnapshot>() {
#Override
public void onComplete(#NonNull Task<DataSnapshot> task) {
if (task.isSuccessful()) {
for (DataSnapshot ds : task.getResult().getChildren()) {
ds.getRef().removeValue();
}
} else {
Log.d("TAG", task.getException().getMessage()); //Never ignore potential errors!
}
}
});
You can also attach a listener to the removeValue() operation to see if something goes wrong.
In our app we are trying to fetch all the products and by iterating each product trying to get specificationId which is equal to another documentId in seperate collection Specifications.
Basically the structure is like below:
ProductDetail
productId
productTitle
productDescription
specificationId
Specification
documentId — this is autogenerated id for collection which is being used as specificationId in ProductDetail collection.
MainActivity.java
Query productResponseQuery = FirebaseFirestore.getInstance()
.collection("productdetails")
.limit(50);
productResponseQuery.addSnapshotListener(new EventListener<QuerySnapshot>() {
#RequiresApi(api = Build.VERSION_CODES.N)
#Override
public void onEvent(#Nullable QuerySnapshot querySnapshot, #Nullable FirebaseFirestoreException error) {
if (error != null) {
Log.d(TAG, "Error getting documents: " + error.getMessage());
return;
}
querySnapshot.forEach(doc -> {
ProductDetailResponse productDetailResponse = new ProductDetailResponse();
productDetailResponse.setProductImage(doc.getData().get("productImageUrl").toString());
productDetailResponse.setProductTitle(doc.getData().get("productTitle").toString());
productDetailResponse.setProductShortDesc(doc.getData().get("productShortDesc").toString());
productDetailResponse.setProductDesc(doc.getData().get("productDescription").toString());
populateSpecification(doc.getData().get("specId").toString());
});
}
});
private void populateSpecification(String specId){
DocumentReference specDocumentRef = FirebaseFirestore.getInstance()
.collection("specifications")
.document(specId);
specDocumentRef.get().addOnCompleteListener(new OnCompleteListener<DocumentSnapshot>() {
#Override
public void onComplete(#NonNull Task<DocumentSnapshot> task) {
if(task.getResult().exists()){
task.getResult();
}else{
task.getException();
}
}
});
}
I understand that the Firestore database calls are asynchronous in nature. Hence if I call the specificationQuery inside the for loop, without waiting for the result it is jumping to the next iteration.
Is there any way without changing the structure I can achieve the result, which will basically show all the product details along with their respective specification collection?
I have a Firestore database with the following structure in a hierarchical form:
collection("notes") > document(currentUserId) > collection("mynotes") > document(auto-generated-key) > items...
I have added data to the Firestore as follows :
mAuth = FirebaseAuth.getInstance();
database = FirebaseFirestore.getInstance();
//here scheduleModel is a model class with constructor(String, boolean, String) with getters and
//setters for three of them
scheduleNoteModel = new ScheduleNote(noteTitle, isImp, strDate);
I have added the note item like this.
database.collection("notes").document(mAuth.getUid())
.collection("mynotes")
.document() //generated id automatically...
.set(scheduleNoteModel)
.addOnSuccessListener(new OnSuccessListener<Void>() {
#Override
public void onSuccess(Void aVoid) {
Toast.makeText(getContext(), "data added ...", Toast.LENGTH_SHORT).show();
etNoteTitle.setText("");
//dismiss (hide) the bottom sheet after adding item
BottomSheetScheduleFragment.this.dismiss();
//refresh the fragment
}
});
The problem is getting that id back while deleting the item. I know there are many questions similar to this, some of them were good but I couldn't figure out to solve them. I had a look at this link solution but I wasn't able to solve
In adapter
holder.deleteNoteIcon.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
deleteNote(noteModel);
}
});
Method to delete
private void deleteNote(ScheduleNote note, int itemPosition, View view) {
mAuth = FirebaseAuth.getInstance();
database = FirebaseFirestore.getInstance();
//getting the doc id of auto-generated code in Firestore.
String id = database.collection("notes").document(mAuth.getUid()).collection("mynotes").document().getId();
Toast.makeText(context, "position " + itemPosition + "doc id " + id, Toast.LENGTH_SHORT).show();
database.collection("notes").document(mAuth.getUid())
.collection("mynotes")
//.document("72NMkKY73CXHVN7DFE8W") get that id automatically
.document(id)
.delete()
.addOnSuccessListener(new OnSuccessListener<Void>() {
#Override
public void onSuccess(Void aVoid) {
//notifyDataSetChanged();
Snackbar snackbar = Snackbar.make(view, "Note Deleted successfully", Snackbar.LENGTH_SHORT);
snackbar.show();
}
});
}
id in the above variable isn't matching the real id. How to get that id?
I basically want two information for this :
How to get the document Id before adding it to the Firestore so that I can attach this to my model class and later on delete based on that ID, has any solution?
Or, just get the document Id by the Model Class that I pass when clicking on the item in RecyclerView?
I actually did a lot of research but couldn't figure it out. Any quick help would be appreciated, thanks.
It doesn't seem to mee, that you are following this link solution. First of all, as also #Frank van Puffelen mentioned in his comment, "document().getId()" will always generate a brand new ID each time is called. So you should save that ID into a variable for later use, so you can delete the desired document right from the adapter. Your "ScheduleNote" class, besides the "noteTitle", "isImp", and "strDate" fields, should also contain a field called "id". So your declaration class should look like this:
class ScheduleNote {
public String noteTitle, strDate, id;
public boolean isImp;
public ScheduleNote() {}
public ScheduleNote(String noteTitle, String strDate, String id, boolean isImp) {
this.noteTitle = noteTitle;
this.strDate = strDate;
this.id = id;
this.isImp = isImp;
}
}
Now, in your particular case, the following lines of code for adding the object to Firestore will do the trick:
CollectionRefference mynotes = database.collection("notes").document(mAuth.getUid())
.collection("mynotes")
String docId = mynotes.document().getId();
To create an object of "ScheduleNote" type, please use:
ScheduleNote scheduleNoteModel = new ScheduleNote(noteTitle, strDate, docId, isImp);
// ^
// newly added
To actually write the data to Firestore, please use:
mynotes.document(docId).set(scheduleNoteModel).addOnSuccessListener(/* ... */);
// ^
To be able to delete that document, you should use the following lines of code:
database.collection("notes").document(mAuth.getUid())
.collection("mynotes")
.document(id)
.delete(note.id)
.addOnSuccessListener(/* ... */);
And this is because, the following line generates again a new ID, which is not correct, as you need to use the one that was generated earlier:
String id = database.collection("notes").document(mAuth.getUid()).collection("mynotes").document().getId();
First of all add document to your collection without any data in it.
DocumentReference addedDocRef =database.collection("notes").document(mAuth.getUid())
.collection("mynotes")
.document();
log.i("Added document with ID: " + addedDocRef.getId());
Then add document id to your model class for later use.
Now, set data for document using your model class.
addedDocRef.set(scheduleNoteModel)
I'm trying to get all of the document ids in a collection groups. The document id is the group name which is unique. Also each document in groups does not have fields, only pointers to other collections. I wrote the following code:
fireDB.collection("groups").get().addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
#Override
public void onComplete(#NonNull Task<QuerySnapshot> task) {
if (task.isSuccessful()) {
QuerySnapshot querySnapshots = task.getResult();
if (querySnapshots != null) {
for (QueryDocumentSnapshot currentDocumentSnapshot : querySnapshots) {
groups.add(currentDocumentSnapshot.getId());
}
Collections.sort(groups);
ArrayAdapter<String> adapter = new ArrayAdapter<>(SignUpActivity.this,android.R.layout.simple_list_item_1,groups);
groupNameText.setAdapter(adapter);
} else {
Log.d(this.getClass().getName(), "No groups in the database");
}
} else {
Log.d(this.getClass().getName(), "addOnCompleteListener:failed");
}
}
});
But groups is always empty because firebase does not give me documents without fields (figured it out after some debugging). How should I do it?
I sounds like you have some subcollections under document paths, without having a document at that path. This is a valid situation, but I don't think it is possible to get those locations in the client-side SDKs, as there is no document there.
See https://www.reddit.com/r/Firebase/comments/b0poug/empty_virtual_docs_this_document_does_not_exist/
The Firebase console shows these locations in italics, since it needs to show the subcollections under each location. It likely uses the show_missing flag in the REST API or Admin SDK for that.
My problem is this instead of adding new data, the existing data in the Firestore Database gets updated. This leads to the display of the last message that is either sent or received in the ChatActivity. Also the message I send appears twice on the screen after sending but once I leave the activity and open it again as i just stated, only the last message that is either sent or received in the ChatActivity is displayed. After a lot of scouring on the internet, and testing out various alternatives on my own and failing to resolve this issue, I now come to this community for help.
I have posted below the methods I am using to send my message from the app to the database and the method to then display those messages.
send message method
private void sendMessage(String sender,String receiver,String message){
DocumentReference documentReference = rootRef.collection("chats").document(roomId).collection("messages").document(roomId);
Map<String,Object> user = new HashMap<>();
user.put("sender",sender);
user.put("receiver",receiver);
user.put("message",message);
user.put("time", FieldValue.serverTimestamp());
user.put("rid",roomId);
documentReference.set(user,SetOptions.merge()).addOnSuccessListener(new OnSuccessListener<Void>() {
#Override
public void onSuccess(Void aVoid) {
Log.d(TAG, "onSuccess: MessageSent "+ userId);
}
}).addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
Log.d(TAG, "onSuccess: Error"+ userId);
}
});
}
get message method
private void readMessages(final String userId, final String recipientId){
mchat = new ArrayList<>();
CollectionReference collectionReference = rootRef.collection("chats").document(roomId).collection("messages");
collectionReference.orderBy("time", Query.Direction.DESCENDING);
collectionReference.addSnapshotListener(new EventListener<QuerySnapshot>() {
#Override
public void onEvent(#Nullable QuerySnapshot documentSnapshots, #Nullable FirebaseFirestoreException e) {
if (e != null) {
Log.e(TAG, "onEvent: Listen failed.", e);
return;
}
if(documentSnapshots!=null){
for(QueryDocumentSnapshot queryDocumentSnapshots : documentSnapshots){
Chat chat = queryDocumentSnapshots.toObject(Chat.class);
if(chat.getReceiver().equals(recipientId)&&chat.getSender().equals(userId)||
chat.getReceiver().equals(userId)&&chat.getSender().equals(recipientId)){
mchat.add(chat);
}
messageAdapter = new MessageAdapter(MessageActivity.this,mchat);
recyclerView.setAdapter(messageAdapter);
}
}
}
});
}
I think the new messages are overriding the old messages because of this line:
DocumentReference documentReference = rootRef.collection("chats").document(roomId).collection("messages").document(roomId);
I believe you need to make the last .document() call have something other than roomId for the value because if the roomId stays the same then you will never be able to have more than one message in the same messages collection. Maybe change the roomId to messageId.
For reading problem see #Frank van Puffelen answer
Since you're attaching your listener with addSnapshotListener, your onEvent method will get called once when you attach the listener and then each time the data it listens to gets modified. So when a message is added, your onEvent gets called again.
Each time the onEvent runs, you read all messages from the database and add them to your view. So the first time that is correct, as you add all initial messages. But on the subsequent calls, you're re-adding the same messages that you already processed over and over.
You have two main options to solve this:
Wipe the existing messages before processing
Handle only the updates
Wiping the existing messages is the simplest, as all you have to do is clear `` at the start of onEvent:
private void readMessages(final String userId, final String recipientId){
mchat = new ArrayList<>();
CollectionReference collectionReference = rootRef.collection("chats").document(roomId).collection("messages");
collectionReference.orderBy("time", Query.Direction.DESCENDING).addSnapshotListener(new EventListener<QuerySnapshot>() {
#Override
public void onEvent(#Nullable QuerySnapshot documentSnapshots, #Nullable FirebaseFirestoreException e) {
if (e != null) {
Log.e(TAG, "onEvent: Listen failed.", e);
return;
}
mChat.clear();
if(documentSnapshots!=null){
for(QueryDocumentSnapshot queryDocumentSnapshots : documentSnapshots){
Chat chat = queryDocumentSnapshots.toObject(Chat.class);
if(chat.getReceiver().equals(recipientId)&&chat.getSender().equals(userId)||
chat.getReceiver().equals(userId)&&chat.getSender().equals(recipientId)){
mchat.add(chat);
}
messageAdapter = new MessageAdapter(MessageActivity.this,mchat);
recyclerView.setAdapter(messageAdapter);
}
}
}
});
}
Note that I also changed where the .orderBy("time", Query.Direction.DESCENDING) call is. Each call to orderBy (and most other query building methods) returns a new object, so you have to chain the calls.
Handling only updates is a bit more involved, but will be more efficient. This means that when you have a lot of documents, it has less chances of flicker in your UI.
I highly recommend reading handling changes between snapshots in the documentation. For the most simple case where you only add new documents to the collection, you could get by with:
private void readMessages(final String userId, final String recipientId){
mchat = new ArrayList<>();
CollectionReference collectionReference = rootRef.collection("chats").document(roomId).collection("messages");
collectionReference.orderBy("time", Query.Direction.DESCENDING).addSnapshotListener(new EventListener<QuerySnapshot>() {
#Override
public void onEvent(#Nullable QuerySnapshot documentSnapshots, #Nullable FirebaseFirestoreException e) {
if (e != null) {
Log.e(TAG, "onEvent: Listen failed.", e);
return;
}
mChat.clear();
if(documentSnapshots!=null){
for(QueryDocumentSnapshot queryDocumentSnapshots : documentSnapshots.getDocumentChanges()){ // first change is here
switch (dc.getType()) {
case ADDED:
Chat chat = queryDocumentSnapshots.toObject(Chat.class);
if(chat.getReceiver().equals(recipientId)&&chat.getSender().equals(userId)||
chat.getReceiver().equals(userId)&&chat.getSender().equals(recipientId)){
mchat.add(chat);
}
break;
case MODIFIED:
Log.d(TAG, "Modified city: " + dc.getDocument().getData());
break;
case REMOVED:
Log.d(TAG, "Removed city: " + dc.getDocument().getData());
break;
}
messageAdapter = new MessageAdapter(MessageActivity.this,mchat);
recyclerView.setAdapter(messageAdapter);
}
}
}
});
}
For a more complete solution, you'll want to:
Handle the MODIFIED and REMOVED events too, by updating and removing the existing messages in/from mchat.
Only call new MessageAdapter(MessageActivity.this,mchat) once, and on subsequent updates call adapter.notifyDataSetChanged to tell it of the changes.