I have the following situation, namely:
I get documents from a database and convert them to objects:
Code:
private void getProductsFromDatabaseBreakfast() {
breakfastProducts.clear();
firebaseFirestore.collection("Users").document(currentUserUID)
.collection("Types of Meals").document("Breakfast")
.collection("Date of Breakfast").document(date)
.collection("List of Products")
.get().addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
#Override
public void onComplete(#NonNull Task<QuerySnapshot> task) {
if(task.isSuccessful()) {
for(DocumentSnapshot documentSnapshot: task.getResult().getDocuments()) {
Log.i("id", documentSnapshot.getId());
breakfastProducts.add(documentSnapshot.toObject(Product.class));
}
}
if(getFragmentRefreshAdapter() != null) {
getFragmentRefreshAdapter().onRefresh();
}
}
});
}
Structure:
Then I display products in RecyclerView:
Going to the merits, I would like the user to be able to change the specifics of the product, and precisely its weight, which will automatically change the other values (calories, protein etc.).
Therefore, after clicking on a given item RecyclerView I go to an activity in which the user can make changes to the product. How can I associate a given product that the user has chosen with the corresponding product in CloudFirestore? That changes would also take place in the document.
I was thinking about incrementing the product ID then I could associate the product with the product position in ReyclerView but I read that it is not good practice or is there any other way?
If the user clicks on an item, then send the name to the other activity and then do a query:
CollectionReference ref = firebaseFirestore.collection("Users").document(currentUserUID)
.collection("Types of Meals").document("Breakfast")
.collection("Date of Breakfast").document(date)
.collection("List of Products");
ref.whereEqualTo("name", name).get().addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
#Override
public void onComplete(#NonNull Task<QuerySnapshot> task) {
if(task.isSuccessful()) {
for(DocumentSnapshot documentSnapshot: task.getResult().getDocuments()) {
Map<String, Object> data = new HashMap<>();
data.put("weight", 200);
ref.document(documentSnapshot.getId()).set(data, SetOptions.merge());
}
}
}
});
Use the query whereEqualTo to query that will return all names with the name Chicken breast meat, then after task is successful update the document with the data that the user entered.
https://firebase.google.com/docs/firestore/query-data/queries
https://firebase.google.com/docs/firestore/manage-data/add-data#set_a_document
Related
I have a problem when I am fetching my Data from Firebase Firestore. I want to get multiple Documents out of my "Posts" collection and that works fine. But to get my user documents out of my "Users" collection i need to start multiple tasks(Task) that ALL need to be completed before I want to call my callback function and i cant figure out how. Is there even a way to do it with a callback? Ive tried to solve it with Continuations but had a hard time.
Thanks in advance.
Here some simple code i wrote so u can maybe understand the problem a little bit better.
public void getPosts(final postCallback callback) {
final FirebaseFirestore db = FirebaseFirestore.getInstance();
CollectionReference postsRef = db.collection("Posts");
Query postsQuery = postsRef.orderBy("createTime", Query.Direction.DESCENDING).limit(20);
// Starting the post documents
Task<QuerySnapshot> task = postsQuery.get();
task.addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
#Override
public void onComplete(#NonNull Task<QuerySnapshot> task) {
if(task.isSuccessful()){
QuerySnapshot querySnapshot = task.getResult();
List<DocumentSnapshot> docsList = querySnapshot.getDocuments();
for(DocumentSnapshot docSnap : docsList){
String userID = docSnap.getString("originalPoster");
// getting user documents
Task<DocumentSnapshot> userTask = db.collection("Users").document(userID).get();
userTask.addOnCompleteListener(new OnCompleteListener<DocumentSnapshot>() {
#Override
public void onComplete(#NonNull Task<DocumentSnapshot> task) {
DocumentSnapshot userDoc = task.getResult();
String userID = userDoc.getId();
String firstName = userDoc.getString("first_name");
String surname = userDoc.getString("surname");
User userObject = new User(firstName, userID, surname);
// cant call my callback right here otherwise its called for every
// completed user fetch
}
});
// cant call my callback right here since its too early
}
}else if(task.isCanceled()){
System.out.println("Fetch failed!");
}
}
});
}
I want to compare the input data of a user, with my firestore record (data that is already stored in my Firebase.
Task<QuerySnapshot> pLJava = CoRef
.whereEqualTo("ProgrammingLanguages", "Java")
.get()
.addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
#Override
public void onComplete(#NonNull Task<QuerySnapshot> task) {
if (task.isSuccessful()) {
for (QueryDocumentSnapshot document : task.getResult()) {
// document.getData();
id = document.getId();
if (id.contains("Java")) {
}
I already wrote an algorithm to query the input data. Now I want to compare the input data with my Firebase record.
Thanks for all the help in forward!
Simply convert the document into a POJO. There's a method toObject(Class<T> valueType) in Firestore API which resolves that.
YourClass foo = document.toObject(YourClass.class);
Then, you may compare the object data with the user's input data.
When I create a user i have a List with the Urls of the images that that user needs.
Like
final List<String> picturesUrls = pictureUrl;
My user object is
import java.util.List;
public class User {
public List<String> picturesUrls;
public User() {
}
public User(List<String> picturesUrls) {
this.picturesUrls = picturesUrls;
}
}
It all work fine, but when I want to get those data from FireStore
db.collection("Users").whereEqualTo("email",user.getEmail())
.get()
.addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
#Override
public void onComplete(#NonNull Task<QuerySnapshot> task) {
if(task.isSuccessful()){
for(QueryDocumentSnapshot document: task.getResult()){
Object obj = document.get("picturesUrls");
actualUser = new User(
picturesUrls
);
}
}
}
});
but In the User model, picturesUrls is the type of List and what gives me from FireStore is an object. For now in that list it is only 1 string.
How can I convert that object to a List to create the new User Object and then loop into that List to get the last value ("actual profile picture")
Thank you in advance!
First you convert DocumentSnapshot to instance of User class. You would have to initialize getter in User class. Then you can use getter to get the url list.
So you code will be like this :
db.collection("Users").whereEqualTo("email",user.getEmail())
.get()
.addOnSuccessListener(new OnSuccessListener<QuerySnapshot>() {
#Override
public void onSuccess(QuerySnapshot queryDocumentSnapshots) {
for(DocumentSnapshot ds : queryDocumentSnapshots) {
User user = ds.toObject(User.class);
List<String> urlList = user.getPicturesUrls();
}
}
});
I have a Firestore database like this:
I want to access each of the different symptom data's i.e. "Anxiety_data" and its children which consists of timestamps, and then a dictionary, and place them into a RecyclerView using FirebaseUI FirebaseRecyclerViewAdapter
I have this model class:
public class EntryDataModel {
private String timestamp, symptom, severity, comment;
public EntryDataModel() {}
public EntryDataModel(String timestamp, String symptom, String severity, String comment) {
this.timestamp = timestamp;
this.symptom = symptom;
this.severity = severity;
this.comment = comment;
}
public String getTimestamp() {return timestamp;}
public String getSymptom() {return symptom;}
public String getSeverity() {return severity;}
public String getComment() {return comment;}
}
Here is my Query:
Query query = db.collection("users").document(user_id).collection("symptom_data");
Here is the Firebase RecyclerView Adapter:
void fireStoreRecyclerAdapterSetup() {
FirestoreRecyclerOptions<EntryDataModel> options = new FirestoreRecyclerOptions.Builder<EntryDataModel>()
.setQuery(query, EntryDataModel.class)
.build();
FirestoreRecyclerAdapter adapter = new FirestoreRecyclerAdapter<EntryDataModel, EntryDataHolder>(options) {
#Override
public void onBindViewHolder(EntryDataHolder holder, int position, EntryDataModel model) {
// Bind the Chat object to the ChatHolder
// ...
System.out.println("Query: " + query.toString());
}
#Override
public EntryDataHolder onCreateViewHolder(ViewGroup group, int i) {
// Create a new instance of the ViewHolder, in this case we are using a custom
// layout called R.layout.message for each item
View view = LayoutInflater.from(group.getContext())
.inflate(R.layout.entry_data_recyclerview_item, group, false);
return new EntryDataHolder(view);
}
};
recyclerView.setAdapter(adapter);
adapter.startListening();
}
}
I am not sure how I should set this up so that I take all the Timestamp arrays from each of the symptom data fields, and have them all together in a list that I can use for the recyclerView.
Maybe I cannot use FirebaseUI Recycler adapter, or I need to iterate through each different symptom field first and build + append a list? Hopefully I am clear in what I would like to do Thank you.
EDIT: I have done it on iOS, This is the result that I want:
EDIT: I have added this, getting the names of each individual document, and then once I have that in a for loop I am now trying to get the Array values and add that to an EntryDataModel list, then I can use my own adapter:
EDIT: This works, I get the data I need, from each Document. Now I just need to be able to Iterate over the fields and timestamps, and use my model to create a list. How can I do that? Log.d("example", "DocumentSnapshot data: " + document.getData()); this prints: D/example: DocumentSnapshot data: {1558879769={severity=Mild, symptom=Anxiety, comment=Mild anxiety at work., timestamp=1558879769}, 1558879745={severity=Mild, symptom=Anxiety, comment=Feeling relaxed watching TV., timestamp=1558879745}, 1558879710={severity=Moderate, symptom=Anxiety, comment=Walking the cat., timestamp=1558879710}, 1558879827={severity=Moderate, symptom=Anxiety, comment=Taking the cat for a walk., timestamp=1558879827}, 1558888729={severity=Mild, symptom=Anxiety, comment=The cat is fed up with walking., timestamp=1558888729}} Now I just need to get each timestamp array and add them to a seperate list, and i can then do this for each document and have a full list of all the timestamp arrays.
void getSymptomData() {
final CollectionReference colRef = db.collection("users").document(user_id).collection("symptom_data");
colRef.get().addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
#Override
public void onComplete(#NonNull Task<QuerySnapshot> task) {
if (task.isSuccessful()) {
List<String> list = new ArrayList<>();
for (QueryDocumentSnapshot document : task.getResult()) {
list.add(document.getId());
DocumentReference docRef = colRef.document(document.getId());
docRef.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("Iteration", "DocumentSnapshot data: " + document.getData());
} else {
Log.d("NoDoc", "No such document");
}
} else {
Log.d("Failed", "get failed with ", task.getException());
}
}
});
}
Log.d("listylist", list.toString());
} else {
Log.d("tag", "Error getting documents: ", task.getException());
}
}
});
}
The automatic mapping that the Firebase SDK can do from a Firestore document to a Java object, requires that each field name from the document matches a property name in the Java class. It has no mechanism to deal with dynamic fields, such as the timestamps in your example.
So you will have to do your own conversion. For that you can use the public T get (FieldPath fieldPath, Class<T> valueType) or public T get (String field, Class<T> valueType) method, which allow you to get an object from a specific field that you specify. So you will have to loop over the timestamp fields, but after that the Firebase SDK can map the severity, symptom, and timestamp properties to the object.
I am using Cloud Firestore in my app and have 2 collections Customers and Properties. I have an activity where the user can update the data contained in a customer document name address etc. This code shown below is working fine.
db.collection("Customers").document(customer.getCustomerId())
.update(
"name", c.getName(),
"email", c.getEmail(),
"phoneNo", c.getPhoneNo(),
"address", c.getAddress(),
"creatorId", c.getCreatorId()
)
.addOnCompleteListener(new OnCompleteListener<Void>() {
I have the document reference of the customers saved in the Properties documents so I can reference which customer owns which property. Using this reference I want to search for Properties containing that reference and update the name field if it has been changed. I have tried adding the code into my method after the onComplete checks for the above code, but it doesn't update the name field every time only every few attempts.
FirebaseFirestore rootRef = FirebaseFirestore.getInstance();
CollectionReference propRef = rootRef.collection("Properties");
propRef.whereEqualTo("customerId", customerId).get().addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
#Override
public void onComplete(#NonNull Task<QuerySnapshot> task) {
if (task.isSuccessful()) {
for (QueryDocumentSnapshot document : Objects.requireNonNull(task.getResult())) {
Map<Object, String> map = new HashMap<>();
map.put("customer", customerName);
propRef.document(document.getId()).set(map, SetOptions.merge()).addOnCompleteListener(new OnCompleteListener<Void>() {
#Override
public void onComplete(#NonNull Task<Void> task) {
if (task.isSuccessful()) {
Is there a way to achieve what I am trying to do?
I did think I could do it using batch but from what I have read this does not allow searching.
#AlexMamo
This is a document from my Customers collection
This is a linked document from my Properties collection
Customers Structure
Properties Structure
According to your comments, to solve your the issue, please use the following lines of code:
FirebaseFirestore rootRef = FirebaseFirestore.getInstance();
CollectionReference propertiesRef = rootRef.collection("Properties");
CollectionReference customersRef = rootRef.collection("Customers");
customersRef.whereEqualTo("customerId", customerId).get().addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
#Override
public void onComplete(#NonNull Task<QuerySnapshot> task) {
if (task.isSuccessful()) {
for (QueryDocumentSnapshot document : task.getResult()) {
String customerName = document.getString("name");
propertiesRef.whereEqualTo("customerId", customerId).get().addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
#Override
public void onComplete(#NonNull Task<QuerySnapshot> task) {
if (task.isSuccessful()) {
for (QueryDocumentSnapshot doc : task.getResult()) {
propertiesRef.document(doc.getId()).update("customer", customerName);
}
}
}
});
}
}
}
});
See you should use different CollectionReference objects, propertiesRef and customersRef, you are using a single one.
you need to implement the Interface from firestore OnDataChange and receive the new values of your variables bind to your views.
in that case when you modify or update a value in your firestore the change triggers this interface and you can assign the new change.
ValueEventListener myListener = new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
// Get Post object and use the values to update the UI
Customer customer = dataSnapshot.getValue(Customer.class);
// ...
}
#Override
public void onCancelled(DatabaseError databaseError) {
// Getting Post failed, log a message
Log.w(TAG, "loadPost:onCancelled", databaseError.toException());
// ...
}
};
rootRef.addValueEventListener(myListener);
hopefully it works for you.
https://firebase.google.com/docs/database/android/read-and-write?authuser=0