04-26 20:29
Notice
Recent Posts
Recent Comments
관리 메뉴

Scientific Computing & Data Science

[MongoDB] Database References 본문

Data Science/MongoDB

[MongoDB] Database References

cinema4dr12 2014. 3. 10. 18:05

by Geol Choi | 

이번 글에서는 "데이터베이스 간 참조"에 대하여 알아보도록 하겠다.

도큐먼트를 참조하는 방법은 크게 두 가지가 있는데, 하나는 수동 참조(Manual Reference)이며 다른 하나는 "DBRef"를 사용하는 것이다.

그러면 각각에 대하여 자세히 알아보자.


수동 참조

수동 참조 방식은 참조할 다른 도큐먼트의 아이디(ObjectID)를 도큐먼트 내 하나의 키(key)로 저장하는 것이다. 즉, 키 값인 아이디를 통해 참조할 도큐먼트를 얻어내어 해당 도큐먼트의 다른 데이터를 얻는 방식이다. 이 방법은 간단한 방식으로 대부분의 경우에서 사용된다.

'백문이 불여일견'이므로 예제(예제는 MongoDB의 공식문서를 참조하여 작성하였다)를 통해 자세히 설명하도록 하겠다. 우선 다음과 같은 도큐먼트를 준비한다.

db.places.insert({
    name: "Broadway Center",
    url: "bc.example.net"
})

db.people.insert({
    name: "Erin",
    places_id: db.places.findOne({name: "Broadway Center"})._id,
    url:  "bc.example.net/Erin"
})

"places"와 "people" 두 개의 컬렉션을 생성하였는데, "people" 컬렉션의 "places_id" 키의 값은(빨간색 하일라이트) "places" 컬렉션으로부터 "name" 키(key)가 "Broadway Center"인 도큐먼트를 찾고 그 도큐먼트의 아이디(._id)임을 알 수 있다.

이제 "people" 컬렉션으로부터 "name"이 "Erin"인 도큐먼트의 "places_id"를 얻어오고, 그 아이디로부터 "places" 컬렉션의 아이디와 일치하는 도큐먼트를 찾아 "url"을 얻어오는 일련의 프로세스를 살펴보도록 하자.

> var peopleDoc = db.people.findOne({name: "Erin"});
> var placeID = peopleDoc.places_id;
> var placesDoc = db.places.findOne({_id: placeID});
> placesDoc.url
bc.example.net

만약 이 과정이 너무 길고 지루하다면 다음과 같이 한 줄로 표현할 수도 있다:

> db.places.findOne({ _id: db.people.findOne({name: "Erin"}).places_id }).url
bc.example.net

이 구조를 그림으로 표현하면 다음과 같다:

비교적 쉽고 간단하지 않은가?

그럼 이번에는 DBRef를 사용하는 방법에 대해 알아보도록 하겠다.


DBRef

DBRef는 참조할 도큐먼트의 "_id" 필드의 값과 옵션으로서의 데이터베이스 이름을 이용하여 어느 하나의 도큐먼트가 다른 도큐먼트를 참조하는 것이다.

DBRef는 총 3개의 인자를 취하는데 처음 두 개는 필수 인자인 $ref, $id이며, 세번째 인자는 옵션 인자인 $db이다. 각각에 대한 설명은 다음과 같다:

$ref

참조할 도큐먼트가 존재하는 컬렉션의 이름.

$id

참조된 도큐먼트 내 _id 필드의 값.

$db

참조할 도큐먼트가 존재하는 데이터베이스의 이름.

예제로 설명을 하겠다. 우선 다음과 같이 컬렉션을 준비한다:

> db.users.insert({"_id" : "mike", "display_name" : "Mike D"})
> db.users.insert({"_id" : "kristina", "display_name" : "Kristina C"})

> db.notes.insert({"_id" : 5, "author" : "mike", "text" : "MongoDB is fun!"})
> db.notes.insert({"_id" : 20, "author" : "kristina", "text" : "... and DBRefs are easy, too", "references": [{"$ref" : "users", "$id" : "mike"}, {"$ref" : "notes", "$id" : 5}]})

> db.users.find().pretty()
{ "_id" : "mike", "display_name" : "Mike D" }
{ "_id" : "kristina", "display_name" : "Kristina C" }

> db.notes.find().pretty()
{ "_id" : 5, "author" : "mike", "text" : "MongoDB is fun!" }
{
    "_id" : 20,
    "author" : "kristina",
    "text" : "... and DBRefs are easy, too",
    "references" : [
        DBRef("users", "mike"),
        DBRef("notes", 5)
    ]
}

"notes" 컬렉션의 _id가 20인 도큐먼트의 "references" 필드를 보면 두 개의 DBRef가 있는 것을 볼 수 있다. 두 개의 인자를 취하고 있는 것을 볼 수 있는데, 첫번째 인자는 참조할 컬렉션의 이름이며 두번째 인자는 해당 컬렉션의 도큐먼트를 찾을 필드값이라고 보면 된다.

예를 들어, DBRef("users", "mike")는 "users" 컬렉션의 "_id"가 "mike"인 도큐먼트를 참조할 목적으로, DBRef("notes", 5)는 "notes" 컬렉션의 "_id"가 5인 도큐먼트를 찾을 목적으로 참조할 목적으로 저장한 것이다.

우선, "note"라는 변수에 "notes" 컬렉션의 "_id"가 20인 도큐먼트를 저장한다:

> var note = db.notes.findOne({"_id" : 20});
> note
{
    "_id" : 20,
    "author" : "kristina",
    "text" : "... and DBRefs are easy, too",
    "references" : [
        DBRef("users", "mike"),
        DBRef("notes", 5)
    ]
}

결과부터 출력 해보면 다음과 같다:

> note.references.forEach(function(ref) {
... printjson(db[ref.$ref].findOne({"_id" : ref.$id}));
... });
{ "_id" : "mike", "display_name" : "Mike D" }
{ "_id" : 5, "author" : "mike", "text" : "MongoDB is fun!" }

먼저 주목해야 할 것이 "note.references"이다:

> note.references
[ DBRef("users", "mike"), DBRef("notes", 5) ]

위에서 보듯이 note.references는 두 개의 DBRef 요소이다. 함수의 인자인 "ref"가 바로 이 두 개의 DBRef 요소이다. 이것은 다음을 통해 확인할 수 있다:

> note.references.forEach(function(ref) {
... print(ref);
... });
DBRef("users", "mike")
DBRef("notes", 5)

"note" 컬렉션을 정의된 내용을 참고해 보면, "users"와 "notes"는 $ref이며, "mike"와 5는 $id이다. 다음을 통해 이러한 사실을 확인할 수 있다:

> note.references.forEach(function(ref) {
... print(ref.$ref);
... });
users
notes
>
> note.references.forEach(function(ref) {
... print(ref.$id);
... });
mike
5

따라서, "db[ref.$ref].findOne({"_id" : ref.$id})"는, 첫번째 DBRef 요소에 대해

db[users].findOne({"_id": "mike"});

와 같으며, 두번째 DBRef 요소에 대해

db[notes].findOne({"_id": 5});

와 같다. 따라서, 출력결과가 어떻게 나왔는지 이해할 수 있다.

앞서 도큐먼트를 참조하는 방법은 "수동참조"와 "DBRef"를 사용하는 방법 두 가지에 대해 알아보았다. 수동참조는 _id만 저장하면 되지만 DBRef는 이보다 많은 정보를 저장해야 하므로 어떤 면에서는 비효율적으로 보인다. 그렇다면 DBRef는 언제 사용하는 것이 좋은가?

간단히 말해, 다른 컬렉션 내의 도큐먼트에 이종의 참조를 저장할 경우에 가장 적합하다. 이는 앞의 예제에서 보는 바와 같다. 또 다른 경우는 driver나 tool에서 DBRef에 특정한 추가 기능을 사용할 때이다. MongoDB의 driver 종류에 대해 알아보려면 여기를 클릭한다.

Comments