Apa cara terbaik untuk menyusun data di firebase?

111

Saya baru mengenal firebase dan ingin tahu apa cara terbaik untuk menyusun data di dalamnya.

Saya punya contoh sederhana:

Ada Pelamar dan Aplikasi di proyek saya. 1 pelamar dapat memiliki beberapa lamaran. Bagaimana cara menghubungkan 2 objek ini di firebase? Apakah ini berfungsi seperti database relasional? Atau pendekatannya harus benar-benar berbeda dalam hal desain data?

pelompat
sumber

Jawaban:

137

PEMBARUAN : Sekarang ada dokumen tentang penataan data . Juga, lihat posting luar biasa ini tentang struktur data NoSQL .

Masalah utama dengan data hierarkis, sebagai lawan dari RDBMS, adalah menggoda data bersarang karena kami bisa. Umumnya, Anda ingin menormalkan data sampai batas tertentu (seperti yang akan Anda lakukan dengan SQL) meskipun tidak ada pernyataan dan kueri gabungan.

Anda juga ingin melakukan denormalisasi di tempat-tempat yang mengutamakan efisiensi membaca. Ini adalah teknik yang digunakan oleh semua aplikasi berskala besar (misalnya Twitter dan Facebook) dan meskipun bertentangan dengan prinsip KERING kami, teknik ini umumnya merupakan fitur yang diperlukan dari aplikasi yang dapat diskalakan.

Intinya di sini adalah Anda ingin bekerja keras dalam menulis untuk memudahkan pembacaan. Jaga agar komponen logis yang dibaca terpisah terpisah (misalnya untuk ruang obrolan, jangan letakkan pesan, info meta tentang ruang, dan daftar anggota semua di tempat yang sama, jika Anda ingin dapat mengulang grup nanti).

Perbedaan utama antara data real-time Firebase dan lingkungan SQL adalah membuat kueri data. Tidak ada cara sederhana untuk mengatakan "PILIH PENGGUNA DI MANA X = Y", karena sifat data waktu nyata (terus berubah, memisahkan, merekonsiliasi, dll., Yang membutuhkan model internal yang lebih sederhana untuk menjaga klien yang disinkronkan tetap terkendali)

Contoh sederhana mungkin akan membuat Anda berada dalam kondisi pikiran yang benar, jadi begini:

/users/uid
/users/uid/email
/users/uid/messages
/users/uid/widgets

Sekarang, karena kita berada dalam struktur hierarki, jika saya ingin mengulang alamat email pengguna, saya melakukan sesuatu seperti ini:

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

Masalah dengan pendekatan ini adalah saya baru saja memaksa klien untuk mengunduh semua pengguna messagesdan widgetsjuga. Bukan masalah besar jika tidak ada yang jumlahnya ribuan. Tetapi masalah besar untuk 10k pengguna dengan masing-masing lebih dari 5k pesan.

Jadi sekarang strategi optimal untuk hierarki, struktur waktu nyata menjadi lebih jelas:

/user_meta/uid/email
/messages/uid/...
/widgets/uid/...

Alat tambahan yang sangat berguna dalam lingkungan ini adalah indeks. Dengan membuat indeks pengguna dengan atribut tertentu, saya dapat dengan cepat mensimulasikan kueri SQL hanya dengan mengulang indeks:

/users_with_gmail_accounts/uid/email

Sekarang jika saya ingin, katakanlah, mendapatkan pesan untuk pengguna gmail, saya bisa melakukan sesuatu seperti ini:

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

Saya menawarkan beberapa detail di posting SO lain tentang denormalisasi data, jadi periksa juga . Saya melihat bahwa Frank sudah memposting artikel Anant, jadi saya tidak akan mengulanginya di sini, tapi ini juga bacaan yang bagus.

Kato
sumber
Terima kasih atas wawasan ini Kato!
hopper
2
Untuk saat ini. Tampilan dalam rilis v2 Firebase akan berisi beberapa kemampuan hebat untuk mengotomatiskan proses itu.
Kato
Sadar bahwa saya menghidupkan kembali utas komentar lama di sini, tetapi saya berjuang untuk menemukan solusi yang lebih mutakhir. Apakah ini masih pendekatan terbaik? yaitu mendapatkan semua users_with_gmail_accounts dan kemudian menjalankan forEach?
owiewio
48

Firebase tidak seperti database relasional. Jika Anda ingin membandingkannya dengan apa pun, saya akan membandingkannya dengan database hierarki.

Anant baru-baru ini menulis postingan yang bagus di blog Firebase tentang denormalisasi data Anda: https://www.firebase.com/blog/2013-04-12-denormalizing-is-normal.html

Saya memang menyarankan untuk menyimpan "ID" dari setiap aplikasi sebagai anak dari setiap pelamar.

Frank van Puffelen
sumber
Terima kasih Frank! Ini sangat membantu. Persis apa yang saya cari!
hopper
4

Skenario Anda terlihat seperti satu ke banyak di dunia relasional, seperti contoh Anda, pelamar memiliki banyak aplikasi. Jika kita datang ke firebase nosql dengan cara seperti di bawah ini. Ini harus diskalakan tanpa masalah kinerja apa pun. Untuk itu diperlukan denormalisasi seperti yang disebutkan di bawah ini.

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

applications:{
application1:{
    .
    .
},
application2:{
    .
    .
},
application3:{
    .
    .
},
application4:{
    .
    .
}}
Prateep Gedupudi
sumber
Bagus tapi saya punya tindak lanjut, bagaimana kita membuat struktur ini dari Swift atau di mana saja menggunakan SDK Firebase? Juga bagaimana kita bisa memvalidasi bahwa data baru yang ditambahkan ke node aplikasi benar-benar ada di daftar aplikasi menggunakan aturan validasi Firebase?
Tommie C.
@prateep, Contoh yang bagus. Tapi di sini masalahnya adalah ketika saya menghapus jalur aplikasi / aplikasi1 di mana aplikasi1 adalah anak untuk beberapa pelamar. Jika saya mencoba mengakses jalur pelamar / aplikasi1 yang tidak ada. jadi Anda perlu memperbarui indeks di kedua tempat seperti application1: {pelamar: {pelamar1: true} ...} jadi sekarang ketika saya menghapus pelamar1 saya harus memeriksa itu pelamar anak dan memperbarui simpul anak pelamar untuk aplikasi. :)
Satish Sojitra