How to design database structure when using firebase realtime database - boreddev


The article discusses some key concepts in the design of data structures and introduces some best practices when structuring JSON data in firebase realtime database.


To build an appropriate database structure requires you to think and calculate the data processing. Most importantly, you need to figure out how the data is stored and how to retrieve the data later to make the data processing process as easy as possible.


The way data data is structured is a JSON tree


All data stored in firebase realtime database are JSON objects. You can think of the database as a JSON tree stored in the cloud. Unlike SQL database, firebase does not have concepts like tables and records. When you add new data to a JSON tree, it becomes a node in the existing JSON structure, and is located by a key. You can create your own key keys like user IDs, or meaningful names, or let firebase create for you using childByAutoId.


Note: if you create the keys yourself, they must be UTF-8 encoded characters, and the maximum length is 768 bytes and cannot contain characters., $, #, (, ), /, or ASCII are in the range 0–31 or 127.


For example, consider a chat application that allows users to store their profile information and contact list. A user profile is located by a path, like /user/$uid. The alovelace user in the database should look like the following:


{
"users": {
"alovelace": {
"name": "Ada Lovelace",
"contacts": { "ghopper": true },
},
"ghopper": { ... },
"eclarke": { ... }
}
}

Although the database uses a JSON tree, the data type stored in the database can be expressed in basic data types corresponding to the JSON types.


Best practices when designing data structures


Avoid nesting data structures


Because the firebase realtime database allows nesting data with depth up to 32 levels, so you will probably think in favor of designing data structures in a nested style. However, when you retrieve data at a node in the database, you must also retrieve all of its child nodes. In addition, when you delegate to someone who is allowed to read, write or access a node in the database, you must give them permission to access all data in that node. Therefore, it is best to keep the flatten data structure as possible.


To visualize why nested data structures are not so good, consider the following example:


{
// Đây là cấu trúc dữ liệu lồng nhau, mỗi node con (cuộc hội thoại)
// trong chats node sẽ
// chứa kèm theo toàn bộ messages của cuộc hội thoại bên trong. Để
// lấy về title của cuả cuộc hội thoại, cần tải về hằng trăm
// megabytes dữ liệu của messages
"chats": {
"one": {
"title": "Historical Tech Pioneers",
"messages": {
"m1": { "sender": "ghopper", "message": "Relay malfunction found. Cause: moth." },
"m2": { ... },
// 1 danh sách rất dài các đoạn messages
}
},
"two": { ... }
}
}

With the above nested data design, data browsing will be problematic. For example, listing the titles of the chat conversations will need to retrieve the chats node: include all members and messages that need to be downloaded to the client (messages information is redundant in this business, and there is no need to download it). client).


Flatten data structures


If data data instead of nested design is separated into separate path locations (This process is called denormalization), then data will be downloaded to the client more efficiently by separate calls (This is what is needed to be done). Consider the following flatten data structure:


{
// Cuộc hội thoại chats chỉ nên chứa thông tin meta nhỏ gọn liên quan tới đoạn hội thoại mà thôi.
// thông tin này được lưu trữ bên trong 1 chats's unique ID
"chats": {
"one": {
"title": "Historical Tech Pioneers",
"lastMessage": "ghopper: Relay malfunction found. Cause: moth.",
"timestamp": 1459361875666
},
"two": { ... },
"three": { ... }
},
// Thông tin về members tham gia vào cuộc hội thoại sẽ trở nên dễ dàng truy cập hơn,
// và được lưu trữ trong 1 chat conversation ID
"members": {
// we'll talk about indices like this below
"one": {
"ghopper": true,
"alovelace": true,
"eclarke": true
},
"two": { ... },
"three": { ... }
},
// Messages được phân tách ra, do ta muốn duyệt nó nhanh hơn, nhưng vẫn dễ dàng cho việc phân trang và truy vấn,
// Hay nhóm gộp theo chat conversation ID
"messages": {
"one": {
"m1": {
"name": "eclarke",
"message": "The relay seems to be malfunctioning.",
"timestamp": 1459361875337
},
"m2": { ... },
"m3": { ... }
},
"two": { ... },
"three": { ... }
}
}

Now you can browse the list of rooms by downloading a few bytes per conversation, quickly getting metadata information for listing or displaying rooms in a UI interface. Messages can be retrieved individually and displayed when they arrive, allowing the UI to respond quickly and effectively.


How to create data that can be scaled


When building apps, it's best to download only a subset of a list of data. This is a very common case when the list contains thousands of records. When each relationship is static and one way, you can simply nest child objects into a parent node.


Occasionally, the relationship becomes more dynamic, and may need to optimize the data structure to manipulate the processing to be optimal.


Consider the example of a two-way (many-to-many) relationship between users and groups. Users can belong to one or more groups and groups can include a list of users (users who have n-n relationships with groups). When we need to consider which groups users belong to, the problem becomes more complex.


The best way is to list out the groups the user belongs to and simply retrieve the data for those groups. An index listing of groups within the user can make this easier to handle:


// users có quan hệ n-n với groups
{
"users": {
"alovelace": {
"name": "Ada Lovelace",
// alovelace user có chứa thông tin lưu mối quan hệ với groups
"groups": {
// giá trị value của các trường fields tại đây không quan
// trọng, bạn chỉ cần quan tâm trường fields có tồn tại hay
//không mà thôi
"techpioneers": true,
"womentechmakers": true
}
},
...
},
"groups": {
"techpioneers": {
"name": "Historical Tech Pioneers",
"members": {
"alovelace": true,
"ghopper": true,
"eclarke": true
}
},
...
}
}

You may notice that there is redundancy of data by storing relationship relationships on both the user and the group. Hours alovelace The user is stored inside the group and techpioneers The group is also stored in the user. So when deleting alovelace from the group, then you need to update both in 2 places.


This is an essential data redundancy for a two-dimensional (n-n) relationship, which allows us to obtain information. alovelace'S memberships effectively and quickly, or even taking out the list of users or groups, it still works.


According to this approach, browsing data by listing IDs is like key keys and setting their value by true, checking key keys in a simple way by reading /users/$uid/groups/$group_id and check if it is null or not.


Source: https://firebase.google.com/docs/database/web/structure-data


0 Comments

×