Structure of Schema in Firebase - firebase

I have a strong background in relational databases. However, I'm always looking to improve my skills. Recently, I've been exposed to Firebase. It seems pretty interesting. However, I'm slightly confused by the "schema" if that's even the correct term.
From what I can tell, each Firebase "app" basically represents a single "table". Thus, if I am building a web application that has two related, but seperate entities, I would have to have two firebase "apps". For example, perhaps I am building a web application that has football teams, coaches and players. In a relational database, I may have something like this:
Relational Database
Team Coach TeamCoachLookup Player TeamPlayerLookup
---- ----- --------------- ------ ----------------
ID ID ID ID ID
Name FirstName TeamID FirstName TeamID
Location LastName CoachID LastName PlayerID
The above shows a possible relational database structure. Some may want to have a Person table with a RoleID to represent whether the person is a Player or a Coach. That's one approach. Still, when I look at the Firebase model, I have trouble getting my head around how the above would be structured. Would it be:
http://teams.firebaseio.com
http://coaches.firebaseio.com
http://players.firebaseio.com
Where the JSON of each item would represent a row in the database? Or, should it just be http://teams.firebaseio.com and the schema would look like this:
{
coaches: [
{ id:1, firstName:'Joe', lastName:'Smith' }
],
players: [
{ id:1, firstName:'Bill', lastName:'Mans' },
{ id:2, firstName:'Zack', lastName:'Dude' }
]
}
The second approach seems to make more sense to me. However, I do not see how Firebase supports that. Instead, in my mind, it looks like Firebase has one URL for each "table" and the JSON isn't really hierarchical. Am I way off? Is there any documentation that anyone can recommend to me?
Thanks!

The corresponding concepts should be (Firebase <=> relational):
application <=> schema
root node <=> table
child node <=> row
node key <=> row id (typically push ids)
In your concrete example:
football-app.firebaseio.com
teams
fx7Q7q
name: "Foo"
coaches
ix0GWF
firstName: "Joe"
lastName: "Smith"
players
uQ8fJK
firstName: "Bill"
lastName: "Mans"
teamCoachLookup
QkW9uH
team: "fx7Q7q"
coach: "ix0GWF"
teamPlayerLookup
BmI48N
team: "fx7Q7q"
player: "uQ8fJK"
See also https://www.firebase.com/docs/web/guide/structuring-data.html.

Related

DynamoDB Collection(s) Model

I am learning how to model a NoSQL databases and trying to understand how to make this works properly.
For my use case I have spin up a local instance of aws dynamo db and connected successfully.
Now, let's say I have two contexts "Users" and "Groups", where one user can belong to any group, and one group can have multiple users. I am trying to avoid multiple collections but I cannot see the way to.
What I have in mind is something like:
{
name: "Bob Smith",
dob: "1980-01-01
groups: [
{
name: "Sports"
CreatedAt: "2022-01-05"
}.
{
name: "Arts"
CreatedAt: "2022-01-08"
}
]
}
What I am not sure is what should we do if I need to create or update a Group? if I follow the previous pattern, how would be the query for the groups?
Shall I have 2 collections?
Thanks guys for your advice.
You would typically only hold a pointer to the groups within a users item, if you need to update the group details then you would do so in the item for the group.
pk
sk
groups
other
user123
USER#user123
groups[{pk:group1, sk: GROUP#group1}, {pk:group3, sk: GROUP#group3}]
user data
user009
USER#user009
groups[{pk:group5, sk: GROUP#group5}, {pk:group7, sk: GROUP#group7}]
user data
group1
GROUP#group1
group-info thats editable
group3
GROUP#group3
group-info thats editable
group5
GROUP#group5
group-info thats editable
group7
GROUP#group7
group-info thats editable
Of course this is not the only way to do this, but with all things NoSQL it totally depends on your access patterns. For example, if you have a requirement to get all users for a given group then you would need to change the above schema:
pk
sk
other
other
user123
USER#user123
user-data
user123
GROUP#group1
some data
user123
GROUP#group3
some data
group1
GROUP#group1
group data
group3
GROUP#group3
group data
Now you can get all the groups a user belongs to by issuing a Query stating:
pk=user123 AND sk BEGINS_WITH(GROUP#).
Then use a Global Secondary Index to get all the users which belong to a given group, with sk being your GSI's partition key:
gsi_pk = GROUP#group1

How to combine multiple firebase docs to get a combined result?

In my firebase db I have 3 collections:
Users
{user_id}: {name: "John Smith"}
Items
{item_id}: {value: 12345}
Actions
{action_id}: {action: "example", user: {user_id}, items:{item_id}}
Basically, instead of storing the Users and Items under the Actions Collection, I just keep an ID. But now I need a list of all actions and this also needs info from the Users and Items Collection. How can I efficiently query firebase so I can get a result that looks like this:
{
action: "example",
user: {
name: "John Smith"
},
item: {
value: 1234
}
}
Unfortunately, there is no such thing in firebase or a similar database, basically, you are looking for a traditional join, which is no recommended thing to do in a NoSQL database.
If you want to do it in firebase, you will need:
Get the element you are looking for from your main collection Actions in this case.
Then you need to do another call to the Items collections where item_id == action.item_id.
Then assign in the actions["Item"] = item_gotten.
This is not a recommended use as I said, usually, when you are using a NoSQL Database you are expecting a denormalize structure, from your application you need to save the whole Item, in the Action JSON, and also in the Item. Yes, you will have duplicate data but this is fine for this kind of model. also you shouldn't expect too many changes in one specific object within your whole object key If you are managing a big set of changes you could be using the incorrect kind of DB.
For aggregation queries reference, you might check: https://firebase.google.com/docs/firestore/solutions/aggregation

Firestore: How to keep data consistent between user and documents that have user information?

Summary
How could I model my database in Firebase to keep, for example, reviews in a specific page updated with the users info, this is, if a user changes it's avatar or name, the reviews should also display the updated data of the user.
I've used MongoDB most of the time, with Mongoose, and I am now working on a mobile app with Firebase. In Mongo I would just store a ref to the user in the review, and populate the field to retrieve the data I wanted from the document. Is there something like this in Firebase, and is it even a good or acceptable practice?
Quick Questions
Is there something like ".populate()" in Firebase?
Should I model the documents as much as possible to have the data that will be used in the view, and avoid "joins"?
Example
We have a users collection, and a store collection with reviews in it.
As far as I've read, you should minimize the doc reads, and so we should model our data with the specific values we need for the view were they will be used, so that we only need to do one query.
For the sake of simplification, let's say:
User has a name, email, avatar
users: {
user_id_1: {
email: "user1#gmail.com",
name: "John Doe",
avatar: "some_firestore_url"
}
}
Should the store collection:
Have nested collection of reviews like this
stores: {
store_id_1: {
name: "Dat Cool Store!",
reviews: {
user_id_1: {
name: "John Doe",
avatar: "some_firestore_url",
text: "Great store love it!",
timestamp: "May 07, 2020 at 03:30"
}
}
}
}
The problem I see with this, is that unless we use a function that updates every field in every document with the new values there is no other way to update the data in name and avatar.
Have the user_id in a field and query for the user information after:
stores: {
store_id_1: {
name: "Dat Cool Store!",
reviews: {
review_id_1: {
user: "user_id_1",
text: "Great store love it!",
timestamp: "May 07, 2020 at 03:30"
}
}
}
}
This is the mimicking the way I would do in MongoDB.
Sorry if some of it sounds confusing or I didn't explain myself the best way, but it's 4 o'clock in the morning here and I'm just trying to get it right :)
How could I model my database in Firebase to keep, for example, reviews in a specific page updated with the user's info, this is, if a user changes its avatar or name, the reviews should also display the updated data of the user.
Without knowing the queries you intend to perform, it's hard to provide a viable schema. We are usually structuring a Firestore database according to the queries that we want to perform.
In Mongo I would just store a ref to the user in the review, and populate the field to retrieve the data I wanted from the document. Is there something like this in Firebase, and is it even a good or acceptable practice?
Yes, there is. According to the official documentation regarding Firestore supported data-types, a DocumentReference is one of them, meaning that you can store only a path to a document and not the entire document. In the NoSQL world, it's quite common to duplicate data, so to have the same data in more than one place. Again, without knowing the use-case of your app it's hard to say whether using normalization it's better than holding only a reference. For a better understanding, I recommend you read my answer from the following post:
What is denormalization in Firebase Cloud Firestore?
And to answer your questions:
Is there something like ".populate()" in Firebase?
If you only store a DocumentReference, it doesn't mean that the data of the document that the reference is pointing to will be auto-populated. No, you first need to get the reference from the document, and right after that, based on that reference, you have to perform another database call, to actually get the data from the referenced document.
Should I model the documents as much as possible to have the data that will be used in the view, and avoid "joins"?
Yes, you should only store the data that you actually need to be displayed in your views. Regarding a JOIN clause, there isn't something like this supported in Firestore. A query can only get documents in a single collection at a time. If you want to get, for example, data from two collections, you'll have at least two queries to perform.
Another solution would be to add a third collection with data already merged from both collections so you can perform a single query. This is already explained in the link above.
Some other information that might be useful is explained in my answer from the following post:
Efficiency of searching using whereArrayContains
Where you can find the best practice to save data into a document, collection, or subcollection.
For me, the way I would go ahead with structuring my json collection also depends on the size of data, I am trying to store in the collection.
Let's say the number of users if small and I only want to support a thousand users. So in that case, I can go with this structure.
{
"store_id_1": {
"name": "Dat Cool Store!",
"reviews": [
{
"user_id_1": {
"name": "John Doe",
"avatar": "some_firestore_url"
},
"text": "Great store love it!",
"timestamp": "May 07, 2020 at 03:30"
},
{
"user_id_2": {
"name": "John Doe 2",
"avatar": "some_firestore_url 2"
},
"text": "Great store love it! TWO",
"timestamp": "May 27, 2020 at 03:30"
}
]
}
}
So now, you can have all the user info embedded in the stores collection. This will reduce your reads too.
But in case you want to scale it, then, I would suggest only store the users metadata and then make another read from users collection.
Hope this helps!

How to fetch all users, but from each user data in firebase

I am creating a hybrid app using firebase web settings.
This is an app for restaurants and the idea is that the user can do orders being at the restaurant and this order goes to a panel in the kitchen. I am using vue.js and when the user picks up an order, it is inserted into a store in VUEX called orderingNow = [ ]
This user is logged in and I have access to the user id.
In my db I have the followings refs:
ref('orders')
ref('users')
ref ('products')
The ref users there is just user basic info, like name, i inserted it when the user signs up.
ref products there are all the products and they are in this format:
{
productName: 'Ice cream',
qtd: null,
id: 223232
}
Ref orders there are objects containing the userId, theOrderid and theProduct id, ike this:
{
productId: 'dde',
userId: 7,
orderId: 'dsdfa'
}
what i need to do is, when the user sends the order, this order should go to somewhere on firebase and the kitchen needs to filter the orders for each user.
I have been searching and thinking how to it for weeks, but i cant find any solution.
Can you tell me if its correct so far? I mean 3 refs and the refs should be organised the way I did so far?
so far I am stuck in to this.
someone can help me with some logic for that?

firebase realtime schema design

i have two set of entities in my firebase realtime schema. Called Orders and customers.
so far i was not actually relating them in my app but was just showing them related. the current schema looked like:
{
"orders" : [
{"id" : 1, "name": "abc", "price": 200, "customer": "vik"}
],
"customers" : [
{"cust_id" : "10", "name" : "vik", "type": "existing"}
]
}
so i have a orders list page showing all the orders in a table which i get firing /orders.json
But practically, instead of having the customer name directly in the orders i should have cust_id attribute as that is the key.
That naturally makes it a standard relational schema where i will be free to change customer attributes without worrying about mismatch in orders.
However, the downside i see right away is that if i have say 20 orders to show in the order list table then instead of 1 i will end up firing 21 rest calls (1 to get order list and 20 to fetch customer name for each of the order)
What are the recommendations or standards around this ?
Firebase is a NoSQL database. So the rules of normalization that you know from relational databases don't necessarily apply.
For example: having the customer name in each order is actually quite normal. It saves having to do a client-side join for each customer record, significantly simplifying the code and improving the speed of the operation. But of course it comes at the cost of having to store data multiple times (quite normal in NoSQL databases), and having to consider if/how you update the duplicated data in case of updates of the customer record.
I recommend reading NoSQL data modeling, watching Firebase for SQL developers, and reading my answer on keeping denormalized data up to date.

Resources