In my app a first time user creates a username which I save in Firestore as a document and set it as displayName (username = name of document = displayName). In the app the user has the option to change his username by typing a new one in an EditText. The result should be that the data which is stored under his current username moves to a new document which has the new username. So first I check if the new username is already in use by someone else, if not I create a new document which name is the new username. Now I get the current displayName (which is the current username) as a string and the input of the EditText as a string (new username). I Implemented a method to move documents from here: How to move a document in Cloud Firestore?
But when I call the method like this moveFirestoreDocument(usernameInput, oldUsername ); I get:
moveFirestoreDocument cannot be applied to (String, String)
How can I resolve this problem? Can I change the moveFirestoreDocument method so it can also takes Strings?
Here is the method:
public void moveFirestoreDocument(DocumentReference fromPath, final DocumentReference toPath) {
fromPath.get().addOnCompleteListener(new OnCompleteListener<DocumentSnapshot>() {
#Override
public void onComplete(#NonNull Task<DocumentSnapshot> task) {
if (task.isSuccessful()) {
DocumentSnapshot document = task.getResult();
if (document != null) {
toPath.set(document.getData())
.addOnSuccessListener(new OnSuccessListener<Void>() {
#Override
public void onSuccess(Void aVoid) {
Log.d(TAG, "DocumentSnapshot successfully written!");
fromPath.delete()
.addOnSuccessListener(new OnSuccessListener<Void>() {
#Override
public void onSuccess(Void aVoid) {
Log.d(TAG, "DocumentSnapshot successfully deleted!");
}
})
.addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
Log.w(TAG, "Error deleting document", e);
}
});
}
})
.addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
Log.w(TAG, "Error writing document", e);
}
});
} else {
Log.d(TAG, "No such document");
}
} else {
Log.d(TAG, "get failed with ", task.getException());
}
}
});
}
You get the following error:
I get "moveFirestoreDocument cannot be applied to (String, String)
Because you are trying to pass to the moveFirestoreDocument() method two arguments of type String and not of type DocumentReference. In order to be able to move a document, both fromPath and toPath must be of type DocumentReference so you can call set() on toPath and get() and delete() on fromPath.
Can I change the moveFirestoreDocument method so it can also takes Strings?
There is no need to change the method, you can simply pass the correct arguments when you call it. According to the details that you have provided in the question, I understand that you have a schema that is similar to this:
Firestore-root
|
--- users (collection)
|
--- John (document)
|
--- //User details
To rename the document call the method using:
FirebaseFirestore rootRef = FirebaseFirestore.getInstance();
CollectionReference usersRef = rootRef.collection("users");
DocumentReference oldUsernameRef = usersRef.document(oldUsername);
DocumentReference usernameInputRef = usersRef.document(usernameInput);
moveFirestoreDocument(oldUsernameRef, usernameInputRef);
Related
I want to push my data (in key:value pair) into my Realtime Database. This is my code:
DatabaseReference pay_details = database.getReference("pay/0/0/");
pay_details.push(
{
"Actual amount":arr.get(i).get(9),
"Current reading":arr.get(i).get(5),
"Employee":arr.get(i).get(1),
"Expected amount":arr.get(i).get(9),
"Installment":arr.get(i).get(10),
"Meter current date":arr.get(i).get(3),
"Meter previous date":arr.get(i).get(2),
"Previous reading":arr.get(i).get(4),
"Start from":arr.get(i).get(8),
"Total charge":arr.get(i).get(7),
"Type":arr.get(i).get(0),
"Unit consumed":arr.get(i).get(6)
}
);
This is my table structure:
I keep getting multiple syntax errors in the push section. Where am I going wrong?
The best way to send/store data in real-time database is to create an object of
Hashmap. Below is the code to achieve this objective
DatabaseReference databaseReference = FirebaseDatabase.getInstance().getReference();
HashMap<String, Object> data = new HashMap<>();
data.put("Actual amount", Your actual amount);
data.put("Current reading", Your current reading);
data.put("Employee", Employee name);
databaseReference.child("The child name")
.updateChildren(data)
.addOnSuccessListener(new OnSuccessListener<Void>() {
#Override
public void onSuccess(Void unused) {
Log.d("TAG", "Data added successfully");
}
}).addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
Log.d("TAG", "Failed " + e.getMessage());
}
});
The add data to the Realtime Database you need to use setValue() method. In code
DatabaseReference pay_details = database.getReference("pay/0/0/");
HashMap<String, Object> map = new HashMap<>();
map.put("fieldName", "fieldValue");
pay_details.setValue(map).addOnCompleteListener(new OnCompleteListener<Void>() {
#Override
public void onComplete(#NonNull Task<Void> task) {
if (task.isSuccessful()) {
Log.d(TAG, "Data added successfully.");
} else {
Log.d(TAG, task.getException().getMessage()); //Never ignore potential errors!
}
}
});
If you want to add multiple children, then you can also use push().
I am working on an application where I have saved my data in firebase Firestore in nested collection now when I am trying to get/retrieve the data from Firestore but not able to get it. please guide me where am I wrong??
CODE TO WRITE/ADD THE DATA IN FIRESTORE
DocumentReference uidRef = firebaseFirestore.collection("listing_details").document(uid);
uidRef.collection("room_details").add(user).addOnSuccessListener(new OnSuccessListener<DocumentReference>() {
#Override
public void onSuccess(DocumentReference documentReference) {
Toast.makeText(getContext(), "data added", Toast.LENGTH_SHORT).show();
}
})
.addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
Toast.makeText(getContext(), "data adding failure", Toast.LENGTH_SHORT).show();
}
});
CODE FOR DATA RETRIEVING
db.collection("listing_details").document().collection("room_details").get().addOnSuccessListener(new OnSuccessListener<QuerySnapshot>() {
#Override
public void onSuccess(QuerySnapshot queryDocumentSnapshots) {
List<DocumentSnapshot> list = queryDocumentSnapshots.getDocuments();
for (DocumentSnapshot d : list)
{
RoomsDetails obj = d.toObject(RoomsDetails.class);
roomsDetails.add(obj);
}
roomsAdapter.notifyDataSetChanged();
}
});
DATA RETRIEVING CODE (UPDATED)
roomDetailsRef.document(doc_id).get().addOnSuccessListener(new OnSuccessListener<DocumentSnapshot>() {
#Override
public void onSuccess(DocumentSnapshot documentSnapshot) {
//GUIDE ME HERE HOW CAN I ITERATE THROUGH IT SIR PLEASE
}
});
Each time you're calling .document() to create the following reference, without passing anything as an argument:
db.collection("listing_details").document().collection("room_details")
// 👆
It means that you're generating a brand new unique document ID. If you want to create a reference that points to a particular document, then you have to pass the particular document ID that already exists in the database to the document() method, and not generate a new one.
So your code should look like this:
//Code to add data to Firestore.
FirebaseFirestore db = FirebaseFirestore.getInstance();
DocumentReference uidRef = db.collection("listing_details").document(uid);
CollectionReference roomDetailsRef = uidRef.collection("room_details");
String docId = roomDetailsRef.document().getId();
roomDetailsRef.document(docId).set(user).addOnSuccessListener(/*.../*);
// 👆
See, I have used DocumentReference#getId() to get the ID of the document, and DocumentReference#set(Object data) to actually add the document to Firestore.
//Code to read data from Firestore.
roomDetailsRef.document(docId).get().addOnSuccessListener(/*.../*);
// 👆
See, I have passed the document ID that was generated earlier, to the CollectionReference#document() method.
Edit:
If you want to get all documents that exist under the room_details collection, then please use the following lines of code:
roomDetailsRef.get().addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
#Override
public void onComplete(#NonNull Task<QuerySnapshot> task) {
if (task.isSuccessful()) {
for (QueryDocumentSnapshot document : task.getResult()) {
if (document != null) {
RoomsDetails obj = document.toObject(RoomsDetails.class);
roomsDetails.add(obj);
}
}
roomsAdapter.notifyDataSetChanged();
} else {
Log.d(TAG, task.getException().getMessage()); //Never ignore potential errors!
}
}
});
I have created a function to add a new user to Firestore Database:
public void addNewUser(String email, String username, String profile_photo){
FirebaseFirestore db = FirebaseFirestore.getInstance(); // this is instantiated here, just to show you
User user = new User(userID, (long) 1, email, StringManipulation.condenseUsername(username), username, profile_photo);
db.collection("Users").add(user)
.addOnSuccessListener(new OnSuccessListener<DocumentReference>() {
#Override
public void onSuccess(DocumentReference documentReference) {
Log.d(TAG, "DocumentSnapshot added with ID: " + documentReference.getId());
}
})
.addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
Log.w(TAG, "Error adding document", e);
}
});
}
And a function to fetch the data from the database, waiting for all the fields to appear in-app.
public UserSettings RetrieveUserSettings(){
Log.d(TAG, "getUserAccountSettings: retrieving user account settings from firebase");
User settings = new User();
DocumentReference userRef;
FirebaseAuth firebaseAuth=FirebaseAuth.getInstance();
try {
userRef = db.collection("Users")
.document(firebaseAuth.getCurrentUser().getUid());
userRef.get()
.addOnSuccessListener(new OnSuccessListener<DocumentSnapshot>() {
#Override
public void onSuccess(DocumentSnapshot documentSnapshot) {
if(documentSnapshot.exists())
{
settings.setDisplay_name(documentSnapshot.getString("display_name"));
settings.setUsername(documentSnapshot.getString("username"));
settings.setProfile_photo(documentSnapshot.getString("profile_photo"));
settings.setEmail(documentSnapshot.getString("email"));
settings.setPhone_number(documentSnapshot.getLong("phone_number"));
settings.setUser_id(documentSnapshot.getString("user_id"));
}
else{
Log.d(TAG, "doc not fount in getUserSettinghs ");
}
}
})
.addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
Log.d(TAG, "onFailure: failed to fetch data");
}
});
} catch (NullPointerException e) {
Log.d(TAG, "getUserAccountSettings: NULLPointerException: " + e.getMessage());
}
Log.e(TAG, "getUserAccountSettings: retrieved user_account_settings information: " + settings.toString());
return new UserSettings(settings);
}
When I authenticate, this is the User UID that is generated: E5bBL0D9lCSKy5a0YdUJjuMVuUT2, but when I call the addNewUser function, my Firestore Database looks like this :
, so it is obvious that the User UID generated at authentication is not the same with the document id, which is :
XWwnRBklieJXSxokQuNe.Do you have any idea why this is happening?
As #HenryTwist mentioned in his comment, when using the following line of code:
db.collection("Users").add(user).addOnSuccessListener(/* ... /*);
You are adding a new "User" object to the "Users" collection using a random ID. The above line of code does the same thing as:
db.collection("Users").document().set(user).addOnSuccessListener(/* ... /*);
When you call CollectionReference's add() method or CollectionReference's document() method without passing any parameters, the built-in generator used by Firestore will always generate a random ID each time one of the methods is called.
As I see from the following line of code:
userRef = db.collection("Users").document(firebaseAuth.getCurrentUser().getUid());
You have already implemented Firebase Authentication, which is good. That being said, when you want to add new "User" object to the database, use the UID that comes from authentication process, as shown below:
String uid = FirebaseAuth.getInstance().getCurrentUser().getUid();
db.collection("Users").document(uid).set(user).addOnSuccessListener(/* ... /*);
In this way, the ID of the document will match the value of the "user_id" property in the database.
I'm trying to get a product from a document form the cloud firestore and then put that product in the shopping cart. When i read (successfully) the product, i try to put it in an arraylist that is declared outside but it doesnt work unless i put final to the variable.
Doing so, when I run the code below, I successfully retrieve the data, but the operation carrelloAttuale.prodotti.add(prod) is executed after the command transaction.update(), so the update doesn't upload nothing different from the start.
//prendo l'utente
FirebaseAuth auth= FirebaseAuth.getInstance();
//mi salvo il codice del prodotto scannerizzato
final String codiceProdottoScannerizzato=String.valueOf(intentData);
final FirebaseFirestore db = FirebaseFirestore.getInstance();
final DocumentReference docRef = db.collection("carrelli").document(auth.getUid());
final DocumentReference docrefprodotti = db.collection("prodotti").document(codiceProdottoScannerizzato);
db.runTransaction(new Transaction.Function<Void>() {
#Override
public Void apply(Transaction transaction) throws FirebaseFirestoreException {
DocumentSnapshot snapshot = transaction.get(docRef);
final Carrello carrelloAttuale = snapshot.toObject(Carrello.class);
docrefprodotti.get().addOnCompleteListener(new OnCompleteListener<DocumentSnapshot>() {
#Override
public void onComplete(#NonNull Task<DocumentSnapshot> task) {
if (task.isSuccessful()) {
DocumentSnapshot document = task.getResult();
if (document.exists()) {
Prodotti prod=document.toObject(Prodotti.class);
prod.id=codiceProdottoScannerizzato;
prod.totalePezziCarrello=1;
carrelloAttuale.prodotti.add(prod);
Log.d(TAG, "PRODOTTO: " + prod.toString());
} else {
Log.d(TAG, "No such document");
}
} else {
Log.d(TAG, "get failed with ", task.getException());
}
}
});
Log.d(TAG, "CARRELLO FB: " + carrelloAttuale.size());
transaction.update(docRef, "prodotti", carrelloAttuale.getProdotti());
// Success
return null;
}
}).addOnSuccessListener(new OnSuccessListener<Void>() {
#Override
public void onSuccess(Void aVoid) {
Log.d(TAG, "Transaction success!");
}
})
.addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
Log.w(TAG, "Transaction failure.", e);
}
});
I expect that the command update is executed after the carrelloAttuale.prodotti.add(prod)
in the debug log the order of tags are:
CARRELLO FB: 0
PRODOTTO: Nome: latte
Data is loaded from Firestore asynchronously, since it may have to be retrieved from the server. To prevent blocking the app, the main code continues while the data is being retrieved. Then when the data is available, your onComplete gets called.
This means that any code that needs the data from the data, must be inside the onComplete method, or be called from there. So something like:
docrefprodotti.get().addOnCompleteListener(new OnCompleteListener<DocumentSnapshot>() {
#Override
public void onComplete(#NonNull Task<DocumentSnapshot> task) {
if (task.isSuccessful()) {
DocumentSnapshot document = task.getResult();
if (document.exists()) {
Prodotti prod=document.toObject(Prodotti.class);
prod.id=codiceProdottoScannerizzato;
prod.totalePezziCarrello=1;
carrelloAttuale.prodotti.add(prod);
Log.d(TAG, "PRODOTTO: " + prod.toString());
} else {
Log.d(TAG, "No such document");
}
} else {
Log.d(TAG, "get failed with ", task.getException());
}
Log.d(TAG, "CARRELLO FB: " + carrelloAttuale.size());
transaction.update(docRef, "prodotti", carrelloAttuale.getProdotti());
}
});
Also see:
How to return a DocumentSnapShot as a result of a method?
Firebase Firestore get data from collection
"the command update" is executed before "carrelloAttuale.prodotti.add(prod)" is called because the onComplete() method has an asynchronous behaviour and returns immediately. This means that listener will not get invoked until some time later, after the database update operation is complete. There is no guarantee how long it will take. Depending on your connection speed and the state, it may take from a few hundred milliseconds to a few seconds for the update operation to complete.
If you want to use some logic with that data, you must wait until the asynchronous Firebase database operation is complete. This means that you can only use the prod object inside the listener callback itself.
For more informarions, I recommend you see the last part of my anwser from this post in which I have explained how it can be done using a custom callback. You can also take a look at this video for a better understanding.
I'm new on Google Firebase and I'm trying to learn something about it.
I'm doing an Android app where you can create a group of person and set the title of the group..then, in the "group page", you can see all your group in a listview.
The structure of my firestore db is something like this:
users --> email(document) ---> Group(collection) --> GroupName(Document) and the group name document contains the partecipants arrayList (partecipant 0 : Name1, partecipant1: name2 etc).
I would like to retrieve the document id(which is the group title) and the arrayList of partecipants, but I don't know of to use the for each in the code...
This is my code:
public void load_list_view(){
String email = getEmail();
final DocumentReference docRef = db.collection("users").document(email).collection("Group").document();
docRef.get()
.addOnSuccessListener(new OnSuccessListener<DocumentSnapshot>() {
#Override
public void onSuccess(DocumentSnapshot documentSnapshot) {
titleArray.add(documentSnapshot.getId());
titleString = documentSnapshot.getId();
partecipantsArray.add(documentSnapshot.getString("partecipant"));
num_partecipants = partecipantsArray.size();
numArray.add(num_partecipants);
trash = R.drawable.trash_icon;
firstChar = Character.toString(titleString.charAt(0));
firstCharArray.add(firstChar);
customAdapter = new GroupAdapter(GroupActivity.this, firstCharArray, titleArray, numArray, trash);
listView.setAdapter(customAdapter);
}
})
.addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
Toast.makeText(GroupActivity.this, e.getStackTrace().toString(), Toast.LENGTH_LONG).show();
}
});
}
with titleArray.add(documentSnapshot.getId()); it retrieve a random ID and I can't understand why.
I haven't found enough documentation on Internet about Arraylist and firestore.
First of all, to get all the documents in a collection you should write your code differently as shown in this documentation.
db.collection("cities")
.get()
.addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
#Override
public void onComplete(#NonNull Task<QuerySnapshot> task) {
if (task.isSuccessful()) {
for (QueryDocumentSnapshot document : task.getResult()) {
Log.d(TAG, document.getId() + " => " + document.getData());
}
} else {
Log.d(TAG, "Error getting documents: ", task.getException());
}
}
});
Secondly, if you are retrieving an ArrayList you should use (ArrayList<String>) documentSnapshot.get("key") instead of documentSnapshot.getString("key").
Thirdly, you are getting random Id because with this line of code (mentioned below) firebase is generating a new document reference with a random id. Reference Link.
final DocumentReference docRef = db.collection("users").document(email).collection("Group").document();
For your help, I have tweaked your code and you can try this code and check if it's working or not.
public void load_list_view() {
String email = getEmail();
final DocumentReference docRef = firestore.collection("users").document(email);
docRef.collection("Group")
.get()
.addOnSuccessListener(new OnSuccessListener<QuerySnapshot>() {
#Override
public void onSuccess(QuerySnapshot queryDocumentSnapshots) {
for (QueryDocumentSnapshot document : queryDocumentSnapshots) {
//Extracting Group name from each document
titleString = document.getId();
titleArray.add(titleString);
//Extracting participants ArrayList from each document
partecipantsArray.add((ArrayList<String>) document.get("participant"));
numArray.add(num_partecipants);
firstChar = Character.toString(titleString.charAt(0));
firstCharArray.add(firstChar);
}
num_partecipants = partecipantsArray.size();
numArray.add(num_partecipants);
trash = R.drawable.trash_icon;
firstChar = Character.toString(titleString.charAt(0));
firstCharArray.add(firstChar);
customAdapter = new GroupAdapter(GroupActivity.this, firstCharArray, titleArray, numArray, trash);
listView.setAdapter(customAdapter);
}
})
.addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
//HANDLE EXCEPTION
}
});
}
For People doing in kotlin you can do the following things to get an arraylist from FireStore
First get the ArrayList as String
Remove the char at first and last Index
Split the String at each ','
Convert the list to an Array List
Here is the Code
ArrayList(doc.document.get("id_here").toString().subSequence(1, doc.document.get("id_here").toString().length - 1).split(","))
Also it may have extra spaces so don't forget to use the functions
trimStart() && trimEnd()