I am currently working on a Rental Car app in Java Android Studio. For the project database, I am using Cloud Firestore. My database is structured like so:
Subcollection:
The first image is represented by the collection of Cars with multiple documents with random ID (each represents a car). Every car has a unique subcollection called bookings where are saved in random ID documents details about bookings made to that specific car (the second image is represented by an example of a booking subcollection).
I am currently working at a "Booking history" section. There, I want to put the current user's booking history (highlighted with red in the second image) in a RecyclerView. I know how to iterate between all cars and their subcollection and put and if(bookedBy == currentUser) condition to obtain only the cars booked by the current user.
But my question is: How can I save those in a RecyclerView? I want to save data highlighted in red (second image) in the RecyclerView if our previous condition is met. I don't know what query to put in the Firestore adapter. I was thinking about saving all the cars booked by the user and their dates in an object or something like this but I can't figure it out. Can anyone help me, please?
Thank you!
According to your database structure, which looks like this:
Firestore-root
|
--- Cars (collection)
|
--- $cardId (document)
|
--- bookings (collection)
|
--- $bookingId
|
--- bookedBy: "bogdanmaris11#yahoo.com"
|
--- endDate: 31 May, 2021...
|
--- finalPrice: 1998
|
--- startDate: 30 May, 2021...
To get all the bookings that correspond to a specific user, a Cloud Firestore collection group query is required. This query looks like this:
FirebaseFirestore rootRef = FirebaseFirestore.getInstance();
Query queryByEmail = rootRef.collectionGroup("bookings").whereEqualTo("bookedBy", "bogdanmaris11#yahoo.com");
queryByEmail.get().addOnCompleteListener(/* ... /*);
There are three things to notice. To get the desired results from this query, you might check the email address, as you might have a typo, bogdanmari(u)s11#yahoo.com, where the "u" is missing. Second, it's best to filter the users based on their UIDs and not based on their email addresses, because a user can change the email address at any time, while the UID will always remain the same. And the third one, as I understand, you intend to use the Firebase-UI library, which is good. In this case, the "queryByEmail" should be passed to FirestoreRecyclerOptions.Builder's setQuery() method. However, there might be an issue, because each query requires an index, and the numbers of indexes are limited in Firestore.
According to what you wrote:
I was thinking about saving all the cars booked by the user and their dates in an object or something like this but I can't figure it out.
That's a great idea. That's not about an object, as it more about a sub-collection or a new top-level collection, where you can nest all bookings that correspond to a specific user. In the first case, your schema will look like this:
Firestore-root
|
--- bookings (collection)
|
--- $uid (document) //Not email address
|
--- userBookings (collection)
|
--- $bookingId
|
--- carId: "YI1J ... VSm0"
|
--- endDate: 31 May, 2021...
|
--- finalPrice: 1998
|
--- startDate: 30 May, 2021...
Where there is no need for any email address in the Booking object, however a "cardId" is required, if you need the car data. To get all booking of a single user, the following reference is needed:
String uid = FirebaseAuth.getInstance().getCurrentUser().getUid();
FirebaseFirestore rootRef = FirebaseFirestore.getInstance();
CollectionReference userBookingsRef = rootRef.collection("bookings").document(uid).collection("userBookings");
userBookingsRef.get().addOnCompleteListener(/* ... /*);
And in the second:
Firestore-root
|
--- allBookings (collection)
|
--- $bookingId
|
--- bookedBy: "longUid"
|
--- carId: "YI1J ... VSm0"
|
--- endDate: 31 May, 2021...
|
--- finalPrice: 1998
|
--- startDate: 30 May, 2021...
Where is a simple query (without index) will do the trick:
String uid = FirebaseAuth.getInstance().getCurrentUser().getUid();
FirebaseFirestore rootRef = FirebaseFirestore.getInstance();
Query QueryByUid = rootRef.collection("allBookings").whereEqualTo("uid", uid);
QueryByUid.get().addOnCompleteListener(/* ... /*);
Pass the first reference or this Query to your "FirestoreRecyclerAdapter" and that's pretty much of it.
Related
--- Users (Collection)
|
--- p0A1fXH4l2TpvGE2lo0x
|
--- List (HashMap)
|
--- ID (String) (Value: UQx4CWRgnVLOdKEY3AKJ)
--- NAME (String) (Value: ...)
In Firestore, how can I find the documents that have a list ID equal to UQx4CWRgnVLOdKEY3AKJ? Before deleting the list, I need to remove it from the users who have used it. How can I determine which documents are using this list ID so I can delete them?
If I understand correctly, the List field inside the user document is a Map which contains only String values. So if your database schema looks exactly like this:
db
|
--- Users (collection)
|
--- $uid (document)
|
--- List (map) //👈
|
--- ID: "UQx4CWRgnVLOdKEY3AKJ"
|
--- NAME: "Taha Sami"
To get all users where the ID field within the List holds the value of UQx4CWRgnVLOdKEY3AKJ, a query like this will do the trick:
Query queryByListId = db.collection("Users").whereEqualTo("List.ID", "UQx4CWRgnVLOdKEY3AKJ");
// 👆
It looks like your List (HashMap) may have multiple child objects, and you want to search across all of those for a specific ID value.
There is no way to search across all objects in a map field in Firestore. If you want to search across all ID values, add an additional array field with just this values, e.g.
ListIDs: ["UQx4CWRgnVLOdKEY3AKJ", ...]
With that field in place, you can then use the array-contains operator to query for matching documents.
I am making a SearchView in the search bar to filter all processes that started or ended within a certain date.
I need to get a query of nodes inside processes and codes.
Here is my data structure and what I need to query:
I'm using this for the codes as an example:
//path of all posts
DatabaseReference ref = FirebaseDatabase.getInstance().getReference("codes");
//get all data from this ref
ref.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot snapshot) {
for (DataSnapshot ds: snapshot.getChildren()) {
String hola = Objects.requireNonNull(ds.getValue()).toString();
if (ds.hasChild("process")) {
for (DataSnapshot dsa: ds.getChildren()) {
System.out.println("result"+dsa);
/* if (!isEmpty(Objects.requireNonNull(dsa.child("ended").getValue()).toString())) {
System.out.println("result: "+dsa.child("ended").getValue().toString());
}
*/
}
But I need to obtain the node of other children, preferably without changing the data structure:
I have a FirebaseRecyclerOption where everything is loaded, and I need to only show the holders filtered from the SearchView.
If you're allowed to change the database schema, a more convenient way of storing the data would be to have a flattened structure like this:
Firebase-root
|
--- codes
|
--- $pushedId
|
--- code: 17240
|
--- proces: "Cajones"
|
--- user: "Jose Anton"
|
--- started: longNumberForStarted
|
--- ended: longNumberForEnded
Things I recommend:
Don't use sequential numbers as keys in the database. Use the unique random IDs that are generated by the push() method.
Don't store dates as strings, rather store them as timestamps.
In this way, you'll be able to filter the results as needed. And since you're using timestamps you'll be able to use startAt() or endAt() if you need.
Due to the formatting of the date in the string, it will not be possible to filter or sort the data on the server side. If it was formatted "yyyy.MM.dd a les HH:mm:ss" it would still be possible because they would be in order of magnitude, but arranged this way I can only imagine you reading all the nodes and reordering in a local list ( I don't think it's a good idea).
My suggestion is that you replace the format of this time field to something like a TIMESTAMP from the firebase itself with the server time or if they are custom times, convert them and re-store them.
If you do, you can create custom filters and order them on the server itself, as needed, using orderByChild(), orderByKey(), orderByValue(), limitToFirst(), limitToLast(), startAt(), startAfter(), endAt (), endBefore() and equalTo().
new database structure
This is how I show data for each user using FirebaseRecyclerOptions.
But I want to get all children data for only a specific user with a condition by checking users with UID or any other solution.
Ps : This post was edited many times because of the database structure has been changed
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
v = inflater.inflate(R.layout.fragment_pdv, container, false);
recyclerView = v.findViewById(R.id.pdv_list);
recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
if (user_id.equals("hZBCb9yk8ycamjoVcISjG2y4ZnI2")){
Query query = FirebaseDatabase.getInstance("https://dtechapp-94795-default-rtdb.europe-west1.firebasedatabase.app")
.getReference().child("commande").orderByChild("user_id").equalTo("hZBCb9yk8ycamjoVcISjG2y4ZnI2"); ;
FirebaseRecyclerOptions<Order> options =
new FirebaseRecyclerOptions.Builder<Order>()
.setQuery(query, Order.class)
.build();
pdvAdapter = new PdvAdapter(options);
recyclerView.setAdapter(pdvAdapter);
return v;
}
}
When you using the following query:
Query query = FirebaseDatabase.getInstance("https://dtechapp-94795-default-rtdb.europe-west1.firebasedatabase.app")
.getReference("pdv").child("user_id");
It means that you are trying to read data from:
rootRef/pdv/user_id
Such a reference actually doesn't exist. Under your "pdv" node there is no child called "user_id", but one called "fP0LLs83MYZqX9BqYO24IX80F3Q2". So you either use:
Query query = FirebaseDatabase.getInstance("https://dtechapp-94795-default-rtdb.europe-west1.firebasedatabase.app")
.getReference("pdv").child("fP0LLs83MYZqX9BqYO24IX80F3Q2");
Or if that UID of the user that comes from a Firebase authentication operation, you can then simply use:
String uid = FirebaseAuth.getInstance().getCurrentUser().getUid();
Query query = FirebaseDatabase.getInstance("https://dtechapp-94795-default-rtdb.europe-west1.firebasedatabase.app")
.getReference("pdv").child(uid);
Edit:
According to one of your comments:
So I want to get data from children of all users at the same time. This is 1st user "fP0LLs83MYZqX9BqYO24IX80F3Q2" I want to get children inside him and at the same time, I want to get children from 2nd user "hZBCb9yk8ycamjoVcISjG2y4ZnI2" and display it in my RecyclerView.
And according to your database schema, please note that you cannot achieve that. There is no way you can get all users having such a schema. Queries in the Realtime Database work on a flat list of nodes. Besides that, you have an unknown key under the UID node, which makes it impossible to query. To solve this, you should consider removing one level in your tree, and add "Amine" and "Zouaoui..." as properties inside each UID node. Your schema should look like this:
Firebase-root
|
--- pvd
|
--- $uid
| |
| --- type: "Amine..."
| |
| --- //Other fields.
|
--- $uid
|
--- type: "Zouaoui..."
|
--- //Other fields.
The code may remain unchanged.
I'm a little stuck, to create FirebaseRecyclerOptions that should present the results in recursive in database
I mean if I had in Data base (Crossfit - > APR_09_2021 -> 10_12 -> "some data collection")
So I noticed if I didn't pass the specific path to "some data collection" so it can't retrieve the data
I mean: DatabaseReference mbase = FirebaseDatabase.getInstance().getReference().child("Crossfit").child(APR_09_2021)
So my question how do I get all results from the database.
for all the dates that I have under the CrossFit
Thanks.
DatabaseReference mbase = FirebaseDatabase.getInstance().getReference().child("Crossfit");
recyclerView = findViewById(R.id.recycler_plan);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
FirebaseRecyclerOptions<TrainingTrainerCollector> options = new FirebaseRecyclerOptions.Builder<TrainingTrainerCollector>()
.setQuery(mbase, TrainingTrainerCollector.class).build();
recycleViewAdapter = new TrainerRecyclerviewViewAdapter(options,"Crossfit");
recyclerView.setAdapter(recycleViewAdapter);
recycleViewAdapter.startListening();
super.onStart();
https://i.stack.imgur.com/x4Fik.png
https://i.stack.imgur.com/1CW2U.png
You cannot do that with your actual database structure. When you are using the following reference:
DatabaseReference mbase = FirebaseDatabase.getInstance().getReference().child("Crossfit");
It means that you are trying to read all children under "Crossfit". So when using the Firebase-UI library, it means that you can only get children one level deep. According to your schema, your data is two-level deep, see?
APR_10_2021 -> 10_12 -> Data
There are two ways in which you can solve this. The first option you have is to create a new node called "allCrossfits" where you should add all "TrainingTrainerCollector" objects. This practice is called denormalization and is a common practice when it comes to Firebase. If you are new to NoSQL databases, I recommend you see this video, Denormalization is normal with the Firebase Database for a better understanding.
So, your new reference should look like this:
DatabaseReference mbase = FirebaseDatabase.getInstance().getReference().child("allCrossfits");
This reference should be passed to the "options" object.
The second option that you have is to remove the "time" level from your database tree, as it is already present in your "time" field. So new your schema should look like this:
Firebase-root
|
--- Crossfit
|
--- APR_10_2021
| |
| --- Current_Participant: "0"
| |
| --- Date: "APR 10 2021"
| |
| --- Max_Participant: "5"
| |
| --- Time: "10:00-12:00"
|
--- APR_14_2021
|
--- //Data
In this case, you only need to change the way you are adding the data to the database. Your actual "mbase" reference will perfectly fine.
I am new to coding and I am looking for a solution to add multiple image URLs (the images are selected from the gallery) to the same field in Cloud Firestore so as I could retrieve and display them later in a ViewPager.
I have been trying to find the solution, but with no luck.
Please, help me.
Thank you in advance.
You can try it by storing json array in cloud_firestore image_url_key
{"array": ["url1","url2","url3"]}
There are two ways in which you can achieve this. The first one would be to store those urls in a map. In this case the database structure should look this:
Firestore-root
|
--- collection
|
--- document
|
--- urls
|
--- url : "http://..."
|
--- url : "http://..."
But because the urls contain characters that aren't allowed to be used in the keys, you'll need to use an encoded version of the URL as the key (typically just strips the illegal characters, but a hashcode is also common) and then use the full URL as the value.
Because from 13th august it is possible to filter data in Cloud Firestore based on array values using whereArrayContains(), the second solution would be to store those urls in an array. In this case the database structure should look this:
Firestore-root
|
--- collection
|
--- document
|
--- urls: ["http://...", "http://..."]