How to paginate in Firebase Database #AskFirebase - android

I am creating an application that is some kind of a personal vocabulary. The database is of the following form.
Now I need to implement a pagination, partial retrieval of the words of a user, but preserving the lexicographical order. Keeping words as keys (/user/{uid}/words/{word}) is not suitable, because handling homographs will be impossible in the future (as their key will coincide). I decided to keep additional property word for each user, so that I can call db.getReference().child("users").child(uid).child("words").orderByChild("word").
This will retrieve all words of a user. Now I need to paginate this query, e.g. first download 20 words and then again 20 etc., but preserving lexicographical order.
{
"users" : {
"yXYSqB016JMr1FIc85pvMbvqDDt2" : {
"words" : {
"5v1a1PaDKnTvvOH19kaFTa1iyOx2" : {
"index" : 1,
"word" : "apple"
},
"kXHakBKxk9TrAlWL1vTOCe0akk80" : {
"index" : 2,
"word" : "house"
},
"xSKSqB312JMrsFig15pvMbvqAAt0" : { ... }
}
},
"zCAtMpl9uxSjG9dJarGktTTs20w2" : { ... }
},
"vocabulary" : {
"en" : {
"5v1a1PaDKnTvvOH19kaFTa1iyOx2" : {
"definitions" : {
"a fruit that grows on a tree" : true
},
"word" : "apple"
},
"kXHakBKxk9TrAlWL1vTOCe0akk80" : { ... },
"xSKSqB312JMrsFig15pvMbvqAAt0" : { ... }
}
}
}

You seem to come from a SQL way of thinking, where you paginate by specifying the number of items to get and the number of items to skip. This is index-based pagination.
Firebase on the other uses cursor-based pagination. You tell it the nimber of items to get and at which item to start (or end). You identify this item by the value of the property on which you order, in your case that is the value of word. Since the same value could potentially appear in multiple children, you can also specify the key (the thing starting with 5v1a1...) of the child at which to start/end as a second parameter.
So say that you have a page size of two. You get the first 2 words with:
DatabaseReference allWords = db.getReference().child("users").child(uid).child("words");
Query firstPage = allWords.orderByChild("word").limitToFirst(2);
When you attach a listener to this, you'll get the first two words. You'll need to remember the word and the key of the last word in this first page:
String lastWordOnPreviousPage = "house";
String lastKeyOnPreviousPage = "5v1a1...";
Now if you need the second page of two words, you get them by:
Query secondPage = allWords.orderByChild("word").startAt(lastWordOnPreviousPage, lastKeyOnPreviousPage).limitToFirst(2);

Related

Firestore - add array of objects

I'm having trobule with adding Array of Objects (tables) to Array of objects(restaurants). By that i mean, that I have following Firestore structure:
... (other data)
restaurants: [
0: {
name: name_0
...
tables: [
0: { no_chairs: 5, ... },
]
},
...
]
I know how to add new object to restaurants, but I'm having trouble with adding new restaurant if tables list contains any data.
My code rn:
var collection = firestore.collection('usersData')
.doc(this.currentUser.uid).collection('buisness').doc('restaurants');
collection.update({
'restaurants': FieldValue.arrayUnion([restaurant.toJson()]),
}).then( ... // reaction based on api answer
class Restaurant:
Map<String, dynamic> toJson() => {
'name': name,
...
'tables': tables!=null? [tables] : [], // I think that I should modify this line
};
class Table:
Map<String, dynamic> toJson()=> { // Or this method / use another
'no_chairs': no_chairs,
...
};
Is there any simple way to do it? Preferably without modyfing firestore file structure, but if it is necessary I'm willing to do it.
Extra question:
How to modify object in tables list? For example I want to change no_chairs to 7
From your code above, the line below has a problem:
'tables': tables!=null? [tables] : [],
This part [tables] means you're putting the list tables as an element of the outer list.
To illustrate, if tables was as shown below:
List<int> tables = [1, 2, 3];
Then, [tables] is this:
[[1, 2, 3]]
Solution:
You can just use the tables variable directly since you're sending a list.
Before sending your tables object, you need to convert it to a list of Maps from the list of TableModel that is currently is (according to your comment below).
So you can modify your code to this:
'tables': tables != null ? tables.map((TableModel table) => table.toJson()).toList() : [],
Extra question: How to modify object in tables list? For example I
want to change no_chairs to 7
Solution:
You will have to get the actual list of restaurants and modify the exact table object you want and set the modified list to Firestore.
var collection = firestore.collection('usersData').doc(this.currentUser.uid).collection('buisness').doc('restaurants');
//Get list of restaurants
List<Restaurant> restaurantList = ((await collection.get()).data()).map((dynamic restaurantJson) => Restaurant.fromJson(restaurantJson)).toList();
Set number of chairs
//
restaurantList[restaurantIndex].tables[tableIndex].no_chairs = 7; //This assumes the no_chairs field is not final
//Set list
collection.set({
"restaurants": restaurantList.toJson()
})

Getting rank of user from firebase database in android

I have a real time firebase database that stores a list of users and it's stored in the following format(Json)
{
"users" : {
"-L29HeOZCmYu9UGJLMQR" : {
"name" : "John Doe",
"phoneNumber" : "+555-1566",
"points" : 21,
"rank" : 10,
"userId" : "-L29HeOZCmYu9UGJLMQR"
},
"-L2ASCuStoH7CTaqgBgG" : {
"name" : "Jenna Rose",
"phoneNumber" : "+555-3562",
"points" : 96,
"rank" : 0,
"userId" : "-L2ASCuStoH7CTaqgBgG"
},
....// A lot more users.
}
So my question is, how do I get the rank of the user in terms of the "points" variable? Also, how do I prepare a list of the top 10 or the top 100 etc..?
There is no way to get the rank of a specific user, without loading all users.
But you can get the top N users by using Firebase's query mechanism to sort and filter data. For example:
DatabaseReference ref = FirebaseDatabase.getInstance().getReference("users");
Query top10query = ref.orderByChild("points").limitToLast(10);
top10query.addChildEventListener(...
The children will be in ascending order. You'll have to revert them client-side, for example by adding each subsequent child to the top of the list.

Iterate two <Iterable> objects to compare data between them

I am working on with a firebase database and android studio.
I am making a recommendation system. The db has 26 travel destinations each with some attributes. (activities, region, weather, time of the year to go)
Destination number 1 : [ https://i.stack.imgur.com/hrkpP.png ]
Those are values that will never going to change.
On the other hand. i ask people a series of questions and they are being saved on the database. responses to questions 1,2, and 3: [ https://i.stack.imgur.com/HJgMn.png ]
I want to check on the responses if the response has anything in common with the destination features. and if it does, i want to increment an int called score ( i also created an arraylist Destination: is made with the name, the id from the destinations db and also has an int score)
Firebase lets me get an from a child that has many childs.
Iterable<DataSnapshot> destinosHijos = dataSnapshot.child("destinos").getChildren();
Iterable<DataSnapshot> destinosRes = dataSnapshot.child("respuestas").child(codigo).child(user).getChildren();
if i want to iterate two lists and compare something between them i would do something like this (hypotecally)
for(int i=0;i<destinationsDB.size();i++){
for(int j=0;j<responsesDB.size();j++){
String destinationR=destinationsDB.get(i).getRegion();
String responsesR=responsesDB.get(j).getRegion();
if(destinationR.equals(responsesR){
score++;
}
}
}
i can't do the same with de foreach. i tried to get all the destination's region and all the responses that had "region" in them. but when i use a foreach inside another is not the same with the forloop
if i try.
for (DataSnapshot hijo : destinosHijos) {
// all of the regions from all of the destinations
String destinoRegion= hijo.child("region").getValue().toString
}
for (DataSnapshot res : destinosRes) {
// all the responses with "region" on them
if (res.child("region").exists()) {
if(res.child("region").getValue.equals(destinoRegion)){
score++;
}
}
}
}
}
I modified it becuase the actual one is waay longer.
anyways. when i try to println - res.child("region").getValue.equals(region) - it only prints out 2 false times. that is 2 responses that have "region" on it.
instead of printint 2 * 26 = 72 times, that would be making comparing each response with each destination (that's what the forloop would do).
Thank you in advance if you read all that.

Realtime database get name and value

i am using firebase real-time database.
there is the user name like - user1 and then some supplies he requested like the supplies name - markers and then the quantity - like 5.
this is my JSON file
{
"Users" : {
"User1" : {
"Markers" : 5,
"Scissors" : 1,
"Staplers" : 4
},
"User2" : {
"Markers" : 2,
"Scissors" : 5,
"Staplers" : 3
}
}
}
i want to get back the supplies whan i ask for in the format of:
markers 5
scissors 1
Staplers 4
the information comes out via a listview but i don't know how to get via the listview both name and quantity, i can get only one of them.
The code i am using to get only the names is:
Set<String> set = new HashSet<String>();
Iterator i = dataSnapshot.getChildren().iterator();
while (i.hasNext()) {
set.add(((DataSnapshot) i.next()).getKey());
}
and the code i am using to get only the quantity is:
// two first lines are the same
while (i.hasNext()) {
set.add(((DataSnapshot) i.next()).getValue().toString());
}
Try something like this instead:
for (DataSnapshot supply : dataSnapshot.getChildren()) {
String key = supply.getKey();
String value = supply.getValue();
}
Also, you could have just saved i.next() to a variable, then you can access the key and value within the same loop without calling additional i.next() (which I presume is what the question is about).
i have changed the script to this one
key = (((DataSnapshot) i.next()).getKey());
value = ((dataSnapshot).child(key).getValue().toString());
set.add(key+" "+value);
it works very good, thanks any way.

Getting objects out from Firebase(Json)

I am new to firebase and Json so having some basic troubles with it. My Json file in Firebase is built like this:
{
"workout" : {
"Heavy Chest and Arms" : [ "Bench press", "Incline Dumbbell Press", "Cable Crossover", "Bicep Barbell Curls", "Alternate Dumbbell Curls", "Preachers Curls", "V-Bar Triceps Extensions", "Skull Crushers", "Sitting Calf raises" ],
"Light Back and Shoulders" : [ "Pull Ups", "Chins", "Cable Rows", "Lateral Pulldowns", "Dumbbell Rows", "Reverse Flies", "Arnold Press", "Side Raises", "Front Raises", "Rotary Cuffs" ]
}
}
First I want to get out a list with all the workouts, that list would contain Heavy Chest and Arms","Light Back and shoulders". After that I want to get out all the elements(child?) in those list, example "Bench press","Incline Dumbell press" etc. I tried several times for hours now experimenting with this:
myFirebaseRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
List<String> messages = dataSnapshot.getValue();
}
#Override
public void onCancelled(FirebaseError firebaseError) {
}
});
Here's a better structure to get you going
workouts
workout_id_0
category: "Heavy Chest and Arms"
elements
element_id_0: true
element_id_1: true
workout_id_1
category: "Light Back and Shoulders"
elements
element_id_0: true
elements
element_id_0
name: "Bench press"
description: "While laying flat on your back, arms at 90 degrees etc"
element_id_1
name: "Incline Dumbbell Press"
description: "Incline the backboard 30 degrees and etc etc"
You can do some snappy things with this structure:
Say you want to include a bench press in both the Heavy Chest and Light back workouts. This structure enables you to re-use your elements.
A year from now, you want to change the name of Bench press to Bench Press SUPER DUPER. You just change the name: within the element_id_0 and that falls into place
You can easily query for which workouts include element_id_1, the Incline dumbbell press
You can easily change the name of your workout. Instead of Light Back and Shoulders, it could be Light Lower Back and Shoulders with just changing one child node.
You should further consider how you want to use your data; do you need to find all elements that are just for the legs and create a workout based on those? Do you need to query or add additional data?
To print out all of the workout category names:
ref.on("child_added", function(snapshot, prevChildKey) {
var workout = snapshot.val();
console.log("Category: " + workout.category);
//the element references are here too! elements = workout.elements
});
It will take a little code to get the element names for each workout but this should get you going.
Firebase is a NoSQL database and as such should not be treated like a normalized SQL database.
To oversimplify you store data in the way you want to retrieve your data, even if that means saving the same data in separate places.
If you want to retrieve a list of workouts, have a workout names object that contains just the workout names (e.g. Heavy chest and arms, Light back and shoulders). Then if you want to retrieve the child elements, do so from another object that matches what you provided in the question.
Some good reading to cement this idea.

Categories

Resources