Computer >> 컴퓨터 >  >> 프로그래밍 >> MongoDB

MongoDB에서 고유값 선택 및 개수 집계하기 – aggregate 쿼리 완벽 가이드

MongoDB에서 배열 형태로 저장된 데이터 안에서 고유값(distinct)을 선택하고 각 값이 등장한 횟수까지 함께 집계해야 하는 경우가 자주 있습니다. 이 글에서는 애그리게이션(aggregate) 파이프라인을 활용해 이 작업을 수행하는 방법을 단계별로 살펴보겠습니다.

1. 샘플 컬렉션 생성하기

먼저 문서를 담고 있는 컬렉션을 생성합니다. 아래 예제에서는 demo586이라는 컬렉션에 학생 이름과 점수를 담은 details 배열 필드를 가진 문서 두 개를 삽입합니다.

> db.demo586.insertOne(
...    {"details": [
...       {
...          "Name":"Chris",
...          "Marks":71
...       },
...       {
...          "Name":"Chris",
...          "Marks":61
...       },
...       {
...          "Name":"David",
...          "Marks":81
...       }
...    ]
... }
... );
{
   "acknowledged" : true,
   "insertedId" : ObjectId("5e9200fefd2d90c177b5bcc7")
}
> db.demo586.insertOne(
... {"details": [
...    {
...       "Name":"Chris",
...       "Marks":71
...    },
...    {
...       "Name":"Carol",
...       "Marks":61
...    },
...    {
...       "Name":"David",
...       "Marks":81
...    }
... ]
... }
... );
{
   "acknowledged" : true,
   "insertedId" : ObjectId("5e9200fefd2d90c177b5bcc8")
}

2. find()로 전체 문서 확인하기

find() 메서드를 사용하면 컬렉션에 저장된 모든 문서를 확인할 수 있습니다.

> db.demo586.find();

위 명령을 실행하면 다음과 같은 결과가 출력됩니다.

{ "_id" : ObjectId("5e9200fefd2d90c177b5bcc7"), "details" : [
   { "Name" : "Chris", "Marks" : 71 },
   { "Name" : "Chris", "Marks" : 61 },
   { "Name" : "David", "Marks" : 81 }
] }
{ "_id" : ObjectId("5e9200fefd2d90c177b5bcc8"), "details" : [
   { "Name" : "Chris", "Marks" : 71 },
   { "Name" : "Carol", "Marks" : 61 },
   { "Name" : "David", "Marks" : 81 }
] }

3. 고유값 선택 및 개수 집계 쿼리

이제 핵심인 애그리게이션 쿼리입니다. 다음 파이프라인은 각 이름(Name)별로 중복을 제거한 점수(Marks) 목록과 해당 점수가 나타난 횟수를 함께 반환합니다.

> var q= [
...    { "$unwind": "$details" },
...    {
...       "$group": {
...          "_id": {
...             "Name": "$details.Name",
...             "Marks": "$details.Marks"
...          },
...          "count": { "$sum": 1 }
...       }
...    },
...    {
...       "$group": {
...          "_id": "$_id.Name",
...          "distinctV": {
...             "$addToSet": {
...                "value": "$_id.Marks",
...                "numberOfValues": "$count"
...             }
...          }
...       }  
...    },
...    {
...       "$project": {
...          "_id": 0,
...          "Name": "$_id",
...          "distinctV": 1
...       }
...    }
... ];
> db.demo586.aggregate(q);

쿼리 실행 결과는 다음과 같습니다.

{ "distinctV" : [ { "value" : 61, "numberOfValues" : 1 } ], "Name" : "Carol" }
{ "distinctV" : [ { "value" : 71, "numberOfValues" : 2 }, { "value" : 61, "numberOfValues" : 1 } ], "Name" : "Chris" }
{ "distinctV" : [ { "value" : 81, "numberOfValues" : 2 } ], "Name" : "David" }

파이프라인 단계별 동작 원리

  • $unwind: details 배열을 개별 문서로 펼쳐서 각 요소를 독립적으로 처리할 수 있게 만듭니다.
  • $group (첫 번째): 이름과 점수 조합을 기준으로 그룹화하고, $sum: 1로 각 조합이 등장한 횟수를 계산합니다.
  • $group (두 번째): 이름 기준으로 다시 그룹화하며, $addToSet을 사용해 중복 없는 점수 값과 개수 정보를 배열로 모읍니다.
  • $project: 불필요한 _id 필드를 제거하고 읽기 좋은 최종 출력 형태를 만듭니다.

결과 해석

  • Carol: 점수 61점이 1번 등장했습니다.
  • Chris: 점수 71점이 2번, 61점이 1번 등장했습니다.
  • David: 점수 81점이 2번 등장했습니다.

이처럼 $unwind, $group, $addToSet을 조합하면 배열 필드 안의 데이터도 손쉽게 고유값 추출과 빈도 집계를 동시에 처리할 수 있습니다. 대량의 데이터를 다룰 때는 인덱스 설계와 함께 이 패턴을 활용하면 효율적인 분석 쿼리를 작성할 수 있습니다.