MongoDB 관계 : 포함 또는 참조?


524

관계형 데이터베이스 배경에서 온 MongoDB를 처음 사용합니다. 나는 약간의 의견에 질문의 구조를 설계 할,하지만 난 의견에 사용할 관계 모르는 : embedreference?

stackoverflow 와 같은 주석이있는 질문은 다음과 같은 구조를 갖습니다.

Question
    title = 'aaa'
    content = bbb'
    comments = ???

처음에는 embed다음과 같이 포함 된 주석 ( MongoDB에서 권장되는 것으로 생각합니다 )을 사용하고 싶습니다.

Question
    title = 'aaa'
    content = 'bbb'
    comments = [ { content = 'xxx', createdAt = 'yyy'}, 
                 { content = 'xxx', createdAt = 'yyy'}, 
                 { content = 'xxx', createdAt = 'yyy'} ]

분명하지만이 사건에 대해 걱정 합니다. 지정된 의견을 편집하려면 내용과 질문을 어떻게 얻습니까? 더 없다 _id내가 하나를 찾을 수 없으며, question_ref내가 그 질문을 찾을 수 있습니다. (저는 초보자가 아니기 때문에 _id및 없이이 작업을 수행 할 방법이 있는지 모르겠습니다 question_ref.)

사용 ref하지 않아야 embed합니까? 그런 다음 주석을위한 새 컬렉션을 만들어야합니까?


모든 Mongo 객체는 필드 생성 여부에 관계없이 _ID로 생성됩니다. 따라서 기술적으로 각 의견에는 여전히 ID가 있습니다.
Robbie Guilfoyle

25
@RobbieGuilfoyle 볼 true--하지 stackoverflow.com/a/11263912/347455을
pennstatephil

13
감사합니다 @pennstatephil :)
Robbie Guilfoyle

4
그가 어쩌면 의미하는 모든 것입니다 몽구스 객체가이 프레임 워크를 사용하는 사람들을위한 _id로 만든 - 볼 몽구스 subdocs을
루카 Steeb

1
mongo db 관계를 배우기위한 아주 좋은 책은 "MongoDB Applied Design Patterns-O'Reilly"입니다. 1 장,이 결정에 대해 이야기하고, 포함 시키거나 참조 하시겠습니까?
Felipe Toledo

답변:


769

이것은 과학보다 예술입니다. 스키마에 대한 몽고 문서 는 좋은 참고 자료이지만 고려해야 할 사항이 있습니다.

  • 가능한 많이 넣어

    문서 데이터베이스의 즐거움은 많은 조인을 제거한다는 것입니다. 첫 번째 본능은 가능한 한 단일 문서에 배치해야합니다. MongoDB 문서에는 구조가 있고 해당 구조 내에서 효율적으로 쿼리 할 수 ​​있기 때문에 (필요한 문서의 일부를 취할 수 있으므로 문서 크기는 크게 걱정하지 않아도 됨) 데이터를 즉시 정규화 할 필요가 없습니다. 당신은 SQL에서 할 것입니다. 특히, 상위 문서와 별도로 유용하지 않은 데이터는 동일한 문서의 일부 여야합니다.

  • 여러 장소에서 참조 할 수있는 데이터를 자체 컬렉션으로 분리합니다.

    이것은 "데이터 일관성"문제이므로 "스토리지 공간"문제는 아닙니다. 많은 레코드가 동일한 데이터를 참조 할 경우 단일 레코드를 업데이트하고 다른 위치에 참조를 유지하는 것이 더 효율적이고 오류가 적습니다.

  • 문서 크기 고려 사항

    MongoDB는 단일 문서에 4MB (16MB, 1.8) 크기 제한을 적용합니다. GB의 데이터 세계에서는이 소리가 작게 들리지만 30 만 트윗 또는 250 개의 일반적인 스택 오버플로 응답 또는 20 번 깜박임 사진입니다. 반면에, 이것은 일반적인 웹 페이지에 한 번에 제시하고자하는 것보다 훨씬 많은 정보입니다. 먼저 쿼리를보다 쉽게 ​​만드는 방법을 고려하십시오. 대부분의 경우 문서 크기에 대한 우려는 조기 최적화입니다.

  • 복잡한 데이터 구조 :

    MongoDB는 임의의 깊은 중첩 데이터 구조를 저장할 수 있지만 효율적으로 검색 할 수는 없습니다. 데이터가 트리, 포리스트 또는 그래프를 형성하는 경우 각 노드와 해당 가장자리를 별도의 문서에 효과적으로 저장해야합니다. (이 유형의 데이터를 위해 특별히 설계된 데이터 저장소도 고려해야합니다.)

    또한 문서에서 요소의 하위 집합을 반환하는 것이 불가능하다는 것보다 지적되었습니다 . 각 문서의 몇 비트를 골라서 선택해야하는 경우 분리하기가 더 쉬울 것입니다.

  • 데이터 일관성

    MongoDB는 효율성과 일관성 사이에서 균형을 유지합니다. 규칙은 단일 문서에 대한 변경 사항은 항상 원 자성이지만 여러 문서에 대한 업데이트 는 원 자성으로 간주해서는 안됩니다. 서버에서 레코드를 "잠그는"방법도 없습니다 (예 : "잠금"필드를 사용하여이를 클라이언트의 논리에 구축 할 수 있음). 스키마를 디자인 할 때 데이터 일관성을 유지하는 방법을 고려하십시오. 일반적으로 문서에 많이 보관할수록 좋습니다.

설명하는 내용에 대해 설명을 포함시키고 각 설명에 ObjectID가있는 id 필드를 제공합니다. ObjectID에는 타임 스탬프가 포함되어 있으므로 원하는 경우 만든 대신 사용할 수 있습니다.


1
OP 질문에 추가하고 싶습니다. 내 의견 모델에는 사용자 이름과 그의 아바타 링크가 포함되어 있습니다. 사용자가 이름 / 아바타를 수정할 수 있다는 점을 고려하면 가장 좋은 방법은 무엇입니까?
user1102018

5
'복잡한 데이터 구조'와 관련하여 집계 프레임 워크를 사용하여 문서에서 요소의 서브 세트를 리턴 할 수 있습니다 ($ unwind 시도).
Eyal Roth

4
Errr,이 기술은 2012 년 초 MongoDB에서 포시 벨이 아니거나 널리 알려지지 않았습니다. MongoDB의 활발한 개발을 중단하고 원래 게시물에서 의견을 제시하기에 좋은 위치에 있지 않습니다.
John F. Miller

54
16MB = 3 천만 트윗? 트윗 당 약 0.5 바이트?
Paolo

8
그렇습니다 .1000의 요인으로 벗어난 것으로 보이며 일부 사람들은 이것이 중요하다고 생각합니다. 글을 수정하겠습니다. 트윗 당 WRT 560 바이트, 2011 년에 이것을 썼을 때 트위터는 여전히 문자 메시지와 루비 1.4 문자열에 묶여있었습니다. 다시 말해서 여전히 ASCII 문자 만 있습니다.
존 F. 밀러


29

지정된 주석을 편집하려면 내용과 질문을 얻는 방법은 무엇입니까?

하위 문서별로 쿼리 할 수 ​​있습니다 : db.question.find({'comments.content' : 'xxx'}).

그러면 전체 질문 문서가 반환됩니다. 지정된 주석을 편집하려면 클라이언트에서 주석을 찾아서 편집 한 후 DB에 다시 저장해야합니다.

일반적으로 문서에 객체 배열이 포함되어 있으면 해당 하위 객체를 클라이언트 쪽에서 수정해야합니다.


4
두 개의 주석이 동일한 내용을 가지고 있으면 작동하지 않습니다. 저자는 검색어에 저자를 추가 할 수 있다고 주장 할 수 있는데, 저자가 동일한 콘텐츠로 동일한 두 개의 댓글을 작성한 경우에도 작동하지 않습니다
Steel Brain

@SteelBrain : 주석 색인을 유지했다면 점 표기법이 도움이 될 수 있습니다. 참조 stackoverflow.com/a/33284416/1587329을
SERV-INC

13
나는이 답변에 34 개의 공감대가있는 방법을 이해하지 못합니다. 두 번째 여러 사람들이 전체 시스템이 깰 것이라고 똑같은 것을 언급합니다. 이것은 절대적으로 끔찍한 디자인이므로 절대 사용해서는 안됩니다. @user가하는 방식은가는 길입니다
user2073973

21

글쎄, 나는 조금 늦었지만 여전히 내 스키마 생성 방식을 공유하고 싶습니다.

고전 OOP에서와 마찬가지로 단어로 설명 할 수있는 모든 것에 대한 스키마가 있습니다.

EG

  • 논평
  • 계정
  • 사용자
  • 블로그 포스트
  • ...

모든 스키마는 문서 또는 하위 문서로 저장 될 수 있으므로 각 스키마마다이를 선언합니다.

문서:

  • 참조로 사용할 수 있습니다. (예 : 사용자가 의견을 작성했습니다.-> 의견은 사용자에 대한 "제작자"참조입니다.)
  • 응용 프로그램의 "루트"입니다. (예 : 블로그 포스트-> 블로그 포스트에 관한 페이지가 있습니다)

하위 문서 :

  • 한 번만 사용할 수 있으며 참조가 아닙니다. (예 : 댓글은 블로그 포스트에 저장됩니다)
  • 응용 프로그램에서 "루트"가 아닙니다. (댓글은 블로그 포스트 페이지에 표시되지만 해당 페이지는 여전히 블로그 포스트에 관한 것입니다)

20

나는이 질문을 스스로 조사 하면서이 작은 프레젠테이션을 보았습니다. 나는 정보와 정보의 표현이 얼마나 잘 구성되어 있는지 놀랐다.

http://openmymind.net/Multiple-Collections-Versus-Embedded-Documents

요약했습니다 :

일반적으로 [자식 문서]가 많거나 큰 경우에는 별도의 모음이 가장 좋습니다.

더 작거나 적은 문서는 자연스럽게 포함시키는 경향이 있습니다.


11
얼마 a lot입니까? 삼? 10? 100? 무엇입니까 large? 1kb? 1MB? 3 개의 필드? 20 개의 필드? smaller/ 무엇입니까 fewer?
Traxo

1
좋은 질문입니다. 구체적인 답변이없는 질문입니다. 같은 프레젠테이션에는 "포함 된 모든 문서 및 배열을 포함한 문서가 16MB를 초과 할 수 없습니다"라는 슬라이드가 포함되어있어, 컷오프 일 수도 있고 특정 상황에 적합한 것 / 합리적 일 수도 있습니다. 현재 프로젝트에서 내장 문서의 대부분은 1 : 1 관계 또는 내장 문서가 실제로 간단한 1 : 1 관계를위한 것입니다.
Chris Bloom

@ john-f-miller의 현재 최고 의견도 참조하십시오. 임계 값에 특정 숫자를 제공하지는 않지만 결정을 안내하는 데 도움이되는 추가 포인터가 포함되어 있습니다.
Chris Bloom

16

나는 이것이 아주 오래되었다는 것을 알고 있지만 지정된 주석 만 반환하는 방법에 대한 OP의 질문에 대한 답을 찾고 있다면 $ (조회) 연산자를 다음과 같이 사용할 수 있습니다 .

db.question.update({'comments.content': 'xxx'}, {'comments.$': true})

4
두 개의 주석이 동일한 내용을 가지고 있으면 작동하지 않습니다. 저자는 검색어에 저자를 추가 할 수 있다고 주장 할 수 있는데, 저자가 동일한 콘텐츠로 동일한 두 개의 댓글을 작성한 경우에도 작동하지 않습니다
Steel Brain

1
@SteelBrain : 잘 연주했습니다.
JakeStrang

12

예, 우리는 문서의 참조를 사용할 수 있습니다 .SQL i 조인과 같은 다른 문서를 채우려면 mongo db에서는 하나의 많은 관계 문서에 매핑하는 조인이 없습니다. 대신 채우기 를 사용 하여 시나리오를 수행 할 수 있습니다.

var mongoose = require('mongoose')
  , Schema = mongoose.Schema

var personSchema = Schema({
  _id     : Number,
  name    : String,
  age     : Number,
  stories : [{ type: Schema.Types.ObjectId, ref: 'Story' }]
});

var storySchema = Schema({
  _creator : { type: Number, ref: 'Person' },
  title    : String,
  fans     : [{ type: Number, ref: 'Person' }]
});

인구는 문서의 지정된 경로를 다른 컬렉션의 문서로 자동으로 바꾸는 프로세스입니다. 단일 문서, 여러 문서, 일반 개체, 여러 일반 개체 또는 쿼리에서 반환 된 모든 개체를 채울 수 있습니다. 몇 가지 예를 살펴 보겠습니다.

더 자세한 정보는 http://mongoosejs.com/docs/populate.html을 참조하십시오.


5
Mongoose는 채워진 각 필드에 대해 별도의 요청을 발행합니다. 이것은 서버에서 수행 될 때 SQL JOINS와 다릅니다. 여기에는 앱 서버와 mongodb 서버 간의 추가 트래픽이 포함됩니다. 다시, 당신은 최적화 할 때 이것을 고려할 수 있습니다. 그럼에도 불구하고, 당신의 대답은 여전히 ​​옳습니다.
Max

6

실제로 UML 사양에 대해 아무도 이야기하지 않은 이유가 궁금합니다. 일반적으로 집계가있는 경우 참조를 사용해야합니다. 그러나 컴포지션 인 경우 커플 링이 더 강력하므로 내장 문서를 사용해야합니다.

그리고 왜 그것이 논리적인지 빨리 이해할 것입니다. 부모와 독립적으로 개체가 존재할 수있는 경우 부모가 존재하지 않아도 개체에 액세스하려고합니다. 존재하지 않는 부모에 포함시킬 수 없으므로 고유 한 데이터 구조에 포함시켜야합니다. 부모가 존재하면 부모에 객체의 참조를 추가하여 서로 연결하십시오.

두 관계의 차이점이 무엇인지 정말로 모르십니까? 여기에 링크를 설명한다 : 집계 구성 대 UML에


왜 -1입니까? 이유를 분명히 설명해주십시오.
Bonjour123


1

지정된 설명을 편집하려면 내용과 질문을 어떻게 얻습니까?

주석 수와 변경하려는 주석 색인을 추적 한 경우 도트 연산자 ( SO 예 )를 사용할 수 있습니다 .

당신은 f.ex를 할 수 있습니다.

db.questions.update(
    {
        "title": "aaa"       
    }, 
    { 
        "comments.0.contents": "new text"
    }
)

(질문 안의 주석을 편집하는 다른 방법으로)

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