[MongoDB] 몽고DB에서 Join을 해보자 - $lookup으로 Join하기, MongoDB Join 예제
NOSql인 MongoDB는 Join을 지원하지 않는다.
하지만, MongoDB 3.2 버전부터는 Join과 비슷한 역할을 하는 '$lookup'을 지원하고 있다.
MongoDB의 Aggregation에서 사용할 수 있는 '$lookup'으로 컬렉션끼리 Join하는 방법을 알아보자.
Products 컬렉션
db.products.insert([
{"_id" : objectId('111111111111'), "title" : "책제목1", "category" : '시'},
{"_id" : objectId('222222222222'), "title" : "책제목2", "category" : '소설'},
{"_id" : objectId('333333333333'), "title" : "책제목3", "category" : '과학'},
{"_id" : objectId('444444444444'), "title" : "책제목4", "category" : '예술'},
])
Recommendation 컬렉션
db.recommendation.insert([
{"product_id" : '111111111111', "description" : "1 추천합니다."},
{"product_id" : '222222222222', "description" : "2 추천합니다."},
{"product_id" : '333333333333', "description" : "3 추천합니다."},
])
상품데이터가 담긴 Products 컬렉션과, 추천 상품 리스트가 담긴 Recommendation 컬렉션이 있다.
Recommendation 컬렉션은 추천 상품의 id값(product_id)을 가지고 있다.
Recommendation 컬렉션의 데이터를 불러올 때 이 product_id 값을 이용해 해당 Products의 정보를 함께 가져오는 기능을 구현해보자.
Recommendation 컬렉션과 Products 컬렉션 함께 조회하기
db.recommendation.aggregate([
{ $addFields: { productId: { $toObjectId: "$product_id" } } },
{
$lookup: {
from: "products",
localField: "productId",
foreignField: "_id",
as: "productInfo",
},
},
{ $unwind: "$productInfo" },
{
$project: {
product_id: 1,
description: 1,
title: "$productInfo.title",
category: "$productInfo.category",
},
},
]);
위 코드를 차근차근 한번 살펴보면,
우선 나는 product_id를 objectId의 형태로 저장하지 않고 String 값으로 저장했기 때문에,
Products에 저장된 objectId 형태의_id와 비교하기 위해 productId라는 필드를 하나 생성해, product_id를 objectId 형태로 바꿔 저장해주었다.
{ $addFields: { productId: { $toObjectId: "$product_id" } } }
이제 $lookup을 사용할 차례다.
- from에는 Join할 컬렉션을 -> 'products'
- localField에는 현재 컬렉션에서 비교할 key값을 -> 아까 비교를 위해 생성해둔 objectId값을 담은 'productId'
- foreignField에는 Join할 컬렉션에서 비교할 key값을 -> products에 저장된 '_id'
- as에는 Join 후 새로 생성될 필드명을 적어준다.
{
$lookup: {
from: "products",
localField: "productId",
foreignField: "_id",
as: "productInfo",
},
},
$unwind는 배열형태로 된 필드를 하나하나 분리해주는 기능이다.
새롭게 생성된 productInfo는 데이터가 하나의 객체지만, [{ }] 이렇게 배열 형태로 감싸져있어, 데이터에 접근 시 불필요하게 productInfo[0].filed 로 접근해야하기 때문에 $unwind를 이용해 배열을 분리해준다.
{ $unwind: "$productInfo" }
마지막으로 $project를 이용해 필요한 필드값을 지정해준다.
Join한 모든 데이터를 사용할 예정이라면 필요없는 과정이지만, 필요한 값만 사용하고 싶다면 필요하다.
recommendation에 원래 있던 필드는 1을 지정해 가져올 필드 데이터에 포함시키고,
productInfo에 있던 필드는 productInfo 객체의 필드로 두지 않고, 아예 전체 필드 목록에 포함시키기 위해, 필드명을 직접 지정해준 뒤, 데이터를 "$productInfo.원하는필드"로 할당해주었다.
{
$project: {
product_id: 1,
description: 1,
title: "$productInfo.title",
category: "$productInfo.category",
},
}
결괏값
[{
"product_id" : '111111111111',
"description" : "1 추천합니다.",
"title" : "책제목1",
"category" : "시"
},
{
"product_id" : '222222222222',
"description" : "2 추천합니다.",
"title" : "책제목2",
"category" : "소설"
},
{
"product_id" : '333333333333',
"description" : "3 추천합니다.",
"title" : "책제목3",
"category" : "과학"
}]