Hiệp hội nhiều-nhiều MongoDB


143

Làm thế nào bạn có thể liên kết nhiều-nhiều với MongoDB?

Ví dụ; giả sử bạn có bảng Người dùng và bảng Vai trò. Người dùng có nhiều vai trò và vai trò có nhiều người dùng. Trong vùng đất SQL, bạn sẽ tạo bảng UserRoles.

Users:
    Id
    Name

Roles:
    Id
    Name

UserRoles:
    UserId
    RoleId

Làm thế nào là cùng một loại mối quan hệ được xử lý trong MongoDB?


Xem thêm câu trả lời cho câu hỏi nàycâu hỏi này
Matthew Murdoch

Câu trả lời:


96

Tùy thuộc vào nhu cầu truy vấn của bạn, bạn có thể đặt mọi thứ vào tài liệu người dùng:

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

Để có được tất cả các Kỹ sư, hãy sử dụng:

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

Nếu bạn muốn duy trì các vai trò trong các tài liệu riêng biệt thì bạn có thể bao gồm _id của tài liệu trong mảng vai trò thay vì tên:

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

và thiết lập các vai trò như:

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

7
Cái sau sẽ tốt hơn vì tôi cần có một danh sách tất cả các vai trò có sẵn. Phần xấu duy nhất là tôi cần thiết lập cả hai đầu của hiệp hội sau đó. Khi thực hiện theo cách SQL, việc thêm UserRole sẽ giúp Người dùng biết về Vai trò và Vai trò biết về Người dùng. Cách này có nghĩa là tôi sẽ phải đặt Vai trò cho Người dùng và Người dùng trên Vai trò. Tôi đoán rằng mặc dù tốt.
Josh Đóng

46
Chỉ vì một cơ sở dữ liệu không sql hỗ trợ không có nghĩa là tài liệu tham khảo không phải là công cụ hữu ích NoSQL = NoReference thấy lời giải thích này: mongodb.org/display/DOCS/Schema+Design
Tom Gruner

8
Đây dường như không phải là một ý tưởng tốt. Nếu bạn chỉ có sáu vai trò, chắc chắn, nhưng nếu bạn có 20000 đối tượng có thể được liên kết với 20000 đối tượng khác (trong mối quan hệ nhiều-nhiều) thì sao? Ngay cả các tài liệu MongoDB cũng gợi ý rằng bạn nên tránh có các mảng tham chiếu khổng lồ, có thể thay đổi. docs.mongodb.org/manual/tutorial/ từ
CaptSaltyJack

Rõ ràng đối với các mối quan hệ nhiều-nhiều với nhiều đối tượng bạn muốn sử dụng một giải pháp khác nhau (như ví dụ về nhà xuất bản / sách trong tài liệu). Trong trường hợp này, nó hoạt động tốt và sẽ chỉ làm phức tạp mọi thứ nếu bạn tạo các tài liệu vai trò người dùng riêng biệt.
deatherikh

1
Điều này hoạt động đối với hầu hết các vai trò của hệ thống thường là một tập hợp nhỏ và chúng tôi thường lấy một người dùng và sau đó xem xét vai trò của anh ấy / cô ấy. Nhưng nếu vai trò lớn thì sao? hoặc nếu tôi yêu cầu bạn cung cấp cho tôi danh sách người dùng có vai trò == "Kỹ sư" thì sao? Bây giờ bạn sẽ phải truy vấn toàn bộ bộ sưu tập người dùng của mình (truy cập tất cả người dùng không có vai trò Kỹ sư) chỉ để có được 2 hoặc 3 người dùng có thể có vai trò này trong số hàng triệu người dùng như vậy. Một bảng hoặc bộ sưu tập riêng là tốt hơn nhiều.
theprogrammer

31

Thay vì cố gắng mô hình hóa theo kinh nghiệm nhiều năm của chúng tôi với RDBMS, tôi đã thấy việc mô hình hóa các giải pháp kho lưu trữ tài liệu bằng cách sử dụng MongoDB, Redis và các kho dữ liệu NoQuery khác bằng cách tối ưu hóa các trường hợp sử dụng đọc, trong khi quan tâm đến nguyên tử hoạt động ghi cần được hỗ trợ bởi các trường hợp sử dụng ghi.

Chẳng hạn, việc sử dụng miền "Người dùng trong Vai trò" tuân theo:

  1. Vai trò - Tạo, Đọc, Cập nhật, Xóa, Liệt kê người dùng, Thêm người dùng, Xóa người dùng, Xóa tất cả người dùng, Chỉ mục người dùng hoặc tương tự để hỗ trợ "Is User In Role" (hoạt động như một container + siêu dữ liệu của chính nó).
  2. Người dùng - Tạo, Đọc, Cập nhật, Xóa (Hoạt động CRUD như một thực thể độc lập)

Điều này có thể được mô hình hóa như các mẫu tài liệu sau:

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

Để hỗ trợ việc sử dụng tần suất cao, chẳng hạn như các tính năng liên quan đến Vai trò từ thực thể Người dùng, User.Roles được cố ý không chuẩn hóa, được lưu trữ trên Người dùng cũng như Vai trò. Người dùng có bộ nhớ trùng lặp.

Nếu nó không dễ dàng xuất hiện trong văn bản, nhưng đây là kiểu suy nghĩ được khuyến khích khi sử dụng kho tài liệu.

Tôi hy vọng rằng điều này sẽ giúp thu hẹp khoảng cách liên quan đến phía đọc của các hoạt động.

Đối với phía viết, những gì được khuyến khích là mô hình hóa theo cách viết nguyên tử. Chẳng hạn, nếu cấu trúc tài liệu yêu cầu lấy khóa, cập nhật một tài liệu, sau đó một tài liệu khác và có thể nhiều tài liệu hơn, sau đó phát hành khóa, có khả năng mô hình đã thất bại. Chỉ vì chúng tôi có thể xây dựng các khóa phân tán không có nghĩa là chúng tôi phải sử dụng chúng.

Đối với trường hợp của mô hình Người dùng trong Vai trò, các hoạt động kéo dài việc tránh ghi khóa nguyên tử của chúng tôi là thêm hoặc xóa Người dùng khỏi Vai trò. Trong cả hai trường hợp, một hoạt động thành công dẫn đến cả một Người dùng và một tài liệu Vai trò duy nhất được cập nhật. Nếu một cái gì đó không thành công, nó rất dễ dàng để thực hiện dọn dẹp. Đây là một lý do mô hình Đơn vị công việc xuất hiện khá nhiều nơi sử dụng kho tài liệu.

Hoạt động thực sự kéo dài việc tránh khóa ghi nguyên tử của chúng ta đang xóa một Vai trò, điều này sẽ dẫn đến nhiều cập nhật của Người dùng để xóa Vai trò.name khỏi mảng User.roles. Hoạt động rõ ràng này sau đó thường không được khuyến khích, nhưng nếu cần có thể được thực hiện bằng cách ra lệnh cho các hoạt động:

  1. Lấy danh sách tên người dùng từ Role.users.
  2. Lặp lại tên người dùng từ bước 1, xóa tên vai trò khỏi User.roles.
  3. Xóa Vai trò.

Trong trường hợp xảy ra sự cố, rất có thể xảy ra trong bước 2, việc khôi phục lại rất dễ dàng vì cùng một bộ tên người dùng từ bước 1 có thể được sử dụng để khôi phục hoặc tiếp tục.


15

Tôi vừa vấp phải câu hỏi này và, mặc dù nó là một câu hỏi cũ, tôi nghĩ rằng sẽ rất hữu ích nếu thêm một vài khả năng không được đề cập trong các câu trả lời được đưa ra. Ngoài ra, mọi thứ đã thay đổi một chút trong vài năm qua, do đó, cần nhấn mạnh rằng SQL và NoQuery đang tiến gần nhau hơn.

Một trong những nhà bình luận đã đưa ra thái độ cảnh báo khôn ngoan rằng, nếu dữ liệu là quan hệ, hãy sử dụng mối quan hệ. Tuy nhiên, nhận xét đó chỉ có ý nghĩa trong thế giới quan hệ, nơi các lược đồ luôn đến trước ứng dụng.

THẾ GIỚI LIÊN QUAN: Dữ liệu cấu trúc> Viết ứng dụng để có được
NOSQL WORLD: Ứng dụng thiết kế> Dữ liệu cấu trúc phù hợp

Ngay cả khi dữ liệu là quan hệ, NoQuery vẫn là một tùy chọn. Ví dụ: các mối quan hệ một-nhiều không có vấn đề gì cả và được bao phủ rộng rãi trong các tài liệu MongoDB

GIẢI PHÁP 2015 ĐẾN VẤN ĐỀ 2010

Vì câu hỏi này đã được đăng, đã có những nỗ lực nghiêm túc trong việc đưa noQuery đến gần hơn với SQL. Nhóm được lãnh đạo bởi Yannis Papakonstantinou tại Đại học California (San Diego) đã làm việc trên FORWARD , một triển khai SQL ++ có thể sớm là giải pháp cho các vấn đề dai dẳng như được đăng ở đây.

Ở mức độ thực tế hơn, việc phát hành Couchbase 4.0 có nghĩa là, lần đầu tiên, bạn có thể thực hiện THAM GIA bản địa trong NoQuery. Họ sử dụng N1QL của riêng họ. Đây là một ví dụ về một JOINtừ hướng dẫn của họ :

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 cho phép hầu hết nếu không phải tất cả các hoạt động SQL bao gồm tổng hợp, lọc, v.v.

GIẢI PHÁP HYBRID KHÔNG-SO-NEW

Nếu MongoDB vẫn là lựa chọn duy nhất, thì tôi muốn quay lại quan điểm của mình rằng ứng dụng sẽ được ưu tiên hơn cấu trúc dữ liệu. Không có câu trả lời nào đề cập đến việc nhúng lai, theo đó, hầu hết dữ liệu được truy vấn được nhúng trong tài liệu / đối tượng và các tài liệu tham khảo được lưu giữ cho một số ít trường hợp.

Ví dụ: thông tin (ngoài tên vai trò) có thể chờ không? có thể bootstrapping ứng dụng nhanh hơn bằng cách không yêu cầu bất cứ điều gì mà người dùng chưa cần?

Đây có thể là trường hợp nếu người dùng đăng nhập và anh ấy cần phải xem tất cả các tùy chọn cho tất cả các vai trò mà anh ấy thuộc về. Tuy nhiên, người dùng là một Kỹ sư trực tuyến và các tùy chọn cho vai trò này hiếm khi được sử dụng. Điều này có nghĩa là ứng dụng chỉ cần hiển thị các tùy chọn cho một kỹ sư trong trường hợp anh ta muốn nhấp vào chúng.

Điều này có thể đạt được với một tài liệu cho ứng dụng biết khi bắt đầu (1) vai trò của người dùng và (2) nơi nhận thông tin về một sự kiện được liên kết với một vai trò cụ thể.

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

Hoặc, thậm chí tốt hơn, lập chỉ mục trường role.name trong bộ sưu tập vai trò và bạn có thể không cần phải nhúng ObjectID ().

Một ví dụ khác: thông tin về TẤT CẢ các vai trò được yêu cầu TẤT CẢ thời gian?

Nó cũng có thể là trường hợp người dùng đăng nhập vào bảng điều khiển và 90% thời gian thực hiện các tác vụ được liên kết với vai trò của Kỹ sư trực. Việc nhúng lai có thể được thực hiện cho vai trò cụ thể đó một cách đầy đủ và chỉ giữ các tài liệu tham khảo cho phần còn lại.

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

Trở thành một schemaless không chỉ là một đặc điểm của NoQuery, nó có thể là một lợi thế trong trường hợp này. Nó hoàn toàn hợp lệ để lồng các loại đối tượng khác nhau trong thuộc tính của Roles của một đối tượng người dùng.


5

trong trường hợp khi nhân viên và công ty là đối tượng thực thể, hãy thử sử dụng lược đồ sau:

employee{
   //put your contract to employee
   contracts:{ item1, item2, item3,...}
}

company{
   //and duplicate it in company
   contracts:{ item1, item2, item3,...}
}
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.