How to get data from a map field from Firebase Firestore? - java

I want to retrieve data stored as a map field on Cloud Firestore.
I want to get the 'Comment' as a string from 'All Comments' field to show it in a TextView.
How can I do it? (Java)
I tried this to add the data
Map<String,String> allComments=new HashMap<String,String>();
String commentContent=commentboxedittext.getText().toString();
allComments.put("Movie Name",name);
allComments.put("Comment",commentContent);
firebaseFirestore.collection("All Comments").document("MovieComments").set(allComments, SetOptions.merge());
And this to retrieve the data
DocumentReference docRef = firebaseFirestore.collection("All Comments").document("MovieComments");
docRef.get().addOnCompleteListener(new OnCompleteListener<DocumentSnapshot>() {
#Override
public void onComplete(#NonNull Task<DocumentSnapshot> task) {
if (task.isSuccessful()) {
DocumentSnapshot document = task.getResult();
if (document.exists()) {
Map<String, Object> m=document.getData();
userComment=m.get("Comment").toString();
mName=m.get("Movie Name").toString();
} else {
Toast.makeText(MovieDetails.this, "No Such Document", Toast.LENGTH_SHORT).show();
}
} else {
Toast.makeText(MovieDetails.this, "Error", Toast.LENGTH_SHORT).show();
}
}
});
But app crashes on doing this.
I also tried doing this to put the data and it worked but then I do not know how to retrieve data form this method.
Map<String,String> allComments=new HashMap<String,String>();
Map<String, Object> user=new HashMap<String,Object>();
userID=firebaseAuth.getCurrentUser().getUid();
userReference=firebaseFirestore.collection("Users ").document(userID);
String commentContent=commentboxedittext.getText().toString();
allComments.put("Movie Name",name);
allComments.put("Comment",commentContent);
user.put("All Comments",allComments);
userReference.set(user, SetOptions.merge()).addOnSuccessListener(new OnSuccessListener<Void>() {
#Override
public void onSuccess(Void unused) {
Toast.makeText(MovieDetails.this, "Comment Added", Toast.LENGTH_SHORT).show();
}
}).addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
if(e instanceof FirebaseNetworkException)
Toast.makeText(MovieDetails.this, "No Internet Connection", Toast.LENGTH_SHORT).show();
Toast.makeText(MovieDetails.this, "Values Not Stored", Toast.LENGTH_SHORT).show();
}
});

Assuming that "l4ir...Xy12" is ID of the authenticated user, to get the value of the "Comment" that exists within the "All Comments" map, please use the following lines of code:
String uid = FirebaseAuth.getInstance().getCurrentUser().getUid();
FirebaseFirestore db = FirebaseFirestore.getInstance();
db.collection("users").document(uid).get().addOnCompleteListener(new OnCompleteListener<DocumentSnapshot>() {
#Override
public void onComplete(#NonNull Task<DocumentSnapshot> task) {
if (task.isSuccessful()) {
DocumentSnapshot document = task.getResult();
if (document.exists()) {
String comment = ((HashMap<String, Object>) document.getData().get("All Comments")).get("Comment").toString();
Log.d("TAG", comment);
} else {
Log.d("TAG", "No such document");
}
} else {
Log.d("TAG", "get failed with ", task.getException());
}
}
});
The result in the logcat will be:
sfgs
A few things to note:
DocumentSnapshot#get(String field) returns an object of type Object. Since each field inside a document represents a pair of keys and values, we can cast the result to an object of type HashMap<String, Object>.
Since we already have a Map, we can get the call Map#get(Object key) method, which returns the value associated with the key.

Related

How to read a Firestore document?

I'm new to Android development ... ;-)
I need to know how to read a specific document I saved to Firestore, without having to copy the "documentPath" manually from the Cloud Firestore Console!
How do you do this automatically?
Next, I put some of the code where the documentPath is that I need to configure:
DocumentReference user = mFirestore.collection("Users").document(idUsers).collection("Companies").document(**"documentPath"**)
link to the image:
Company that I registered now and that I wish to need to show the user automatically
link to the document:
Document fields
I'm testing the following class:
private void ReadSingleEmpresa() {
FirebaseAuth autenticacao = ConfiguracaoFirebase.getFirebaseAutenticacao();
String idUsuario = Base64Custom.codificarBase64(autenticacao.getCurrentUser().getEmail());
DocumentReference user = mFirestore.collection("Users").document(idUsuario).collection("Companies").document("gaSpr59pbeMmO9UpFxQQ");//document path
user.get().addOnCompleteListener(new OnCompleteListener<DocumentSnapshot>() {
#Override
public void onComplete(#NonNull Task<DocumentSnapshot> task) {
if (task.isSuccessful()) {
DocumentSnapshot document = task.getResult();
if (document.exists()) {
Log.d("ler doc", "DocumentSnapshot data: " + document.getData());
StringBuilder fields = new StringBuilder("");
//Some document fields
fields.append("Company name: ").append(document.get("nomeEmpresa"));
fields.append("\nEmail: ").append(document.get("emailRepresentante"));
fields.append("\nTelephone number: ").append(document.get("telefoneRepresentante"));
txtEmpresa.setText(fields.toString());
} else {
Log.d("ler doc", "No such document");
}
} else {
Log.d("ler doc", "get failed with ", task.getException());
}
}
})
.addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
}
});
}
This is the result:
Result of reading some document fields
It works but I do not know how to get the document I just registered among several. I only get it when I manually copy the document ID ...

How to get a list of Strings from inside of a document Firestore

I have stored a list of Strings inside a document.i have given it a name("name") now i am trying to get it on the client side but do not know how to do it exactly. i can see that if my field is of type number i can use getLong("Fieldname") but i am unable to figure it out how to get back a list of type Strings.
My code so far
FirebaseFirestore getlistofbrands=FirebaseFirestore.getInstance();
getlistofbrands.collection(FireStoreConstants.COL_BRANDNAMESONLY).document("Brands").get().addOnSuccessListener(new OnSuccessListener<QuerySnapshot>() {
#Override
public void onSuccess(QuerySnapshot queryDocumentSnapshots) {
List<String> listofbrandsfromfirestore=queryDocumentSnapshots.getDocuments().toArray(String.class);
also tried this
List<String> listofbrandsfromfirestore=queryDocumentSnapshots.toObjects(String.class);
how i uploaded the data
Map < String, Object > docData = new HashMap < > ();
List < String > deviceNameList = new ArrayList < String > ();
deviceNameList.add("brand1");
deviceNameList.add("brand2");
deviceNameList.add("brand3");
deviceNameList.add("brand4");
docData.put("name", deviceNameList);
firebaseFirestore.collection("FireStoreConstants.COL_BRANDNAMESONLY")
.document("Brands").set(docData).addOnSuccessListener(new
OnSuccessListener < Void > () {
#Override
public void onSuccess(Void aVoid) {
Log.d(TAG, "Repository:onSuccess:");
}
}).addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
Log.d(TAG, "Repository:error" + e.toString());
}
});
Since you store the brand names in a single document, I doubt QuerySnapshot is the right class to get back. As far as I can see, you'll want to use getData() to get the Map<String, Object> that Firestore uses for your List:
getlistofbrands
.collection(FireStoreConstants.COL_BRANDNAMESONLY)
.document("Brands")
.get()
.addOnCompleteListener(new OnCompleteListener<DocumentSnapshot>() {
#Override
public void onComplete(#NonNull Task<DocumentSnapshot> task) {
if (task.isSuccessful()) {
DocumentSnapshot document = task.getResult();
if (document.exists()) {
Log.d(TAG, "DocumentSnapshot data: " + document.getData());
Map<String, String> brandsMap = (Map<String, String>)documen.getData().getData("AllBrands");
List<String> brands = new LinkedList<String>();
for (String key: brandsMap.keySet()) {
brands.add(brandsMap.get(key));
}
// TODO: use the brands list
} else {
Log.d(TAG, "No such document");
}
} else {
Log.d(TAG, "get failed with ", task.getException());
}
}
});
You might also want to try:
List<String> brands = brandsMap.value();
i used the code provided by #Frank van and used this function to cast my Object to a list of Strings
#SuppressWarnings("unchecked")
public static <T extends List<?>> T cast(Object obj) {
return (T) obj;
}
Object brandsobj = queryDocumentSnapshots.getData().get("AllBrands");
List<String> allbrands = cast(brandsobj);
You can check out this question post I had posted earlier. You can change from the ArrayAdapter that I have used to a List
AutoCompleteTextView data from Firestore
Hope it helps you.

Firestore perform delete based on condition [duplicate]

This question already has answers here:
How to delete document from firestore using where clause
(12 answers)
Closed 9 months ago.
Is there a way I can perform a delete on Firestore documents where field1 =x and field2 = y?
I see the delete function but does not come with where.
If I use the transaction then there is get and delete but the get does not seem to accept "where" clause.
I hope I am missing something in the documentation.
Thanks
To achieve this, you need to create the desired query first and then just use the delete() method like this:
FirebaseFirestore rootRef = FirebaseFirestore.getInstance();
CollectionReference itemsRef = rootRef.collection("yourCollection");
Query query = itemsRef.whereEqualTo("field1", "x").whereEqualTo("field2", "y");
query.get().addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
#Override
public void onComplete(#NonNull Task<QuerySnapshot> task) {
if (task.isSuccessful()) {
for (DocumentSnapshot document : task.getResult()) {
itemsRef.document(document.getId()).delete();
}
} else {
Log.d(TAG, "Error getting documents: ", task.getException());
}
}
});
Here's my method for both querying and deleting documents from firestore. First it queries the data, then it deletes it.
Note, this method must be adapted for integer/double values.
public void whereQueryDelete(final String collection, final String field, final String value) {
mFirestoreDatabase.collection(collection)
.whereEqualTo(field, value)
.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());
String idDelete = document.getId();
mFirestoreDatabase.collection(collection).document(idDelete)
.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);
}
});
}
} else {
Log.d(TAG, "Error getting documents: ", task.getException());
}
}
});
}

Add new field or change the structure on all Firestore documents

Consider a collection of users. Each document in the collection has name and email as fields.
{
"users": {
"uid1": {
"name": "Alex Saveau",
"email": "saveau.alexandre#gmail.com"
},
"uid2": { ... },
"uid3": { ... }
}
}
Consider now that with this working Cloud Firestore database structure I launch my first version of a mobile application. Then, at some point I realize I want to include another field such as last_login.
In the code, reading all the users documents from the Firestore DB using Java would be done as
FirebaseFirestore.getInstance().collection("users").get()
.addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
#Override
public void onComplete(#NonNull Task<QuerySnapshot> task) {
if (task.isSuccessful()) {
for (DocumentSnapshot document : task.getResult()) {
mUsers.add(document.toObject(User.class));
}
}
}
});
where the class User contains now name, email and last_login.
Since the new User field (last_login) is not included in the old users stored in the DB, the application is crashing because the new User class is expecting a last_login field which is returned as null by the get() method.
What would be the best practice to include last_login in all the existing User documents of the DB without losing their data on a new version of the app? Should I run an snippet just once to do this task or are there any better approaches to the problem?
You fell into a gap of NOSQL databases: Document oriented databases do not guarantee structural integrity of the data (as RDBMS do)
The deal is:
in an RDBMS all stored data have the same structure at any given time (within the same instance or cluster). When changing the structure (ER-diagram) you have to migrate the data for all existing records which costs time and effort.
As a result, your application can be optimized for the current version of the data structure.
in a Document oriented database each record is an independent "Page" with its own independent structure. If you change the structure it only applies to new documents. So you don't need to migrate the existing data.
As a result, your application must be able to deal with all versions of the data structure you've ever used in your current database.
I don't know about firebase in detail but in general you never update a document in a NOSQL database. You only create a new version of the document. So even if you update all documents your application must be prepared to deal with the "old" data structure...
I wrote some routines to help automate this process back when I posted the question. I did not post them since these are a bit rudimentary and I was hoping for an elegant Firestore-based solution. Because such solution is not still available, here are the functions I wrote.
In short, we have functions for renaming a field, adding a field, or deleting a field. To rename a field, different functions are used depending on the data type. Maybe someone could generalise this better? The functions below are:
add_field: Adds a field in all documents of a collection.
delete_field: Deletes a field in all documents of a collection.
rename_*_field: Renames a field containing a certain data type (*) in all documents of a collection. Here I include examples for String, Integer, and Date.
Add field:
public void add_field (final String key, final Object value, final String collection_ref) {
FirebaseFirestore.getInstance().collection(collection_ref).get()
.addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
#Override
public void onComplete(#NonNull Task<QuerySnapshot> task) {
if (task.isSuccessful()) {
WriteBatch batch = db.batch();
for (DocumentSnapshot document : task.getResult()) {
DocumentReference docRef = document.getReference();
Map<String, Object> new_map = new HashMap<>();
new_map.put(key, value);
batch.update(docRef, new_map);
}
batch.commit();
} else {
// ... "Error adding field -> " + task.getException()
}
}
})
.addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
// ... "Failure getting documents -> " + e
}
});
}
Delete field:
public void delete_field (final String key, final String collection_ref) {
FirebaseFirestore.getInstance().collection(collection_ref).get()
.addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
#Override
public void onComplete(#NonNull Task<QuerySnapshot> task) {
if (task.isSuccessful()) {
WriteBatch batch = db.batch();
for (DocumentSnapshot document : task.getResult()) {
DocumentReference docRef = document.getReference();
Map<String, Object> delete_field = new HashMap<>();
delete_field.put(key, FieldValue.delete());
batch.update(docRef, delete_field);
}
// Commit the batch
batch.commit().addOnCompleteListener(new OnCompleteListener<Void>() {
#Override
public void onComplete(#NonNull Task<Void> task) {
// ...
}
});
} else {
// ... "Error updating field -> " + task.getException()
}
}
})
.addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
// ... "Failure getting notices -> " + e
}
});
}
Rename field:
public void rename_string_field (final String old_key, final String new_key, final String collection_ref) {
FirebaseFirestore.getInstance().collection(collection_ref).get()
.addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
#Override
public void onComplete(#NonNull Task<QuerySnapshot> task) {
if (task.isSuccessful()) {
WriteBatch batch = db.batch();
for (DocumentSnapshot document : task.getResult()) {
DocumentReference docRef = document.getReference();
String old_value = document.getString(old_key);
if (old_value != null) {
Map<String, Object> new_map = new HashMap<>();
new_map.put(new_key, old_value);
Map<String, Object> delete_old = new HashMap<>();
delete_old.put(old_key, FieldValue.delete());
batch.update(docRef, new_map);
batch.update(docRef, delete_old);
}
}
// Commit the batch
batch.commit().addOnCompleteListener(new OnCompleteListener<Void>() {
#Override
public void onComplete(#NonNull Task<Void> task) {
// ...
}
});
} else {
// ... "Error updating field -> " + task.getException()
}
}
})
.addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
// ... "Failure getting notices ->" + e
}
});
}
public void rename_integer_field (final String old_key, final String new_key, final String collection_ref) {
FirebaseFirestore.getInstance().collection(collection_ref).get()
.addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
#Override
public void onComplete(#NonNull Task<QuerySnapshot> task) {
if (task.isSuccessful()) {
WriteBatch batch = db.batch();
for (DocumentSnapshot document : task.getResult()) {
DocumentReference docRef = document.getReference();
int old_value = document.getDouble(old_key).intValue();
Integer ov = old_value;
if (ov != null) {
Map<String, Object> new_map = new HashMap<>();
new_map.put(new_key, old_value);
Map<String, Object> delete_old = new HashMap<>();
delete_old.put(old_key, FieldValue.delete());
batch.update(docRef, new_map);
batch.update(docRef, delete_old);
}
}
// Commit the batch
batch.commit().addOnCompleteListener(new OnCompleteListener<Void>() {
#Override
public void onComplete(#NonNull Task<Void> task) {
// ...
}
});
} else {
// ... "Error updating field -> " + task.getException()
}
}
})
.addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
// ... "Failure getting notices -> " + e
}
});
}
public void rename_date_field (final String old_key, final String new_key, final String collection_ref) {
FirebaseFirestore.getInstance().collection(collection_ref).get()
.addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
#Override
public void onComplete(#NonNull Task<QuerySnapshot> task) {
if (task.isSuccessful()) {
WriteBatch batch = db.batch();
for (DocumentSnapshot document : task.getResult()) {
DocumentReference docRef = document.getReference();
Date old_value = document.getDate(old_key);
if (old_value != null) {
Map<String, Object> new_map = new HashMap<>();
new_map.put(new_key, old_value);
Map<String, Object> delete_old = new HashMap<>();
delete_old.put(old_key, FieldValue.delete());
batch.update(docRef, new_map);
batch.update(docRef, delete_old);
}
}
// Commit the batch
batch.commit().addOnCompleteListener(new OnCompleteListener<Void>() {
#Override
public void onComplete(#NonNull Task<Void> task) {
// ...
}
});
} else {
// ... "Error updating field -> " + task.getException()
}
}
})
.addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
// ... "Failure getting notices -> " + e
}
});
}
Just wanted to share because I read that you were hoping for a Firestore based solution.
This worked for me. forEach will query each document in the collection and you can manipulate as you like.
db.collection("collectionName").get().then(function(querySnapshot) {
querySnapshot.forEach(async function(doc) {
await db.collection("collectionName").doc(doc.id).set({newField: value}, {merge: true});
// doc.data() is never undefined for query doc snapshots
console.log(doc.id, " => ", doc.data());
});
});
To solve this, you need to update each user to have the new property and for that I recommend you to use a Map. If you are using a model class when you are creating the users as explained in my answer from this post, to update all users, just iterate over the users collection amd use the following code:
Map<String, Object> map = new HashMap<>();
map.put("timestamp", FieldValue.serverTimestamp());
userDocumentReference.set(map, SetOptions.merge());
I am guessing that last_login is a primitive data type, maybe a long to hold a timestamp. An auto-generated setter would look like this:
private long last_login;
public void setLast_login(long last_login) {
this.last_login = last_login;
}
This leads to a crash when old documents that lack the field are fetched due to null assignment to a variable of a primitive data type.
One way around it is to modify your setter to pass in a variable of the equivalent wrapper class - Long instead of long in this case, and put a null check in the setter.
private long last_login;
public void setLast_login(Long last_login) {
if(last_login != null) {
this.last_login = last_login;
}
}
The cost of avoiding the null pointer exception is the boxing-unboxing overhead.

Firestore - How Can I Get The Collections From a DocumentSnapshot?

Let's say I have a userSnapshot which I have got using get operation:
DocumentSnapshot userSnapshot=task.getResult().getData();
I know that I'm able to get a field from a documentSnapshot like this (for example):
String userName = userSnapshot.getString("name");
It just helps me with getting the values of the fields, but what if I want to get a collection under this userSnapshot? For example, its friends_list collection which contains documents of friends.
Is this possible?
Queries in Cloud Firestore are shallow. This means when you get() a document you do not download any of the data in subcollections.
If you want to get the data in the subcollections, you need to make a second request:
// Get the document
docRef.get().addOnCompleteListener(new OnCompleteListener<DocumentSnapshot>() {
#Override
public void onComplete(#NonNull Task<DocumentSnapshot> task) {
if (task.isSuccessful()) {
DocumentSnapshot document = task.getResult();
// ...
} else {
Log.d(TAG, "Error getting document.", task.getException());
}
}
});
// Get a subcollection
docRef.collection("friends_list").get()
.addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
#Override
public void onComplete(#NonNull Task<QuerySnapshot> task) {
if (task.isSuccessful()) {
for (DocumentSnapshot document : task.getResult()) {
Log.d(TAG, document.getId() + " => " + document.getData());
}
} else {
Log.d(TAG, "Error getting subcollection.", task.getException());
}
}
});

Categories