MongoDB의 애그리게이션 파이프라인에서 $group 연산을 사용할 때, 각 그룹 안에서 특정 기준으로 정렬된 첫 번째 문서만 추출해야 하는 경우가 자주 있습니다. 이 글에서는 $sort, $group, $project 단계를 조합해 이 작업을 수행하는 방법을 예제와 함께 살펴보겠습니다.
샘플 컬렉션 생성하기
먼저 demo576 컬렉션을 만들고 문서를 차례대로 삽입합니다.
> db.demo576.insertOne({id:101,Name:"Chris",Marks:45})
{ "acknowledged" : true, "insertedId" : ObjectId("5e916c3b581e9acd78b427fa") }
> db.demo576.insertOne({id:101,Name:"John",Marks:55})
{ "acknowledged" : true, "insertedId" : ObjectId("5e916c43581e9acd78b427fb") }
> db.demo576.insertOne({id:101,Name:"John",Marks:65})
{ "acknowledged" : true, "insertedId" : ObjectId("5e916c47581e9acd78b427fc") }
> db.demo576.insertOne({id:102,Name:"David",Marks:37})
{ "acknowledged" : true, "insertedId" : ObjectId("5e916c55581e9acd78b427fd") }
> db.demo576.insertOne({id:102,Name:"David",Marks:75})
{ "acknowledged" : true, "insertedId" : ObjectId("5e916c5e581e9acd78b427fe") }
전체 문서 조회하기
find() 메서드를 사용하면 컬렉션에 저장된 모든 문서를 확인할 수 있습니다.
> db.demo576.find();
실행하면 다음과 같은 결과가 출력됩니다.
{ "_id" : ObjectId("5e916c3b581e9acd78b427fa"), "id" : 101, "Name" : "Chris", "Marks" : 45 }
{ "_id" : ObjectId("5e916c43581e9acd78b427fb"), "id" : 101, "Name" : "John", "Marks" : 55 }
{ "_id" : ObjectId("5e916c47581e9acd78b427fc"), "id" : 101, "Name" : "John", "Marks" : 65 }
{ "_id" : ObjectId("5e916c55581e9acd78b427fd"), "id" : 102, "Name" : "David", "Marks" : 37 }
{ "_id" : ObjectId("5e916c5e581e9acd78b427fe"), "id" : 102, "Name" : "David", "Marks" : 75 }
$group 연산에서 정렬 후 첫 번째 문서만 가져오기
다음은 데이터를 먼저 정렬한 뒤 $group 연산으로 각 그룹의 첫 번째 문서만 가져오는 쿼리입니다.
> var query = [
... {
... "$sort": { "Marks": 1 }
... },
... {
... "$group": {
... "_id": "$id",
... "out": { "$first": "$$ROOT" }
... }
... },
... {
... "$project": {
... "_id": "$out._id",
... "id": "$out.id",
... "Name": "$out.Name",
... "MinMarks": "$out.Marks"
... }
... }
... ]
> db.demo576.aggregate(query);
쿼리 동작 원리
- $sort:
Marks필드를 오름차순(1)으로 정렬해, 점수가 가장 낮은 문서가 각 그룹의 맨 앞에 오도록 만듭니다. - $group:
id필드를 기준으로 문서를 묶고,$first연산자와$$ROOT변수를 이용해 각 그룹의 첫 번째 문서 전체를out필드에 담습니다. - $project: 최종 결과에서
_id,id,Name, 그리고 최소 점수를 의미하는MinMarks만 선택해 깔끔하게 출력합니다.
실행 결과
애그리게이션을 실행하면 각 id 그룹별로 가장 낮은 점수를 가진 문서가 하나씩 반환됩니다.
{ "_id" : ObjectId("5e916c3b581e9acd78b427fa"), "id" : 101, "Name" : "Chris", "MinMarks" : 45 }
{ "_id" : ObjectId("5e916c55581e9acd78b427fd"), "id" : 102, "Name" : "David", "MinMarks" : 37 }
이처럼 $sort → $group($first) → $project 순서로 파이프라인을 구성하면 그룹별 최솟값과 함께 해당 문서의 다른 필드 정보도 한 번에 얻을 수 있습니다. 정렬 방향을 내림차순(-1)으로 바꾸면 그룹별 최댓값을 가진 문서를 손쉽게 구할 수 있으니, 분석 목적에 맞게 활용해 보시기 바랍니다.