คู่มือ���ี้ครอบคลุมแนวคิดหลักบางประการเกี่ยวกับสถาปัตยกรรมข้อมูลและ แนวทางปฏิบัติในการจัดโครงสร้างข้อมูล JSON ใน Firebase Realtime Database
การสร้างฐานข้อมูลที่มีโครงสร้างอย่างเหมาะสมต้องอาศัยความรอบคอบพอสมควร สิ่งสำคัญที่สุดคือคุณต้องวางแผน วิธีบันทึกข้อมูล และ ที่ดึงออกมาในภายหลังเพื่อให้กระบวนการดังกล่าวง่ายที่สุด
ข้อมูลมีโครงสร้างอย่างไร: เป็นแผนผัง JSON
ข้อมูล Firebase Realtime Database ทั้งหมดจะจัดเก็บเป็นออบเจ็กต์ JSON ลองนึกถึง
ฐานข้อมูลเป็นแผนผัง JSON ที่โฮสต์บนระบบคลาวด์ ต่างจากฐานข้อมูล SQL ตรงที่ไม่มี
ตารางหรือระเบียน เมื่อคุณเพิ่มข้อมูลลงในโครงสร้าง JSON ข้อมูลนั้นจะกลายเป็นโหนดใน
โครงสร้าง JSON ที่มีอยู่
ด้วยคีย์ที่เชื่อมโยง คุณสามารถใส่คีย์ของตัวเอง
เช่น รหัสผู้ใช้หรือชื่อความหมาย หรืออาจมีการระบุให้คุณโดยใช้
push()
หากคุณสร้างคีย์ของคุณเอง คีย์ดังกล่าวจะต้องเ��้����หัส������� UTF-8 และมีขีดจำกัดจำนวนสูงสุด
768 ไบต์ และต้องไม่มีการควบคุม .
, $
, #
, [
, ]
, /
หรือ ASCII
0-31 หรือ 127 อักขระ คุณไม่สามารถใช้อักขระควบคุม ASCII ในค่าได้
ด้วยตนเอง อย่างใดอย่างหนึ่ง
ตัวอย่างเช่น พิจารณาแอปพลิเคชันสำหรับแชทที่ช่วยให้ผู้ใช้จัดเก็บ
และข้อมูลติดต่อ โปรไฟล์ผู้ใช้ทั่วไปจะอยู่ที่เส้นทาง เช่น
/users/$uid
ผู้ใช้ alovelace
อาจมีรายการฐานข้อมูลที่
ซึ่งจะมีลักษณะดังนี้
{ "users": { "alovelace": { "name": "Ada Lovelace", "contacts": { "ghopper": true }, }, "ghopper": { ... }, "eclarke": { ... } } }
แม้ว่าฐานข้อมูลจะใช้โครงสร้าง JSON แต่ข้อมูลที่จัดเก็บในฐานข้อมูลสามารถ แสดงเป็นประเภทเนทีฟบางประเภทซึ่งตรงกับประเภท JSON ที่ใช้ได้ เพื่อช่วยให้คุณเขียนโค้ดที่ดูแลรักษาได้มากขึ้น
แนวทางปฏิบัติแนะนำสำหรับโครงสร้างข้อมูล
หลีกเลี่ยงการซ้อนข้อมูล
เนื่องจาก Firebase Realtime Database อนุญาตให้ฝังข้อมูลได้ลึกถึง 32 ระดับ คุณอาจต้องคิดว่านี่เป็นโครงสร้างเริ่มต้น อย่างไรก็ตาม เมื่อคุณดึงข้อมูลที่ตำแหน่งหนึ่งในฐานข้อมูลของคุณ คุณจะเรียกข้อมูล โหนดย่อยทั้งหมด นอกจากนี้ เมื่อคุณให้สิทธิ์อ่านหรือเขียนแก่ผู้ใช้ ที่โหนดในฐานข้อมูลของคุณ คุณยังให้สิทธิ์พวกเขาในการเข้าถึงข้อมูลทั้งหมดภายใต้ ดังนั้น ในทางปฏิบัติแล้ว คุณควรทำให้โครงสร้างข้อมูลเป็นแบบแนวราบ ให้มากที่สุด
ลองดูตัวอย่างสาเหตุที่ข้อมูลที่ซ้อนกันไม่ถูกต้อง ดังนี้ โครงสร้างที่ซ้อนกัน:
{ // This is a poorly nested data architecture, because iterating the children // of the "chats" node to get a list of conversation titles requires // potentially downloading hundreds of megabytes of messages "chats": { "one": { "title": "Historical Tech Pioneers", "messages": { "m1": { "sender": "ghopper", "message": "Relay malfunction found. Cause: moth." }, "m2": { ... }, // a very long list of messages } }, "two": { ... } } }
การออกแบบเชิงซ้อนนี้ทำให้ข้อมูลซ้ำๆ กลายเป็นปัญหาตามมา สำหรับ
เช่น ชื่อของการสนทนาผ่านแชทจะต้องใช้ chats
ทั้งหมด
แผนผังซึ่งรวมถึงสมาชิกและข้อความทั้งหมด จะดาวน์โหลดไปยังไคลเอ็นต์
แยกโครงสร้างข้อมูล
หากมีการแยกข้อมูลเป็นเส้นทางแยกต่างหาก หรือที่เรียกว่าการดีนอร์มัลไลซ์ จะสามารถดาวน์โหลดได้อย่างมีประสิทธิภาพในการโทรแยกต่างหาก ตามความจำเป็น พิจารณา โครงสร้างที่แบ่งเป็นหลายรายการนี้
{ // Chats contains only meta info about each conversation // stored under the chats's unique ID "chats": { "one": { "title": "Historical Tech Pioneers", "lastMessage": "ghopper: Relay malfunction found. Cause: moth.", "timestamp": 1459361875666 }, "two": { ... }, "three": { ... } }, // Conversation members are easily accessible // and stored by chat conversation ID "members": { // we'll talk about indices like this below "one": { "ghopper": true, "alovelace": true, "eclarke": true }, "two": { ... }, "three": { ... } }, // Messages are separate from data we may want to iterate quickly // but still easily paginated and queried, and organized by chat // conversation ID "messages": { "one": { "m1": { "name": "eclarke", "message": "The relay seems to be malfunctioning.", "timestamp": 1459361875337 }, "m2": { ... }, "m3": { ... } }, "two": { ... }, "three": { ... } } }
ตอนนี้คุณสามารถทำซ้ำผ่านรายการห้องแชทได้ด้วยการดาวน์โหลดเฉพาะ 2-3 ไบต์ต่อการสนทนา เรียกข้อมูลเมตาเพื่อแสดงข้อมูลหรือแสดงได้อย่างรวดเร็ว ห้องแชทใน UI ข้อความสามารถดึงข้อมูลแยกกันและแสดงผลเมื่อมาถึง ทำให้ UI ตอบสนอง และรวดเร็วยิ่งขึ้น
สร้างข้อมูลที่ปรับขนาด
เมื่อสร้างแอป การดาวน์โหลดรายการบางส่วนมักจะดีกว่า กรณีนี้พบได้บ่อยหากรายการมีระเบียนหลายพ���นรายการ เมื่อความสัมพันธ์นี้เป็นแบบคงที่และเป็นทิศทางเดียว คุณสามารถวาง ออบเจ็กต์ย่อยที่อยู่ภายใต้รายการหลัก
บางครั้ง ความสัมพันธ์นี้เป็นแบบไดนามิกมากขึ้น หรืออาจต้อง เปลี่ยนค่าปกติของข้อมูลนี้ บ่อยครั้งที่คุณสามารถถอดรหัสข้อมูลให้เป็นมาตรฐานเดียวกันได้โดยใช้ข้อความค้นหา เพื่อดึงข้อมูลบางส่วนตามที่กล่าวไว้ใน ดึงข้อมูล
แต่ถึงอย่างนั้นก็อาจไม่เพียงพอ เช่น ลองนึกถึงความสัมพันธ์แบบ 2 ทาง ระหว่างผู้ใช้และกลุ่ม ผู้ใช้สามารถอยู่ในกลุ่มได้ และกลุ่มต่างๆ ประกอบด้วย รายชื่อผู้ใช้ เมื่อต้องเลือกว่าผู้ใช้อยู่ในกลุ่มใด อะไรๆ ก็เริ่มซับซ้อน
สิ่งที่ต้องมีคือวิธีสร้างรายการกลุ่มที่ผู้ใช้เป็นสมาชิกอย่างสวยงามและ จะดึงข้อมูลสำหรับกลุ่มเหล่านั้นเท่านั้น ดัชนีของกลุ่มจะช่วย ได้เป็นอย่างดี:
// An index to track Ada's memberships { "users": { "alovelace": { "name": "Ada Lovelace", // Index Ada's groups in her profile "groups": { // the value here doesn't matter, just that the key exists "techpioneers": true, "womentechmakers": true } }, ... }, "groups": { "techpioneers": { "name": "Historical Tech Pioneers", "members": { "alovelace": true, "ghopper": true, "eclarke": true } }, ... } }
คุณอาจสังเกตเห็นว่ารายการนี้ทำซ้ำข้อมูลบางอย่างโดยจัดเก็บความสัมพันธ์
ภายใต้ทั้งบันทึกของ Ada และใต้กลุ่ม ตอนนี้ alovelace
ได้รับการจัดทำดัชนีภายใต้
และ techpioneers
แสดงอยู่ในโปรไฟล์ของ Ada ดังนั้นการลบ Ada
กลุ่มนั้นต้องมีการอัปเดตใน 2 ที่
นี่เป็นการสำรองที่จำเป็นสำหรับความสัมพันธ์แบบ 2 ทาง ซึ่งช่วยให้คุณ ดึงข้อมูลการเป็นสมาชิกของ Ada ได้อย่างรวดเร็วและมีประสิทธิภาพ แม้เวลาที่แสดงรายชื่อผู้ใช้หรือ เพิ่มเป็นล้านหรือเมื่อRealtime Databaseกฎความปลอดภัย ป้องกันการเข้าถึงระเบียนบางอย่าง
วิธีนี้เป็นการกลับข้อมูลโดยแสดงรหัสเป็นคีย์และตั้งค่า
เป็นจริง ทำให้การตรวจสอบคีย์เป็นเรื่องง่ายเหมือนการอ่าน
/users/$uid/groups/$group_id
และตรวจสอบว่าเป็น null
หรือไม่ ดัชนีเร็วขึ้น
และมีประสิทธิภาพมากกว่าการค้นหาหรือสแกนหาข้อมูล