MongoDB 가 하나의 컬렉션안에 다양한 계층과 릴레이션 관계를 JSON 으로 구조화 할 수 있긴 하지만, 결국에는 collection 간에 데이터를 조인(JOIN) 해야 하는 니즈는 생기기 마련이다. aggregate
함수에서는 $lookup
기능을 이용하여 컬렉션간에 데이터를 JOIN 할 수 있다.
$lookup 사용법
샘플데이터를 하나 준비했다. 학원 수납정보인 sampleData
컬렉션과 회원정보 member
컬렉션을 조인해보기로 한다. 그 중 sampleData
컬렉션의 memberId
필드는 회원정보 member
컬렉션에 있는 _id
필드이다.
mydb> db.sampleData.find()
// 출력결과
[
{
_id: ObjectId("65c8df8b20d712293ea1d0f1"),
memberId: '655808defd437954470f9d7d',
paymentDate: '2024-02-09',
amount: 90000
}
]
member
컬렉션에서 _id
값은 ObjectId
타입이라서 아래와 같이 조회해야 한다.
db.member.find({ _id : ObjectId('655808defd437954470f9d7d') })
그렇다보니, member
컬렉션과 조인하려면 memberId
필드를 미리 ObjectId
타입으로 변경해놓아야 한다. aggregate
함수내에서는 $toObjectId
로 변환한다.
db.sampleData.aggregate([
{
$addFields : {
memberObjectId : {
$toObjectId : "$memberId"
}
}
}
])
// 출력결과
[
{
_id: ObjectId("65c8df8b20d712293ea1d0f1"),
memberId: '655808defd437954470f9d7d',
paymentDate: '2024-02-09',
amount: 90000,
memberObjectId: ObjectId("655808defd437954470f9d7d")
}
]
이제 준비가 끝났으니 $lookup
으로 조인만 하면 된다.
db.sampleData.aggregate([
{
$addFields : {
memberObjectId : {
$toObjectId : "$memberId"
}
}
},
{
$lookup : {
from : "member",
localField : "memberObjectId",
foreignField : "_id",
as: "member_info"
}
}
])
출력결과를 보면 member_info
이름으로 조인된걸 볼 수 있다.
// 출력결과
[
{
_id: ObjectId("65c8df8b20d712293ea1d0f1"),
memberId: '655808defd437954470f9d7d',
paymentDate: '2024-02-09',
amount: 90000,
memberObjectId: ObjectId("655808defd437954470f9d7d"),
member_info: [
{
_id: ObjectId("655808defd437954470f9d7d"),
name: '홍길동',
gender: 'M',
membershipDate: '2023-12-02',
delYn: false
}
]
}
]
여기가 완성이면 좋겠지만, $project
로 필요한 데이터만 출력해보려 한다.
db.sampleData.aggregate([
{
$addFields : {
memberObjectId : {
$toObjectId : "$memberId"
}
}
},
{
$lookup : {
from : "member",
localField : "memberObjectId",
foreignField : "_id",
as: "member_info"
}
},
{
$project : {
memberId : 1,
paymentDate : 1,
amount : 1,
"member_info.name" : 1,
"member_info.gender" : 1
}
}
])
그런데 결과를 보니, member_info
구조가 그대로 유지되어 있을 뿐아니라, array 형식도 유지되어 있다.
// 출력결과
[
{
_id: ObjectId("65c8df8b20d712293ea1d0f1"),
memberId: '655808defd437954470f9d7d',
paymentDate: '2024-02-09',
amount: 90000,
member_info: [ { name: '홍길동', gender: 'M' } ]
}
]
$lookup
으로 조인된 데이터는 [ ]
로 묶여있다. array로 연결되어 있기 때문에, $arrayElemAt
기능으로 꺼내어 필요한 필드만 가져와야 한다. 어짜피 이 샘플데이터들은 1:1로 조인되는 구조라 이렇게 한 것이고, 다건인 경우 케이스에 맞춰 정제해야 한다.
db.sampleData.aggregate([
{
$addFields : {
memberObjectId : {
$toObjectId : "$memberId"
}
}
},
{
$lookup : {
from : "member",
localField : "memberObjectId",
foreignField : "_id",
as: "member_info"
}
},
{
$addFields: {
name : {
$arrayElemAt : [ "$member_info.name", 0 ]
},
gender : {
$arrayElemAt : [ "$member_info.gender", 0 ]
}
}
}
])
name
, gender
가 밖으로 추가된게 보인다.
// 출력결과
[
{
_id: ObjectId("65c8df8b20d712293ea1d0f1"),
memberId: '655808defd437954470f9d7d',
paymentDate: '2024-02-09',
amount: 90000,
memberObjectId: ObjectId("655808defd437954470f9d7d"),
member_info: [
{
_id: ObjectId("655808defd437954470f9d7d"),
name: '홍길동',
gender: 'M',
membershipDate: '2023-12-02',
delYn: false
}
],
name: '홍길동',
gender: 'M'
}
]
마지막으로 정말 출력할 필드만 $project
로 정의해주면 된다.
db.sampleData.aggregate([
{
$addFields : {
memberObjectId : {
$toObjectId : "$memberId"
}
}
},
{
$lookup : {
from : "member",
localField : "memberObjectId",
foreignField : "_id",
as: "member_info"
}
},
{
$addFields: {
name : {
$arrayElemAt : [ "$member_info.name", 0 ]
},
gender : {
$arrayElemAt : [ "$member_info.gender", 0 ]
}
}
},
{
$project : {
memberId : 1,
paymentDate : 1,
amount : 1,
name : 1,
gender : 1
}
}
])
// 출력결과
[
{
_id: ObjectId("65c8df8b20d712293ea1d0f1"),
memberId: '655808defd437954470f9d7d',
paymentDate: '2024-02-09',
amount: 90000,
name: '홍길동',
gender: 'M'
}
]