MongoDB에서 $project로 필드 프로젝션하기
MongoDB에서 특정 필드를 프로젝션(projection)하려면 집계 파이프라인의 $project 연산자를 사용합니다. 실무에서는 문서마다 필드 이름이 다르게 저장되는 경우가 종종 있습니다. 예를 들어 어떤 문서에는 Marks1, Marks2로, 다른 문서에는 Score1, Score2로 점수가 저장되어 있을 수 있죠. 이럴 때 $ifNull과 $cond를 함께 사용하면 필드 이름이 달라도 하나의 일관된 출력 형태로 데이터를 추출할 수 있습니다.
아래 예제를 통해 단계별로 살펴보겠습니다.
1. 컬렉션 생성 및 문서 삽입
먼저 demo439 컬렉션을 생성하고 세 개의 문서를 삽입합니다.
> db.demo439.insertOne(
... {
... "Name" : "Chris",
... "MarksInformation" : {
... "Marks1" : 67,
... "Marks2" :45,
... "Marks3" : 78
... }
... }
... );
{
"acknowledged" : true,
"insertedId" : ObjectId("5e77833abbc41e36cc3caeab")
}
> db.demo439.insertOne(
... {
... "Name" : "David",
... "MarksInformation" : {
... "Marks1" : 50,
... "Marks2" :57,
... "Marks3" : 68
... }
... }
... );
{
"acknowledged" : true,
"insertedId" : ObjectId("5e77833abbc41e36cc3caeac")
}
> db.demo439.insertOne(
... {
... "Name" : "Bob",
... "MarksInformation" : {
... "Score1" : 65,
... "Score2" :71,
...
... }
... }
... );
{
"acknowledged" : true,
"insertedId" : ObjectId("5e77833bbbc41e36cc3caead")
}Chris와 David의 문서는 Marks1, Marks2, Marks3 필드를 가지고 있는 반면, Bob의 문서는 Score1, Score2라는 다른 필드 이름으로 점수가 저장되어 있습니다.
2. find() 메서드로 전체 문서 조회하기
find() 메서드를 사용해 컬렉션의 모든 문서를 확인합니다.
> db.demo439.find();
실행 결과는 다음과 같습니다.
{ "_id" : ObjectId("5e77833abbc41e36cc3caeab"), "Name" : "Chris", "MarksInformation" : { "Marks1" : 67, "Marks2" : 45, "Marks3" : 78 } }
{ "_id" : ObjectId("5e77833abbc41e36cc3caeac"), "Name" : "David", "MarksInformation" : { "Marks1" : 50, "Marks2" : 57, "Marks3" : 68 } }
{ "_id" : ObjectId("5e77833bbbc41e36cc3caead"), "Name" : "Bob", "MarksInformation" : { "Score1" : 65, "Score2" : 71 } }3. $project를 사용한 필드 프로젝션 쿼리
이제 $project를 사용해 필드를 프로젝션하는 쿼리를 작성합니다. 핵심은 $ifNull로 필드 존재 여부를 확인하고, $cond로 조건 분기를 처리하는 것입니다.
> db.demo439.aggregate({
... $project: {
... "Name" : 1,
... "Marks1": { $cond: [ { $eq:[ { $ifNull: [ "$MarksInformation.Marks1", 0 ] }, 0 ] },{ $ifNull: [ "$MarksInformation.Score1", 0 ] }, "$MarksInformation.Marks1" ] },
... "Marks2": { $cond: [ { $eq:[ { $ifNull: [ "$MarksInformation.Marks2", 0 ] }, 0 ] }, { $ifNull: [ "$MarksInformation.Score2", 0 ] }, "$MarksInformation.Marks2" ] },
... "Marks3": { $cond: [ { $eq:[ { $ifNull: [ "$MarksInformation.Marks3", 0] }, 0 ] }, { $ifNull: [ "$MarksInformation.Score3", 0 ] }, "$MarksInformation.Marks3" ] }
... }})쿼리 동작 방식은 다음과 같습니다.
- $ifNull: 지정한 필드가 존재하지 않거나 null일 경우 대체값(여기서는 0)을 반환합니다.
- $eq:
$ifNull의 결과가 0인지 비교하여 해당 필드(Marks)가 실제로 존재하는지 판단합니다. - $cond: 조건이 참이면 두 번째 표현식(Score 필드 값), 거짓이면 세 번째 표현식(Marks 필드 값)을 반환합니다.
즉, Marks1 필드가 없는 문서라면 자동으로 Score1 값을 가져오고, 둘 다 없으면 0을 반환합니다.
4. 실행 결과
쿼리 실행 결과는 다음과 같습니다.
{ "_id" : ObjectId("5e77833abbc41e36cc3caeab"), "Name" : "Chris", "Marks1" : 67, "Marks2" : 45, "Marks3" : 78 }
{ "_id" : ObjectId("5e77833abbc41e36cc3caeac"), "Name" : "David", "Marks1" : 50, "Marks2" : 57, "Marks3" : 68 }
{ "_id" : ObjectId("5e77833bbbc41e36cc3caead"), "Name" : "Bob", "Marks1" : 65, "Marks2" : 71, "Marks3" : 0 }Bob의 문서는 원래 Score1, Score2 필드만 가지고 있었지만, 결과에서는 Marks1: 65, Marks2: 71로 통일되어 출력되고, 존재하지 않는 Marks3는 0으로 표시됩니다. 이처럼 $project와 $ifNull, $cond를 조합하면 스키마가 불규칙한 컬렉션도 손쉽게 정규화된 형태로 조회할 수 있습니다.