MongoDB 다 대다 협회


143

MongoDB와 다 대다 연결을 어떻게 수행 하시겠습니까?

예를 들어; Users 테이블과 Roles 테이블이 있다고 가정하겠습니다. 사용자에게는 많은 역할이 있으며 역할에는 많은 사용자가 있습니다. SQL 랜드에서는 UserRoles 테이블을 작성합니다.

Users:
    Id
    Name

Roles:
    Id
    Name

UserRoles:
    UserId
    RoleId

MongoDB에서 동일한 종류의 관계는 어떻게 처리됩니까?


또한에 대한 답변을 참조하십시오 이 질문이 질문에
매튜 머독

답변:


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
이것은 좋은 생각처럼 보이지 않습니다. 6 개의 역할 만 가지고 있다면 20000 개에 연결할 수있는 20000 개의 개체가 있다면 (다수의 관계로) 어떻게 될까요? MongoDB 문서조차도 변경 가능하고 거대한 참조 배열을 피해야한다고 암시합니다. docs.mongodb.org/manual/tutorial/…
CaptSaltyJack

분명히 많은 객체와 다 대 다 관계의 경우 문서의 출판사 / 책 예제와 같은 다른 솔루션을 사용하려고합니다. 이 경우 잘 작동하며 별도의 사용자 역할 문서를 만드는 경우에만 문제가 발생합니다.
diederikh

1
이것은 대부분의 시스템에서 작동합니다. coz 역할은 일반적으로 소규모이며 우리는 일반적으로 사용자를 가지고 자신의 역할을 살펴 봅니다. 그러나 역할이 큰 경우 어떻게해야합니까? 또는 role == "Engineer"인 사용자 목록을 요청하면 어떻게됩니까? 이제 전체 사용자 콜렉션 (역할 엔지니어가없는 모든 사용자를 방문)을 쿼리하여 예를 들어 백만 명의 사용자 중이 역할을 수행 할 수있는 2 명 또는 3 명의 사용자 만 가져와야합니다. 별도의 테이블이나 컬렉션이 훨씬 좋습니다.
프로그래머

32

RDBMS에 대한 수년간의 경험에 따라 모델링을 시도하는 대신 원자력을 고려하면서 읽기 사용 사례를 최적화하여 MongoDB, Redis 및 기타 NoSQL 데이터 저장소를 사용하여 문서 저장소 솔루션을 모델링하는 것이 훨씬 쉽다는 것을 알았습니다. 쓰기 사용 사례에서 지원해야하는 쓰기 작업

예를 들어 "역할 사용자"도메인의 사용은 다음과 같습니다.

  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 엔터티의 역할 관련 기능과 같은 높은 빈도의 사용을 지원하기 위해 User.Roles는 의도적으로 비정규 화되어 중복 저장소가있는 Role.Users 및 User에 저장됩니다.

텍스트에서 쉽게 알 수 없지만 문서 리포지토리를 사용할 때 권장되는 유형의 사고입니다.

이것이 작업의 읽기 측면과 관련된 격차를 해소하는 데 도움이되기를 바랍니다.

쓰기 측면에서 권장되는 것은 원자 쓰기에 따라 모델링하는 것입니다. 예를 들어, 문서 구조가 잠금 획득, 한 문서, 다른 문서 및 가능하면 더 많은 문서를 업데이트 한 다음 잠금을 해제해야하는 경우 모델이 실패했을 수 있습니다. 분산 잠금을 구축 할 수 있다고해서 반드시 사용하도록되어있는 것은 아닙니다.

User in Roles 모델의 경우, 잠금의 원자 적 쓰기 방지를 확장하는 작업은 역할에서 사용자를 추가하거나 제거하는 것입니다. 두 경우 모두 성공적인 작업으로 인해 단일 사용자 및 단일 역할 문서가 모두 업데이트됩니다. 문제가 발생하면 정리하기가 쉽습니다. 이것이 문서 저장소가 사용되는 작업 단위 패턴이 많이 나타나는 이유 중 하나입니다.

원자 적 쓰기 방지 잠금을 실제로 확장하는 작업으로 인해 역할이 지워져 User.roles 배열에서 Role.name을 제거하기위한 많은 사용자 업데이트가 발생합니다. 이 클리어 작업은 일반적으로 권장되지 않지만 필요한 경우 작업을 주문하여 구현할 수 있습니다.

  1. Role.users에서 사용자 이름 목록을 가져옵니다.
  2. 1 단계에서 사용자 이름을 반복하고 User.roles에서 역할 이름을 제거하십시오.
  3. Role.users를 지우십시오.

2 단계에서 발생할 가능성이 가장 높은 문제의 경우 1 단계와 동일한 사용자 이름 세트를 사용하여 복구하거나 계속할 수 있으므로 롤백이 쉽습니다.


15

방금이 질문에 걸려 넘어졌지만 오래된 질문이지만 주어진 답변에 언급되지 않은 몇 가지 가능성을 추가하는 것이 유용 할 것이라고 생각했습니다. 또한 지난 몇 년 동안 상황이 조금 바뀌 었으므로 SQL과 NoSQL이 서로 더 가까워지고 있음을 강조하는 것이 좋습니다.

주석가 중 한 명이“데이터가 관계형 인 경우 관계형을 사용하십시오”라는 현명한주의 태도를 나타 냈습니다. 그러나이 주석은 관계형 세계에서만 의미가 있으며 스키마는 항상 응용 프로그램보다 우선합니다.

관계 세계 : 구조 데이터>이를 얻기 위해 응용 프로그램 작성
NOSQL 세계 : 설계 응용 프로그램> 그에 따라 구조 데이터

데이터가 관계형이더라도 NoSQL은 여전히 ​​옵션입니다. 예를 들어 일대 다 관계는 전혀 문제가되지 않으며 MongoDB 문서 에서 광범위하게 다루고 있습니다.

2010 년 문제에 대한 2015 년 해결책

이 질문이 게시 된 이후, noSQL을 SQL에 더 가까이 가져 오려는 심각한 시도가있었습니다. 캘리포니아 대학 (샌디에고)의 Yannis Papakonstantinou가 이끄는 팀은 FORWARD 를 개발하고 있으며, 여기에 게시 된 것과 같은 지속적인 문제에 대한 해결책이 될 수있는 SQL ++을 구현했습니다.

보다 실질적인 수준에서 Couchbase 4.0 릴리스는 NoSQL에서 처음으로 네이티브 JOIN을 수행 할 수 있음을 의미했습니다. 그들은 자신의 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 작업을 허용하지는 않습니다.

전혀 그렇지 않은 하이브리드 솔루션

MongoDB가 여전히 유일한 옵션이라면 응용 프로그램이 데이터 구조보다 우선해야한다는 점으로 돌아가고 싶습니다. 대부분의 쿼리 된 데이터가 문서 / 객체에 임베드되어 소수의 경우에 대한 참조가 유지되는 하이브리드 임베딩에 대한 답변은 없습니다.

예 : 역할 이름 이외의 정보가 기다릴 수 있습니까? 사용자가 아직 필요하지 않은 것을 요청하지 않으면 응용 프로그램을 부트 스트랩하는 것이 더 빠를 수 있습니까?

사용자가 로그인하여 자신이 속한 모든 역할에 대한 모든 옵션을 확인해야하는 경우가 이에 해당합니다. 그러나 사용자는 "엔지니어"이며이 역할에 대한 옵션은 거의 사용되지 않습니다. 이는 엔지니어가 엔지니어를 클릭하려는 경우 애플리케이션에 대한 옵션 만 표시하면됨을 의미합니다.

이는 시작시 응용 프로그램에 사용자에게 속한 역할과 (2) 특정 역할에 연결된 이벤트에 대한 정보를 얻을 수있는 위치를 알려주는 문서를 통해 달성 할 수 있습니다.

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

또는 역할 컬렉션에서 role.name 필드를 색인화하면 ObjectID ()를 포함하지 않아도됩니다.

다른 예 : 항상 요청 된 모든 역할에 대한 정보입니까?

사용자가 대시 보드에 로그인하고 90 %의 시간이 "엔지니어"역할과 연결된 작업을 수행하는 경우도 있습니다. 특정 역할에 대해 하이브리드 임베딩을 완전히 수행하고 나머지 부분에 대해서만 참조를 유지할 수 있습니다.

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

스키마가없는 것은 NoSQL의 특성 일뿐만 아니라이 경우 이점이 될 수 있습니다. 사용자 객체의 "역할"속성에 다른 유형의 객체를 중첩하는 것은 완벽하게 유효합니다.


당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.