Cách tốt nhất để cấu trúc dữ liệu trên firebase là gì?


111

Tôi mới sử dụng firebase và tôi muốn biết cách tốt nhất để cấu trúc dữ liệu trên đó là gì.

Tôi có một ví dụ đơn giản:

Có các ứng viên và ứng dụng trong dự án của tôi. 1 ứng viên có thể có nhiều ứng dụng. Làm cách nào để liên kết 2 đối tượng này trên firebase? Nó có hoạt động giống như một cơ sở dữ liệu quan hệ không? Hay cách tiếp cận cần phải khác hoàn toàn về thiết kế dữ liệu?

Câu trả lời:


137

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 messageswidgetsquá. 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.


Cảm ơn vì cái nhìn sâu sắc này Kato!
phễu

2
Trong thời điểm hiện tại. Các chế độ xem trong bản phát hành v2 của Firebase sẽ chứa một số khả năng tuyệt vời để tự động hóa quy trình đó.
Kato

Biết rằng tôi đang phục hồi một chuỗi nhận xét cũ ở đây, nhưng tôi đang đấu tranh để tìm một giải pháp cập nhật hơn. Đây vẫn là cách tiếp cận tốt nhất? tức là lấy tất cả user_with_gmail_accounts và sau đó chạy forEach?
owiewio

48

Firebase không giống như một cơ sở dữ liệu quan hệ. Nếu bạn muốn so sánh nó với bất kỳ thứ gì, tôi sẽ so sánh nó với cơ sở dữ liệu phân cấp.

Anant gần đây đã viết một bài đăng tuyệt vời trên blog Firebase về việc chuẩn hóa dữ liệu của bạn: https://www.firebase.com/blog/2013-04-12-denormalizing-is-normal.html

Tôi thực sự khuyên bạn nên giữ "ID" của mỗi ứng dụng khi là con của mỗi người nộp đơn.


Cảm ơn Frank! Điều này thực sự hữu ích. Chính xác những gì tôi đang tìm kiếm !
phễu

4

Kịch bản của bạn trông giống như một với nhiều trong thế giới quan hệ, như ví dụ của bạn, một ứng viên có nhiều ứng dụng. Nếu chúng ta đến với firebase nosql theo cách nó sẽ giống như bên dưới. Nó sẽ mở rộng mà không có bất kỳ vấn đề hiệu suất nào. Đó là lý do tại sao chúng ta cần không chuẩn hóa như đã đề cập bên dưới.

applicants:{
applicant1:{
    .
    .
    applications:{
        application1:true,
        application3:true
    }
},
applicant2:{
    .
    .
    applications:{
        application2:true,
        application4:true
    }
}}

applications:{
application1:{
    .
    .
},
application2:{
    .
    .
},
application3:{
    .
    .
},
application4:{
    .
    .
}}

Tốt nhưng tôi có một thông tin tiếp theo, làm cách nào để chúng tôi tạo cấu trúc này từ Swift hoặc bất kỳ nơi nào bằng cách sử dụng SDK Firebase? Ngoài ra, làm thế nào chúng tôi có thể xác nhận rằng dữ liệu mới được thêm vào nút ứng dụng thực sự tồn tại Trong danh sách ứng dụng bằng cách sử dụng quy tắc xác thực Firebase?
Tommie C.

@prateep, Ví dụ tốt. Nhưng vấn đề ở đây là khi tôi xóa các ứng dụng đường dẫn / application1 trong đó application1 là con đối với một số ứng viên. Nếu tôi cố gắng truy cập các ứng dụng / ứng dụng đường dẫn1 không có ở đó. vì vậy bạn cần cập nhật các chỉ mục ở cả hai nơi như application1: {application: {application1: true} ...} vì vậy bây giờ khi tôi xóa applicationion1, tôi phải kiểm tra đó là các ứng viên con và cập nhật nút con của các ứng viên cho ứng dụng. :)
Satish Sojitra
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.