MongoDB에서 중첩(nested) 배열을 집계하려면 aggregate() 메서드를 사용합니다. 이 글에서는 실제 예제를 통해 중첩 배열 안의 데이터를 조건에 따라 필터링하고, 새로운 필드로 출력하는 전체 과정을 단계별로 살펴보겠습니다.
1. 샘플 컬렉션 생성하기
먼저 insertOne() 메서드로 테스트용 문서를 삽입합니다. 이 문서는 'details' 배열 안에 'details1'이라는 배열이 한 번 더 중첩된 구조를 가지고 있습니다.
> db.demo441.insertOne(
... {
... "Name" : "David",
... "Age" : 21,
... "details" : [
... {
... "id" : 1,
... "CountryName" : "US",
... "details1" : [
... {
... "SubjectName" : "MySQL",
... "Score":56
... },
... {
... "SubjectName" : "MongoDB",
... "Score":78
... }
... ]
... }
... ]
... }
... );
{
"acknowledged" : true,
"insertedId" : ObjectId("5e78cc05bbc41e36cc3caeb7")
}
위 코드는 학생 정보(Name, Age)와 함께 국가별 과목 점수 배열을 포함하는 문서를 생성합니다. 'details1' 배열에는 MySQL(56점)과 MongoDB(78점) 두 개의 과목 데이터가 들어 있습니다.
2. 저장된 문서 확인하기
find() 메서드를 사용하면 컬렉션의 모든 문서를 조회할 수 있습니다.
> db.demo441.find();
실행 결과는 다음과 같습니다.
{ "_id" : ObjectId("5e78cc05bbc41e36cc3caeb7"), "Name" : "David", "Age" : 21, "details" : [ { "id" : 1, "CountryName" : "US", "details1" : [ { "SubjectName" : "MySQL", "Score" : 56 }, { "SubjectName" : "MongoDB", "Score" : 78 } ] } ] }3. 중첩 배열 집계 쿼리 작성하기
이제 핵심 단계입니다. 아래 쿼리는 $addFields, $map, $let, $arrayElemAt, $cond 연산자를 조합하여 중첩 배열을 처리합니다. 쿼리의 동작 방식은 다음과 같습니다.
- $addFields: 기존 문서에 'ResultOfDetails'라는 새 필드를 추가합니다.
- $map: 'details' 배열의 각 요소를 순회하며 변환 작업을 수행합니다.
- $arrayElemAt (-1): 'details1' 배열의 마지막 요소를 가져옵니다.
- $cond: 마지막 과목의 점수(Score)가 78점과 일치하는지 검사하고, 일치하면 해당 요소를 포함하고 그렇지 않으면 빈 배열을 반환합니다.
> db.demo441.aggregate([{
... $addFields: {
... ResultOfDetails: {
... $map: {
... input: "$details",
... as: "output",
... in: {
... id: "$$output.id",
... CountryName: "$$output.CountryName",
... details1: {
... $let: {
... vars: {
... last: {
... $arrayElemAt: ["$$output.details1", -1]
... }
... },
... in: {
... $cond: [{
... $eq: ["$$last.Score", 78]
... },
... ["$$last"],
... []
... }
... }
... }
... }
... }
... }
... }
... }]).pretty();집계 결과
쿼리를 실행하면 다음과 같은 출력을 확인할 수 있습니다.
{
"_id" : ObjectId("5e78cc05bbc41e36cc3caeb7"),
"Name" : "David",
"Age" : 21,
"details" : [
{
"id" : 1,
"CountryName" : "US",
"details1" : [
{
"SubjectName" : "MySQL",
"Score" : 56
},
{
"SubjectName" : "MongoDB",
"Score" : 78
}
]
}
],
"ResultOfDetails" : [
{
"id" : 1,
"CountryName" : "US",
"details1" : [
{
"SubjectName" : "MongoDB",
"Score" : 78
}
]
}
]
}결과를 보면 원본 'details' 필드는 그대로 유지되고, 새로 추가된 'ResultOfDetails' 필드에는 점수가 78점인 MongoDB 과목 데이터만 담긴 것을 확인할 수 있습니다. 이처럼 aggregate() 파이프라인과 배열 연산자를 조합하면 깊게 중첩된 배열 구조에서도 원하는 조건의 데이터만 손쉽게 추출할 수 있습니다.