CẬP NHẬT : Hiện đã có tài liệu về cấu trúc dữ liệu . Ngoài ra, hãy xem bài đăng xuất sắc này về cấu trúc dữ liệu NoSQL .
Vấn đề chính với dữ liệu phân cấp, trái ngược với RDBMS, là nó rất hấp dẫn để lồng dữ liệu vì chúng ta có thể. Nói chung, bạn muốn chuẩn hóa dữ liệu ở một mức độ nào đó (giống như bạn làm với SQL) mặc dù thiếu câu lệnh và truy vấn nối.
Bạn cũng muốn không chuẩn hóa ở những nơi mà hiệu quả đọc là mối quan tâm. Đây là một kỹ thuật được sử dụng bởi tất cả các ứng dụng quy mô lớn (ví dụ như Twitter và Facebook) và mặc dù nó đi ngược lại các nguyên tắc KHÔ của chúng tôi, nhưng nói chung nó là một tính năng cần thiết của các ứng dụng có thể mở rộng.
Ý chính ở đây là bạn muốn làm việc chăm chỉ để viết để làm cho việc đọc dễ dàng. Giữ các thành phần logic được đọc tách biệt riêng biệt (ví dụ: đối với phòng trò chuyện, không đặt các tin nhắn, thông tin meta về các phòng và danh sách thành viên vào cùng một nơi, nếu bạn muốn có thể lặp lại các nhóm sau này).
Sự khác biệt chính giữa dữ liệu thời gian thực của Firebase và môi trường SQL là truy vấn dữ liệu. Không có cách nào đơn giản để nói "CHỌN NGƯỜI DÙNG TẠI ĐÂU X = Y", vì tính chất thời gian thực của dữ liệu (dữ liệu liên tục thay đổi, sắc nét, điều chỉnh, v.v., đòi hỏi một mô hình nội bộ đơn giản hơn để kiểm soát các ứng dụng khách được đồng bộ hóa)
Một ví dụ đơn giản có thể sẽ đưa bạn vào trạng thái tinh thần phù hợp, vì vậy đây là:
/users/uid
/users/uid/email
/users/uid/messages
/users/uid/widgets
Bây giờ, vì chúng ta đang ở trong một cấu trúc phân cấp, nếu tôi muốn lặp lại địa chỉ email của người dùng, tôi sẽ làm như sau:
// I could also use on('child_added') here to great success
// but this is simpler for an example
firebaseRef.child('users').once('value')
.then(userPathSnapshot => {
userPathSnapshot.forEach(
userSnap => console.log('email', userSnap.val().email)
);
})
.catch(e => console.error(e));
Vấn đề với cách tiếp cận này là tôi vừa buộc khách hàng để tải về tất cả các người sử dụng messages
và widgets
quá. Không có vấn đề gì nếu không có thứ nào trong số đó lên tới hàng nghìn. Nhưng một vấn đề lớn đối với 10k người dùng với hơn 5 nghìn tin nhắn mỗi người.
Vì vậy, bây giờ chiến lược tối ưu cho cấu trúc phân cấp, thời gian thực trở nên rõ ràng hơn:
/user_meta/uid/email
/messages/uid/...
/widgets/uid/...
Một công cụ bổ sung cực kỳ hữu ích trong môi trường này là các chỉ số. Bằng cách tạo chỉ mục người dùng có các thuộc tính nhất định, tôi có thể nhanh chóng mô phỏng một truy vấn SQL bằng cách chỉ cần lặp lại chỉ mục:
/users_with_gmail_accounts/uid/email
Bây giờ, nếu tôi muốn, giả sử, nhận tin nhắn cho người dùng gmail, tôi có thể làm như sau:
var ref = firebase.database().ref('users_with_gmail_accounts');
ref.once('value').then(idx_snap => {
idx_snap.forEach(idx_entry => {
let msg = idx_entry.name() + ' has a new message!';
firebase.database().ref('messages').child(idx_entry.name())
.on(
'child_added',
ss => console.log(msg, ss.key);
);
});
})
.catch(e => console.error(e));
Tôi đã cung cấp một số chi tiết trong một bài đăng SO khác về việc chuẩn hóa dữ liệu, vì vậy hãy kiểm tra những điều đó . Tôi thấy rằng Frank đã đăng bài báo của Anant, vì vậy tôi sẽ không nhắc lại điều đó ở đây, nhưng nó cũng là một bài đọc tuyệt vời.