Getting objects out from Firebase(Json) - android

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.

Related

Firestore update only one field

I have a database. The sequence is: collections - document - hashmaps. For example:
users - the name of the collection
users.uid - the name of the document
Hashmap the document consists of a lot of hashmaps user data
Hashmap
Hashmap
etc
Hashmap: the name of user is the key and telephone, location etc are the values. I need to update only one field(location) for one username, but can't understand how do this?
I tried the next way (update phone number for alex):
User user = new User();
user.setPhone(131902331);
Map<String,RealmObject> userMap = new HashMap<>();
userMap.put("alex",user);
mFirebaseFirestore
.collection("users")
.document(mFirebaseAuth.getUid())
.set(userMap, SetOptions.merge())
.addOnSuccessListener(new OnSuccessListener<Void>() {
#Override
public void onSuccess(Void aVoid) {
LOG.info("Success");
}
})
.addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
LOG.error("Failure "+e.toString());
}
});
What I am doing wrong?
I know it's almost a year old question, but this might help someone. Use dot notation
db.collection("users")
.document("frank")
.update({
"age": 13,
"favorites.color": "Red"
});
When you call set() on a document, the existing contents of that documents are replaced with the data you pass in.
If you want to only update the values of the field you specify in a map, use update():
mFirebaseFirestore
.collection("users")
.document(mFirebaseAuth.getUid())
.update(userMap)
See the Firestore documentation on updating a document.
Please note that as of 2021, the syntax in MrAleister's answer has changed a bit if using firebase-firestore-ktx (see documentation). I did not edit answer because perhaps it is still valid for people using other versions of the library.
// Assume the document contains:
// {
// name: "Frank",
// favorites: { food: "Pizza", color: "Blue", subject: "recess" }
// age: 12
// }
//
// To update age and favorite color:
db.collection("users").document("frank")
.update(mapOf(
"age" to 13,
"favorites.color" to "Red"
))
I'm finding some odd notation in some of these answers. Maybe firestore parameters were updated? At any rate, I wanted to post an answer of my own.
If you want to update a single element in your document, do so with the below code:
db.collection("users").document("frank")
.update("age", 13);
The update() parameter accepts as many key/value pairs as you need, which are not separated by a colon, and are NOT put in some sort of array notation { } as shown in some of these answers. They are simply added in and separated by commas. See below:
.update( key , value , additionalkey, additionalvalue)
After update(), feel free to add your OnSuccess/OnFailure listeners as needed.
Dynamic nested reference:
def nest_update(new, parent):
return {parent+'.'+new: val for new, val in list(new.items())}
Usage:
old = {'nested': {'a': 123,'b': 456, 'c': 789}}
new = {'b': 0}
print('\nOld: ', old)
print('New: ', nest_update(new, 'nested'))
print('New should be: ', {'nested.b': 0})
# OUTPUT
Old: {'nested': {'a': 123, 'b': 456, 'c': 789}}
New: {'nested.b': 0}
New should be: {'nested.b': 0}

Android - Firebase Database Structure

I am developing an android application where older kids can pick up younger kids and walk to school. With the application the authenticated (email and password) younger kid can choose between three adresses to get picked up. As of right now my realtime database looks like this:
Should I make a new node named "Adresses" and have a structure like this below?
Adresses
Sherman Street
username: Jannie
Because I want to retrieve the name of the street and all the users that have chosen the adress in a listview
Your suggested method is good practice: you should try to flatten your data structure as much as possible.
I'd suggest using the user's ID for the membership of each address so it's easy to identify though. This way you can obtain a list of the members of "Sherman Street" from /addresses/Sherman Street and then match the keys listed within there to the users at /users/ with ease.
{
"users": {
"oXrJPVZsnMP3VKp9palSBfdnntk1": { ... },
"xQDx3ntavhV3c02KFRPS8lxYfC62": { ... }
},
"addresses": {
"Sherman Street": {
"members": {
"xQDx3ntavhV3c02KFRPS8lxYfC62": true
}
},
"Wallaby Way": {
"members": {
"oXrJPVZsnMP3VKp9palSBfdnntk1": true
}
}
}
}
You can also add backwards linking too by adding an address field to the user which matches the address name:
{
"users": {
"oXrJPVZsnMP3VKp9palSBfdnntk1": { ... },
"xQDx3ntavhV3c02KFRPS8lxYfC62": {
"username": "Jannie",
"address": "Sherman Street"
}
},
"addresses": {
"Sherman Street": { ... }
}
}
Using both together makes it easy to identify what users have selected which addresses, irrespective of which object you are currently handling within the app.
See the Firebase documentation on database structure for further details on structuring your data like this.

How to paginate in Firebase Database #AskFirebase

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);

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.

Android Firebase Listview ---> Hashmap Fail

I am trying to retrieve the Restaurant Name data from Firebase and output them in individual lines on ListView. I created a sample of the data which only consists of numbers(strings).
Retrieving the data seems fine as I could output them line by line in console, but my Hashmap is saving everything into the same "node" or "field"
Can anyone help me understand what I did wrong?
#Override
public void onDataChange(DataSnapshot Snapshot) {
int x = 1;
//Do some stuff once
for (DataSnapshot RestNames : Snapshot.getChildren()) {
name = RestNames.getValue().toString();
//System.out.println(name);
map.put(x, name);
x=x+1;
}
System.out.println(map);
Items.add(map);
System.out.println(Items);
listView.setAdapter(mgadapter);
}
The Output in the console is as follows :
{8=3456, 11=9, 9=34567, 5=3, 3=3, 4=4, 10=0, 1=1, 7=345, 6=34, 2=2}
Android emulator shows the same value for every single row.
I want to display each value on a separate row.
Thank you!
EDIT: SNIPPET OF JSON
{
"Eat": {
"Name": {
"-Jy3yehAkgqhg4knlxx_": "1",
"-Jy3yjQT2AxtZMqD2kov": "2",
"-Jy3yk96Mo5MKOEEzviJ": "3",
"-Jy3yksamL08R0BckxNZ": "4",
"-Jy5JBJYZUTxZQtmdDmi": "3",
"-Jy5JIXT_lDZrUOkF3T1": "34",
"-Jy5JJ0oMqGrs2vfFge2": "345",
"-Jy5JJTyET830PYOT3yA": "3456",
"-Jy5JJu-jDGMDXncWDKf": "34567",
"-Jy5JVejdsUtggM8vBoi": "0",
"-Jy5JbwEoWrKAi6XIVQY": "9"
}
}
}
Since we're missing some code in your snippet, I completed it an ran it locally:
Firebase ref = new Firebase("https://stackoverflow.firebaseio.com/32367022/Eat/Name");
ref.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot snapshot) {
int x = 1;
Map<Integer,Object> map = new HashMap<Integer, Object>();
for (DataSnapshot child: snapshot.getChildren()) {
String name = child.getValue().toString();
System.out.println(name);
map.put(x, name);
x=x+1;
}
System.out.println(map);
}
#Override
public void onCancelled(FirebaseError firebaseError) {
}
});
This prints out the following for me:
1
2
3
4
3
34
345
3456
34567
0
9
{1=1, 2=2, 3=3, 4=4, 5=3, 6=34, 7=345, 8=3456, 9=34567, 10=0, 11=9}
The first lines show the output from inside the for loop. The last lines shows the HashMap.
I use the most basic of Java classes, and this behavior seems correct to me.
I expect that the System.out.println(name); inside the for loop will display the same output as above for you, because the Firebase SDK handles ordering there.
If the order in the loop is correct, either your map or your Items object changes the order. It is impossible to say without seeing the types of these.
But in general, the approach above is how you should troubleshoot this problem: isolate it to a single object/class and then either fix what's wrong with that class (if it's your code) or replace it with a class that works for your use-case (if it comes from a library).
Update: I created a minimal app that shows the numbers in the correct order in a list view in this Github repo.

Categories

Resources