이번 포스팅에서는 "데이터베이스 간 참조"에 대하여 알아보도록 하겠습니다.
도큐먼트를 참조하는 방법은 크게 두 가지가 있는데, 하나는 수동 참조(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 종류에 대해 알아보려면 여기를 클릭하시기 바랍니다.