Let's say I have this JSON tree:
"employees":[
{"firstName":"John", "lastName":"Doe"},
{"firstName":"Anna", "lastName":"Smith"},
{"firstName":"Peter","lastName":"Jones"}
]
How can I do this in Firebase? Every time I create an object under "employees", with the name "firstname", it replaces the previous object with "Firstname".
I previously used Parse's tables, but since it's been taken down, then I need help learning this confusing thing.
I'm using Android.
Firebase databases have no native support for lists or arrays. If we try to store an list or an array, it really gets stored as an "object" with integers as the key names (see doc).
// we send this
['hello', 'world']
// Firebase databases store this
{0: 'hello', 1: 'world'}
In this way your tree in firebase would look like this:
{"employees":{
0:{"firstName":"John", "lastName":"Doe"},
1:{"firstName":"Anna", "lastName":"Smith"},
2:{"firstName":"Peter","lastName":"Jones"}
}
}
Using Firebase terminology we can say that node emloyees has three child nodes with IDs 0,1,2 respectively.
But saving data with integer IDs in Firebase is not recommended (see this to know why). Firebase provides a push() function that generates a unique ID every time a new child is added to the specified Firebase reference.
Here is an example from Firebase Android doc.:
//create firebase ref using your firebase url
Firebase ref = new Firebase("https://docs-examples.firebaseio.com/android/saving-data/fireblog");
Firebase postRef = ref.child("posts");
Map<String, String> post1 = new HashMap<String, String>();
post1.put("author", "gracehop");
post1.put("title", "Announcing COBOL, a New Programming Language");
postRef.push().setValue(post1);
Map<String, String> post2 = new HashMap<String, String>();
post2.put("author", "alanisawesome");
post2.put("title", "The Turing Machine");
postRef.push().setValue(post2);
And as a result in posts node we will have two childs with autogenerated ids:
{
"posts": {
"-JRHTHaIs-jNPLXOQivY": {
"author": "gracehop",
"title": "Announcing COBOL, a New Programming Language"
},
"-JRHTHaKuITFIhnj02kE": {
"author": "alanisawesome",
"title": "The Turing Machine"
}
}
}
You're probably looking for DatabaseReference.push(), which creates a new child under the location.
var employeesRef = mDatabase.child("employees");
var newEmployeeRef = employeesRef.push()
newEmployeeRef.setValue(employee);
The best place to read more about this is in the section on appending data to a list in the Firebase documentation.
Related
I'm looking for to return all exercices who contains a specific muscles group reference.
I tried this :
val db = FirebaseFirestore.getInstance()
db.collection("exercises")
.whereEqualTo("musclesGroups.hgMweNPXXXXXXXXX", true)
.addSnapshotListener({ value, e ->
Log.i("test", "Exercises " + value.documents.size)
})
But there is no result and no error, and size is 0.
There is no way to query for whether a certain value exists in an array. Have a look at the Firebase documentation on working with arrays, lists , and sets for an alternative data structure that allows you to meet your goals.
It looks like your query already comes from there, but your data structure doesn't follow the model outlined in that solution. To write the proper structure the documentation uses a Map with the values you want to filter for in the key, and true in the value:
Map<String, Boolean> categories = new HashMap<>();
categories.put("technology", true);
categories.put("opinion", true);
categories.put("cats", true);
MapPost myMapPost = new MapPost("My great post", categories);
Given the data structure below in firebase, i want to run a query to retrieve the blog 'efg'. I don't know the user id at this point.
{Users :
"1234567": {
name: 'Bob',
blogs: {
'abc':{..},
'zyx':{..}
}
},
"7654321": {
name: 'Frank',
blogs: {
'efg':{..},
'hij':{..}
}
}
}
The Firebase API only allows you to filter children one level deep (or with a known path) with its orderByChild and equalTo methods.
So without modifying/expanding your current data structure that just leaves the option to retrieve all data and filter it client-side:
var ref = firebase.database().ref('Users');
ref.once('value', function(snapshot) {
snapshot.forEach(function(userSnapshot) {
var blogs = userSnapshot.val().blogs;
var daBlog = blogs['efg'];
});
});
This is of course highly inefficient and won't scale when you have a non-trivial number of users/blogs.
So the common solution to that is to a so-called index to your tree that maps the key that you are looking for to the path where it resides:
{Blogs:
"abc": "1234567",
"zyx": "1234567",
"efg": "7654321",
"hij": "7654321"
}
Then you can quickly access the blog using:
var ref = firebase.database().ref();
ref.child('Blogs/efg').once('value', function(snapshot) {
var user = snapshot.val();
ref.child('Blogs/'+user+'/blogs').once('value', function(blogSnapshot) {
var daBlog = blogSnapshot.val();
});
});
You might also want to reconsider if you can restructure your data to better fit your use-case and Firebase's limitations. They have some good documentation on structuring your data, but the most important one for people new to NoSQL/hierarchical databases seems to be "avoid building nests".
Also see my answer on Firebase query if child of child contains a value for a good example. I'd also recommend reading about many-to-many relationships in Firebase, and this article on general NoSQL data modeling.
Given your current data structure you can retrieve the User that contains the blog post you are looking for.
const db = firebase.database()
const usersRef = db.ref('users')
const query = usersRef.orderByChild('blogs/efg').limitToLast(1)
query.once('value').then((ss) => {
console.log(ss.val()) //=> { '7654321': { blogs: {...}}}
})
You need to use limitToLast since Objects are sorted last when using orderByChild docs.
It's actually super easy - just use foreslash:
db.ref('Users').child("userid/name")
db.ref('Users').child("userid/blogs")
db.ref('Users').child("userid/blogs/abc")
No need of loops or anything more.
I'm using this query to fetch the posts by user A and sort by timestamp.
This below query fetches the posts by user A but it doesn't sort by date.
mDatabase = FirebaseDatabase.getInstance().getReference("posts");
String UID = "userA";
Query myTopPostsQuery = mDatabase.equalTo(UID).orderByChild("UID");
I tried using below query, but it returns an empty set.
Query myTopPostsQuery = mDatabase.equalTo(UID).orderByChild("date");
What is the right way to achieve my result?
This is my Data Structure:
posts
-KlgYXK01ezPjk
UID: "YiXgM3qgqcsd"
date: 1496428200000
post: "This is a Test Post by user A"
,
-KlgYXK01ezPpl
UID: "YiXgM3qgqcsd"
date: 1496428220022
post: "This is another Test Post by user A"
,
-KlgYXK01ezKjk
UID: "YiXCWsdj712"
date: 1496428203000
post: "This is a Test Post by user B"
,
Well this may not be the exact answer you are expecting but it helps when your app scales up.
I recommend you use a fan out data structure.
By this create a separate node user-posts where you store all the posts by individual users like below:
user-posts
-YiXgM3qgqcsd //this us A's UID
KlgYXK01ezPjket4ery62
post: "This is a Test Post by user A"
date: 1496428200000
author:'A'
KlgYXK01ezPjket4ery62
post: "This is a 2nd Test Post by user A"
date: 1496428500000
author:'A'
KlgYXK01ezPjket4ery62
post: "This is a 3rd Test Post by user A"
date: 1496428600000
author:'A'
-YiXCWsdj712 //this us B's UID
KlgYXK01ezPjket4ery62
post: "This is a Test Post by user B"
date: 1496428200000
author:'B'
Now you can query for A's posts lik this:
mDatabase = FirebaseDatabase.getInstance().getReference("user-posts");
String UID = "userA";
Query myTopPostsQuery = mDatabase.child(UID).limitToLast(10);
Since pushing data into a node creates a unique key which are by default in chronological order you don't have to worry about sorting by timeline as you are using limitToLast() which gives you posts from the bottom i.e latest
So its better you push data to different nodes posts and user-posts whenever a user creates a post. This is better as writing data is cheap in firebase as compared to reading data
Now you can just pull out data from ref "user-posts/UID" instead of firebase querying data fromposts filtering all the posts by user A then again ordering by timeline which will be expensive and slow if you have many number of posts
When it comes to pushing data to different nodes i.e posts and user-posts this could be cheap and you can use updateChildren() method like below:
Firebase ref = new Firebase("https://<YOUR-FIREBASE-APP>.firebaseio.com");
// Generate a new push ID for the new post
Firebase newPostRef = ref.child("posts").push();
String newPostKey = newPostRef.getKey();
// Create the data we want to update
Map newPost = new HashMap();
newPost.put("title", "New Post");
newPost.put("content", "Here is my new post!");
Map updatedUserData = new HashMap();
updatedUserData.put("user-posts/" + newPostKey, true);
updatedUserData.put("posts/" + newPostKey, newPost);
// Do a deep-path update
ref.updateChildren(updatedUserData, new Firebase.CompletionListener() {
#Override
public void onComplete(FirebaseError firebaseError, Firebase firebase) {
if (firebaseError != null) {
System.out.println("Error updating data: " + firebaseError.getMessage());
}
}
});
You could refer to the firebase blog post here
I want to add some certain data to a Firebase as arrays. Example:
groups : ['a','b','c']
How can I add and read data in Firebase from Android?
When you have a structure like that, you actually shouldn't be using an array to model it. It seems much more like a set in my eyes.
In the Firebase Database sets are best modeled as keys, since that automatically guarantees that items are unique. So your structure then becomes:
groups: {
"a": true,
"b": true,
"c": true
}
The true values are just markers, since Firebase won't allow you to store keys without a value.
Now to add a group to this, you'd use Firebase's setValue() function:
DatabaseReference root = FirebaseDatabase.getInstance().reference();
DatabaseReference groupsRef = root.child("groups");
groupsRef.child("d").setValue(true);
From the documentation:
setValue() - Record or change exists values
If you want to only append datas, you can to use updateChildren().
In Java, if we know that the data is array-like, it can be cast as a List:
Firebase julieRef = new Firebase("https://SampleChat.firebaseIO-demo.com/users/julie/");
julieRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot snapshot) {
GenericTypeIndicator<List<String>> t = new GenericTypeIndicator?<List<String>>() {};
List messages = snapshot.getValue(t);
if( messages === null ) {
System.out.println('No messages');
}
else {
System.out.println("The first message is: " + messages.get(0) );
}
}
// onCancelled...
});
Check this best practices post from the Firebase Blog.
I am new in noSQL and Firebase. But I want to build the structure of my database via Firebase.
I have users and a list of users lots. Structure with relationship.
So I did what's in the example:
String key = mDatabase.child("lots").push().getKey();
//create new lot
Lot lot = new Lot(fbUser.getUid(), fbUser.getEmail(), mMessage.getText().toString());
//turn to map
Map<String, Object> lotValues = lot.toMap();
Map<String, Object> childUpdates = new HashMap<>();
childUpdates.put("/lots/" + key, lotValues);
childUpdates.put("/user-lots/" + fbUser.getUid() + "/" + key, lotValues);
mDatabase.updateChildren(childUpdates);
But in the result I had this data to duplicate:
May be it's better to get this structure. I tried to find an example how to build one, because I do not want to to reinvent a wheel, but my efforts are futile so far.
Any suggestions? Thanks a lot.
What you're looking for is Pointers, which Firebase, believe it or not, DOES NOT have.
For example, if you want to have 3 lists of data:
My Posts
Recent Posts
Then you'll have to do it like this:
databaseRoot:{
Posts:{
{UNQ_KEY}:
{
title: "x",
description: "y",
authorUserID: {USERID1}
}
}
Users:{
{USERID1}
Posts:{
{UNQ_KEY_2}: {
title: "x",
description: "y"
}
}
}
}
When UNQ_KEY is created, you also create UNQ_KEY_2 under the user's userID.
To display "My Posts", you get the list under {USERID1}. To get "Recent Posts", you have to go to the Posts node.