MongoDB หลายต่อหลายสมาคม


143

คุณจะทำความสัมพันธ์แบบกลุ่มต่อกลุ่มกับ MongoDB ได้อย่างไร?

ตัวอย่างเช่น; สมมติว่าคุณมีตารางผู้ใช้และตารางบทบาท ผู้ใช้มีหลายบทบาทและบทบาทมีผู้ใช้หลายคน ใน SQL land คุณจะสร้างตาราง UserRoles

Users:
    Id
    Name

Roles:
    Id
    Name

UserRoles:
    UserId
    RoleId

ความสัมพันธ์แบบเดียวกันนั้นจัดการกับ MongoDB อย่างไร


ดูคำตอบสำหรับคำถามนี้และคำถามนี้ด้วย
Matthew Murdoch

คำตอบ:


96

คุณสามารถใส่ทุกอย่างในเอกสารผู้ใช้ทั้งนี้ขึ้นอยู่กับแบบสอบถามของคุณ:

{name:"Joe"
,roles:["Admin","User","Engineer"]
}

ในการรับวิศวกรทั้งหมดให้ใช้:

db.things.find( { roles : "Engineer" } );

หากคุณต้องการรักษาบทบาทในเอกสารแยกจากกันคุณสามารถรวม _id ของเอกสารไว้ในอาร์เรย์บทบาทแทนชื่อ:

{name:"Joe"
,roles:["4b5783300334000000000aa9","5783300334000000000aa943","6c6793300334001000000006"]
}

และตั้งค่าบทบาทเช่น:

{_id:"6c6793300334001000000006"
,rolename:"Engineer"
}

7
หลังจะดีกว่าเนื่องจากฉันต้องการรับรายการบทบาทที่มีอยู่ทั้งหมด ส่วนที่แย่เพียงอย่างเดียวคือฉันต้องตั้งค่าทั้งสองด้านของการเชื่อมโยงแล้ว เมื่อทำวิธี SQL การเพิ่ม UserRole จะทำให้ผู้ใช้ทราบเกี่ยวกับบทบาทและบทบาทที่รู้เกี่ยวกับผู้ใช้ วิธีนี้หมายความว่าฉันจะต้องกำหนดบทบาทให้กับผู้ใช้และผู้ใช้ตามบทบาท ฉันคิดว่ามันดีนะ
Josh Close

46
เพียงเพราะฐานข้อมูลไม่รองรับ sql ไม่ได้หมายความว่าการอ้างอิงไม่ใช่เครื่องมือที่มีประโยชน์ NoSQL! = NoReference ดูคำอธิบายนี้: mongodb.org/display/DOCS/Schema+Design
Tom Gruner

8
ดูเหมือนจะไม่เป็นความคิดที่ดี หากคุณมีเพียงหกบทบาทแน่นอน แต่จะเกิดอะไรขึ้นถ้าคุณมีวัตถุ 20,000 ชิ้นที่สามารถเชื่อมโยงกับวัตถุอีก 20,000 ชิ้น (ในหลาย ๆ ความสัมพันธ์) แม้แต่เอกสาร MongoDB ยังบอกเป็นนัยว่าคุณควรหลีกเลี่ยงการอ้างอิงที่ไม่แน่นอนและมีขนาดใหญ่ docs.mongodb.org/manual/tutorial/…
CaptSaltyJack

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

1
การทำงานนี้สำหรับบทบาทส่วนใหญ่ของระบบ coz มักจะเป็นชุดเล็กและเรามักจะใช้ผู้ใช้แล้วดูบทบาทของเขา / เธอ แต่ถ้าบทบาทมีขนาดใหญ่ล่ะ หรือถ้าฉันขอให้คุณส่งรายชื่อผู้ใช้ที่มีบทบาท == "วิศวกร" ให้ฉัน ตอนนี้คุณจะต้องค้นหาคอลเลกชันผู้ใช้ทั้งหมดของคุณ (เยี่ยมชมผู้ใช้ทั้งหมดที่ไม่มีบทบาทวิศวกรด้วย) เพื่อให้ได้ผู้ใช้ 2 หรือ 3 คนที่อาจมีบทบาทนี้ระหว่างผู้ใช้หลายล้านคน ตารางหรือคอลเล็กชันแยกต่างหากนั้นดีกว่ามาก
theprogrammer

32

แทนที่จะพยายามทำตัวแบบตามประสบการณ์ในการใช้ RDBMS ของฉันฉันพบว่ามันง่ายกว่ามากในการสร้างแบบจำลองโซลูชันที่เก็บเอกสารโดยใช้ MongoDB, Redis และที่เก็บข้อมูล NoSQL อื่น ๆ โดยปรับให้เหมาะสมสำหรับกรณีการใช้การอ่านในขณะที่คำนึงถึงอะตอม การดำเนินการเขียนที่จำเป็นต้องได้รับการสนับสนุนจากกรณีใช้งานการเขียน

ตัวอย่างเช่นการใช้โดเมน "Users in Roles" มีดังนี้:

  1. บทบาท - สร้างอ่านอัปเดตลบผู้ใช้รายชื่อเพิ่มผู้ใช้ลบผู้ใช้ล้างผู้ใช้ทั้งหมดดัชนีผู้ใช้หรือคล้ายกันเพื่อสนับสนุน "Is User In Role" (การดำเนินการเหมือนคอนเทนเนอร์ + ข้อมูลเมตาของตัวเอง)
  2. ผู้ใช้ - สร้างอ่านอัปเดตลบ (การดำเนินการ CRUD เช่นเอนทิตีแบบยืน)

สามารถสร้างแบบจำลองเป็นเทมเพลตเอกสารต่อไปนี้:

User: { _id: UniqueId, name: string, roles: string[] }
    Indexes: unique: [ name ]
Role: { _id: UniqueId, name: string, users: string[] }
    Indexes: unique: [ name ]

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

หากยังไม่ปรากฏชัดเจนในข้อความ แต่เป็นประเภทของความคิดที่ได้รับการสนับสนุนเมื่อใช้ที่เก็บเอกสาร

ฉันหวังว่าสิ่งนี้จะช่วยลดช่องว่างโดยคำนึงถึงด้านการอ่านของการดำเนินการ

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

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

การดำเนินการที่ยืดออกไปอย่างหลีกเลี่ยงไม่ได้ของการเขียนอะตอมของเราคือการล้างบทบาทซึ่งจะส่งผลให้ผู้ใช้อัปเดตจำนวนมากเพื่อลบ Role.name ออกจากอาร์เรย์ User.roles โดยทั่วไปแล้วการดำเนินการที่ชัดเจนนี้จะหมดกำลังใจ แต่ถ้าจำเป็นสามารถดำเนินการได้โดยการสั่งซื้อการดำเนินการ:

  1. รับรายชื่อผู้ใช้จาก Role.users
  2. ทำซ้ำชื่อผู้ใช้จากขั้นตอนที่ 1 ลบชื่อบทบาทจาก User.roles
  3. ล้าง Role.users

ในกรณีที่มีปัญหาซึ่งน่าจะเกิดขึ้นภายในขั้นตอนที่ 2 การย้อนกลับนั้นง่ายเนื่องจากชื่อผู้ใช้ชุดเดียวกันจากขั้นตอนที่ 1 สามารถใช้ในการกู้คืนหรือดำเนินการต่อ


15

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

หนึ่งในผู้แสดงความคิดเห็นได้นำทัศนคติเตือนอย่างชาญฉลาดว่า“ หากข้อมูลมีความสัมพันธ์ให้ใช้ความสัมพันธ์” อย่างไรก็ตามความคิดเห็นนั้นมีความหมายเฉพาะในโลกสัมพันธ์ที่ซึ่ง schema มาก่อนแอปพลิเคชันเสมอ

โลกแห่งความสัมพันธ์: ข้อมูลโครงสร้าง> เขียนแอปพลิเคชันเพื่อรับ
NOSQL WORLD: แอปพลิเคชันการออกแบบ> ข้อมูลโครงสร้างตามลำดับ

แม้ว่าข้อมูลจะสัมพันธ์กัน NoSQL ก็ยังเป็นตัวเลือก ตัวอย่างเช่นความสัมพันธ์แบบหนึ่งต่อหลายคนไม่มีปัญหาและครอบคลุมในMongoDB เอกสารอย่างกว้างขวาง

แนวทางแก้ไขปัญหาของปีพ. ศ

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

ในระดับที่ปฏิบัติได้มากขึ้นการเปิดตัว Couchbase 4.0 นั้นหมายความว่าเป็นครั้งแรกที่คุณสามารถเข้าร่วมพื้นเมืองใน NoSQL ได้ พวกเขาใช้ N1QL ของตัวเอง นี่คือตัวอย่างของการสอนJOINจาก:

SELECT usr.personal_details, orders 
        FROM users_with_orders usr 
            USE KEYS "Elinor_33313792" 
                JOIN orders_with_users orders 
                    ON KEYS ARRAY s.order_id FOR s IN usr.shipped_order_history END

N1QL อนุญาตการดำเนินงาน SQL ส่วนใหญ่ถ้าไม่ใช่ทั้งหมดรวมถึง aggregration การกรองและอื่น ๆ

โซลูชันไฮบริดที่ไม่แปลกใหม่

หาก MongoDB ยังคงเป็นตัวเลือกเดียวฉันต้องการกลับไปยังจุดของฉันว่าแอปพลิเคชันควรมีความสำคัญเหนือกว่าโครงสร้างของข้อมูล ไม่มีคำตอบใด ๆ ที่กล่าวถึงการฝังแบบไฮบริดโดยข้อมูลที่ถูกสอบถามส่วนใหญ่จะถูกฝังอยู่ในเอกสาร / วัตถุและการอ้างอิงจะถูกเก็บไว้สำหรับกรณีส่วนน้อย

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

อาจเป็นกรณีนี้หากผู้ใช้ลงชื่อเข้าใช้และเขา / เธอต้องการที่จะเห็นตัวเลือกทั้งหมดสำหรับบทบาททั้งหมดที่เขา / เธอเป็น อย่างไรก็ตามผู้ใช้เป็น“ วิศวกร” และตัวเลือกสำหรับบทบาทนี้ไม่ค่อยได้ใช้ ซึ่งหมายความว่าแอปพลิเคชันจะต้องแสดงตัวเลือกสำหรับวิศวกรในกรณีที่เขา / เธอต้องการที่จะคลิกที่พวกเขา

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

   {_id: ObjectID(),
    roles: [[“Engineer”, ObjectId()”],
            [“Administrator”, ObjectId()”]]
   }

หรือดียิ่งขึ้นทำดัชนีฟิลด์ role.name ในการรวบรวมบทบาทและคุณอาจไม่จำเป็นต้องฝัง ObjectID () เช่นกัน

อีกตัวอย่างหนึ่งคือข้อมูลเกี่ยวกับบทบาททั้งหมดที่ร้องขอตลอดเวลาหรือไม่

อาจเป็นกรณีที่ผู้ใช้เข้าสู่แดชบอร์ดและ 90% ของเวลาดำเนินงานที่เชื่อมโยงกับบทบาท“ วิศวกร” การฝังแบบไฮบริดสามารถทำได้สำหรับบทบาทนั้นโดยเฉพาะและเก็บการอ้างอิงสำหรับส่วนที่เหลือเท่านั้น

{_id: ObjectID(),
  roles: [{name: Engineer”, 
           property1: value1,
           property2: value2
          },   
          [“Administrator”, ObjectId()”]
         ]
}

การเป็น schemaless ไม่ได้เป็นเพียงคุณสมบัติของ NoSQL แต่มันอาจเป็นข้อได้เปรียบในกรณีนี้ สามารถใช้ได้อย่างสมบูรณ์แบบในการซ้อนวัตถุประเภทต่างๆในคุณสมบัติ“ บทบาท” ของวัตถุผู้ใช้


โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.