วิธีใดที่ดีที่สุดในการจัดโครงสร้างข้อมูลบน Firebase


111

ฉันเพิ่งเริ่มใช้ firebase และฉันต้องการทราบว่าวิธีใดเป็นวิธีที่ดีที่สุดในการจัดโครงสร้างข้อมูล

ฉันมีตัวอย่างง่ายๆ:

มีผู้สมัครและใบสมัครในโครงการของฉัน ผู้สมัคร 1 คนสามารถสมัครได้หลายใบ ฉันจะเชื่อมโยง 2 ออบเจ็กต์นี้กับ firebase ได้อย่างไร มันทำงานเหมือนฐานข้อมูลเชิงสัมพันธ์หรือไม่? หรือแนวทางต้องแตกต่างกันอย่างสิ้นเชิงในแง่ของการออกแบบข้อมูล?

คำตอบ:


137

UPDATE : ขณะนี้มีเอกสารเกี่ยวกับการจัดโครงสร้างข้อมูล นอกจากนี้ยังเห็นโพสต์ที่ยอดเยี่ยมนี้NoSQL โครงสร้างข้อมูล

ปัญหาหลักเกี่ยวกับข้อมูลแบบลำดับชั้นซึ่งตรงข้ามกับ RDBMS คือการดึงดูดข้อมูลแบบซ้อนกันเพราะเราทำได้ โดยทั่วไปคุณต้องการทำให้ข้อมูลเป็นปกติในระดับหนึ่ง (เช่นเดียวกับที่คุณทำกับ SQL) แม้ว่าจะไม่มีคำสั่งเข้าร่วมและแบบสอบถาม

นอกจากนี้คุณยังต้องการที่จะdenormalizeในสถานที่ที่มีประสิทธิภาพในการอ่านเป็นกังวล นี่เป็นเทคนิคที่ใช้โดยแอปขนาดใหญ่ทั้งหมด (เช่น Twitter และ Facebook) และแม้ว่าจะขัดกับหลักการ DRY ของเรา แต่โดยทั่วไปแล้วก็เป็นคุณสมบัติที่จำเป็นของแอปที่ปรับขนาดได้

สาระสำคัญคือคุณต้องการทำงานอย่างหนักในการเขียนเพื่อให้อ่านง่าย เก็บส่วนประกอบทางตรรกะที่อ่านแยกกัน (เช่นสำหรับห้องสนทนาอย่าใส่ข้อความข้อมูลเมตาเกี่ยวกับห้องและรายชื่อสมาชิกทั้งหมดไว้ในที่เดียวกันหากคุณต้องการให้สามารถทำซ้ำกลุ่มได้ในภายหลัง)

ความแตกต่างหลักระหว่างข้อมูลเรียลไทม์ของ Firebase และสภาพแวดล้อม SQL คือการสืบค้นข้อมูล ไม่มีวิธีง่ายๆในการพูดว่า "เลือกผู้ใช้ WHERE X = Y" เนื่องจากลักษณะของข้อมูลแบบเรียลไทม์ (มีการเปลี่ยนแปลงตลอดเวลาแตกต่างกันการกระทบยอด ฯลฯ ซึ่งต้องใช้โมเดลภายในที่ง่ายกว่าเพื่อให้ไคลเอ็นต์ที่ซิงโครไนซ์อยู่ในการตรวจสอบ)

ตัวอย่างง่ายๆอาจทำให้คุณอยู่ในสภาพที่ถูกต้องได้ดังนี้

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

ตอนนี้เนื่องจากเราอยู่ในโครงสร้างลำดับชั้นหากฉันต้องการย้ำที่อยู่อีเมลของผู้ใช้ฉันจึงทำสิ่งนี้:

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

ปัญหาของวิธีนี้คือฉันเพิ่งบังคับให้ไคลเอนต์ดาวน์โหลดผู้ใช้ทั้งหมดmessagesและwidgetsด้วย ไม่มีเรื่องใหญ่ถ้าไม่มีสิ่งเหล่านั้นเป็นพัน ๆ แต่เป็นเรื่องใหญ่สำหรับผู้ใช้ 10k ที่มีข้อความมากกว่า 5k ต่อข้อความ

ตอนนี้กลยุทธ์ที่ดีที่สุดสำหรับโครงสร้างตามลำดับชั้นแบบเรียลไทม์จึงชัดเจนยิ่งขึ้น:

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

เครื่องมือเพิ่มเติมที่มีประโยชน์อย่างยิ่งในสภาพแวดล้อมนี้คือดัชนี ด้วยการสร้างดัชนีของผู้ใช้ที่มีคุณสมบัติบางอย่างฉันสามารถจำลองแบบสอบถาม SQL ได้อย่างรวดเร็วโดยการทำดัชนีซ้ำ:

/users_with_gmail_accounts/uid/email

ตอนนี้ถ้าฉันต้องการพูดรับข้อความสำหรับผู้ใช้ gmail ฉันสามารถทำสิ่งนี้ได้:

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

ผมเสนอรายละเอียดบางอย่างในโพสต์ SO อื่นเกี่ยวกับ denormalizing ข้อมูลเพื่อตรวจสอบผู้ออกเช่นกัน ฉันเห็นว่าแฟรงค์โพสต์บทความของ Anant แล้วดังนั้นฉันจะไม่ย้ำตรงนี้ แต่ก็เป็นการอ่านที่ยอดเยี่ยมเช่นกัน


ขอบคุณสำหรับข้อมูลเชิงลึก Kato!
กระโดด

2
ในขณะนี้ มุมมองใน Firebase รุ่น v2 จะมีความสามารถที่ยอดเยี่ยมบางอย่างสำหรับการทำให้กระบวนการนั้นเป็นไปโดยอัตโนมัติ
Kato

โปรดทราบว่าฉันกำลังรื้อฟื้นชุดความคิดเห็นเก่าที่นี่ แต่ฉันกำลังดิ้นรนเพื่อหาวิธีแก้ไขที่ทันสมัยกว่านี้ วิธีนี้ยังเป็นแนวทางที่ดีที่สุดหรือไม่? คือรับ users_with_gmail_accounts ทั้งหมดแล้วเรียกใช้ forEach?
owiewio

48

Firebase ไม่เหมือนกับฐานข้อมูลเชิงสัมพันธ์ หากคุณต้องการเปรียบเทียบกับสิ่งใด ๆ ฉันจะเปรียบเทียบกับฐานข้อมูลแบบลำดับชั้น

Anant เพิ่งเขียนโพสต์ที่ยอดเยี่ยมในบล็อก Firebase เกี่ยวกับการทำให้ข้อมูลของคุณเสียปกติ: https://www.firebase.com/blog/2013-04-12-denormalizing-is-normal.html

ฉันขอแนะนำให้เก็บ "ID" ของแต่ละใบสมัครไว้เป็นลูกของผู้สมัครแต่ละคน


ขอบคุณแฟรงค์! นี่เป็นประโยชน์จริงๆ สิ่งที่ฉันกำลังมองหา!
กระโดด

4

สถานการณ์ของคุณดูเหมือนหนึ่งต่อหลายคนในโลกเชิงสัมพันธ์ตามตัวอย่างของคุณผู้สมัครมีแอปพลิเคชันมากมาย ถ้าเรามาที่ firebase nosql จะมีลักษณะดังนี้ ควรปรับขนาดโดยไม่มีปัญหาด้านประสิทธิภาพ นั่นเป็นเหตุผลที่เราต้องการการทำให้เป็นปกติตามที่ระบุไว้ด้านล่าง

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

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

ดี แต่ฉันมีสิ่งที่ตามมาเราจะสร้างโครงสร้างนี้จาก Swift หรือที่ใดก็ได้โดยใช้ Firebase SDK ได้อย่างไร นอกจากนี้เราจะตรวจสอบได้อย่างไรว่าข้อมูลใหม่ที่เพิ่มในโหนดแอปพลิเคชันมีอยู่จริงในรายการแอปพลิเคชันโดยใช้กฎการตรวจสอบความถูกต้องของ Firebase
ทอมมีค

@ ประทีปตัวอย่างที่ดี. แต่ปัญหาที่นี่คือเมื่อฉันลบ path applications / application1 โดยที่ application1 เป็นลูกของผู้สมัครบางคน หากฉันพยายามเข้าถึงเส้นทางผู้สมัคร / application1 ซึ่งไม่มีอยู่ ดังนั้นคุณต้องอัปเดตดัชนีในทั้งสองที่เช่น application1: {ผู้สมัคร: {ผู้สมัคร 1: true} ... } ดังนั้นตอนนี้เมื่อฉันลบผู้สมัคร 1 ฉันต้องตรวจสอบว่าเป็นผู้สมัครลูกและอัปเดตโหนดลูกของผู้สมัครสำหรับการสมัคร :)
Satish Sojitra
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.