Build the data structure with relationship via Firebase - android

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.

Related

Return documents using references into an array

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

How to set epoch in Firestore using server time

I'm trying to set the epoch when data is created in Firestore. I'm looking to get some similar result to what is done in the real-time database, using ServerValue.TIMESTAMP.
I don't want to set it by using the device time System.getCurrentMillis because that time can be changed by the user.
According to docs an update needs to be done, the problem with that is the format. This is my code:
Map<String, Object> map = new HashMap<>();
map.put("timestamp", FieldValue.serverTimestamp());
reference.update(map);
And this is the result in the Firebase web console:
I was very surprised it is in spanish, which could be useful in some situations but epoch is what I'm chasing. Try to see the bright side and stick with it and thought that I was seeing the web in spanish, so I changed the language in the footer selector, it didn't change. On this point I'm assuming is set in the project language.
Back to the epoch attempt. Considering my project is using the real-time database as well, I try to set it in that way:
Map<String, Object> map = new HashMap<>();
map.put("timestamp", ServerValue.TIMESTAMP);
reference.update(map);
It did upload something, but it was just nonsense.
I think using epoch as the server-side timestamp is a better standard approach, after that every client can transform it to the user convenience and locale.
Can the epoch by set as server value in Firestore?
UPDATE
The answer marked as correct lead me to some interesting findings that I would like to share, so others in the same situation can benefit from:
There is no need to set the epoch because of the FieldValue.serverTimestamp() it is a date object handled by the database, what we see in the console is just a friendly way to show it.
Since FieldValue.serverTimestamp() is a date object it can be sort as any other timestamp could be, if you add orderBy("timestamp", Query.Direction.DESCENDING) to your query (or Query.Direction.ASCENDING) it will sort the results correctly.
And regarding to a the #34m0 comment, that is right, clients should not take care of the logic for setting the creation time, but it should be done in Functions.
The object that results from setting a Firestore field with FieldValue.serverTimestamp() is an instance of java.util.Date. When you later read the value, you can get the epoch time using getTime().
As an example, for a document created like this:
Map<String, Object> doc = new HashMap<>();
doc.put("timestamp", FieldValue.serverTimestamp());
The resulting value can be read like this:
docRef.get().addOnCompleteListener(new OnCompleteListener<DocumentSnapshot>() {
#Override
public void onComplete(#NonNull Task<DocumentSnapshot> task) {
if (task.isSuccessful()) {
DocumentSnapshot snapshot = task.getResult();
if (snapshot != null) {
Map<String,Object> map = snapshot.getData();
Date date = (Date) map.get("timestamp");
Log.d(TAG, "date=" + date);
Log.d(TAG, "time=" + date.getTime());
} else {
Log.d(TAG, "No such document");
}
} else {
Log.d(TAG, "get() failed with ", task.getException());
}
}
});

Couchbase Lite: reading document vs querying data

I'm switching an Android project to using Couchbase Lite, and I'm confused with ways for fetching the data from the database.
I have a document, which contains only one property:
Map<String, Object> properties = new HashMap<String, Object>();
properties.put("data_key", json);
Document document = database.getDocument("data_doc_id");
document.putProperties(properties);
The next my step is getting the stored data from the database. I found two ways:
The first approach is reading the document
Document doc = database.getDocument("data_doc_id");
String json = (String) doc.getProperty("data_key");
The second one is querying
View view = database.getView("data_json");
view.setMap(new Mapper() {
#Override
public void map(Map<String, Object> document, Emitter emitter) {
if ("data_doc_id".equals(document.get("_id")) {
String json = (String) doc.getProperty("data_key");
emitter.emit("data_key", json);
}
}
}, "1.0");
QueryEnumerator queryEnumerator = view.createQuery().run();
String dataJson = "";
for (QueryRow queryRow : run) {
if ("data_key".equals(queryRow.getKey()) {
json = (String) queryRow.getValue();
}
}
Is it okay to use the first approach to get the stored JSON?
For what cases the second approach should be used? It has far more code than the first one, maybe it has something to do with caching or/and speed/performance? What are the pros and cons of this approach?
The first approach is retrieving the document directly. This is the fastest way to do it.
With databases, though, often you want to retrieve documents based on some feature of the data. To do this you want the ability to create queries about the data. That's what the second approach is for.

Firebase where condition with orderBy in Android

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

Cannot seem to understand Firebase's JSON table

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.

Categories

Resources