$group (집계)
정의
$group
$group
단계는 '그룹 키'에 따라 문서를 그룹으로 분리합니다. 출력은 각 고유 그룹 키당 하나의 문서입니다.그룹 키는 대부분 필드이거나 필드 그룹입니다. 그룹 키는 표현식의 결과일 수도 있습니다. 그룹 키는
$group
파이프라인 단계의_id
필드를 사용해 설정할 수 있습니다. 사용 예시는 아래를 참조하세요.$group
단계 출력에서_id
필드는 해당 문서의 그룹 키로 설정됩니다.출력 문서에는 축적자 표현식을 사용하여 설정한 추가 필드도 포함될 수 있습니다.
참고
$group
은 출력 문서의 순서를 지정하지 않습니다.
호환성
다음 환경에서 호스팅되는 배포에 $group
사용할 수 있습니다.
MongoDB Atlas: 클라우드에서의 MongoDB 배포를 위한 완전 관리형 서비스
MongoDB Enterprise: MongoDB의 구독 기반 자체 관리 버전
MongoDB Community: MongoDB의 소스 사용 가능 무료 자체 관리 버전
구문
$group
단계의 프로토타입 형식은 다음과 같습니다.
{ $group: { _id: <expression>, // Group key <field1>: { <accumulator1> : <expression1> }, ... } }
필드 | 설명 |
---|---|
_id | 필수 사항입니다. _id 표현식은 그룹 키를 지정합니다. _id null 값 또는 기타 상수 값을 지정하는 경우 $group 단계는 모든 입력 문서의 값을 집계하는 단일 문서를 반환합니다. Null로 그룹화 예시를 참조하세요. |
field | 선택 사항. 누산기 연산자를 사용해 계산합니다. |
_id
및 축적자 연산자 는 유효한 모든 expression
을 사용할 수 있습니다. 표현식에 대한 자세한 내용은 표현식을 참조하세요 .
고려 사항
성능
$group
는 차단 단계로, 파이프라인이 데이터를 처리하기 전에 차단 단계에 대해 모든 입력 데이터가 조회될 때까지 기다립니다. 차단 단계는 여러 단계가 있는 pipeline에 대한 병렬 처리를 줄이기 때문에 성능을 저하시킬 수 있습니다. 차단 단계는 큰 data set에 대해 상당한 양의 메모리를 사용할 수도 있습니다.
누산기 연산자
<accumulator>
연산자는 다음 누산기 연산자 중 하나이어야 합니다.
버전 5.0에서 변경됨
이름 | 설명 |
---|---|
사용자 정의 누산기 함수의 결과를 반환합니다. | |
각 그룹에 대한 고유한 표현식 값의 배열을 반환합니다. 배열 요소의 순서가 정의되지 않았습니다. 버전 5.0에서 변경: | |
숫자 값의 평균을 반환합니다. 숫자가 아닌 값을 무시합니다. 버전 5.0에서 변경: | |
그룹에 있는 문서 수를 반환합니다.
버전 5.2의 새로운 기능: | |
각 그룹의 첫 번째 문서에서 값을 반환합니다. 순서는 문서가 정렬된 경우에만 정의됩니다.
버전 5.0에서 변경: | |
그룹 내 첫 버전 5.2의 새로운 기능: | |
각 그룹의 마지막 문서에서 값을 반환합니다. 순서는 문서가 정렬된 경우에만 정의됩니다.
버전 5.0에서 변경: | |
그룹 내 마지막 버전 5.2의 새로운 기능: | |
각 그룹에 대해 가장 높은 표현식 값을 반환합니다. 버전 5.0에서 변경: | |
그룹 내 최댓값을 가진 개 요소의 집계를 반환합니다. 버전 5.2에 추가되었습니다.
| |
각 그룹에 대한 입력 문서를 결합하여
생성된 문서를 반환합니다. | |
각 그룹에 대해 가장 낮은 표현식 값을 반환합니다. 버전 5.0에서 변경: | |
그룹 내 최솟값을 가진 개 요소의 집계를 반환합니다. 버전 5.2에 추가되었습니다.
| |
각 그룹의 문서에 대한 표현식 값의 배열을 반환합니다. 버전 5.0에서 변경: | |
입력 값의 모집단 표준 편차를 반환합니다. 버전 5.0에서 변경: | |
입력 값의 표본 표준 편차를 반환합니다. 버전 5.0에서 변경: | |
숫자 값의 합계를 반환합니다. 숫자가 아닌 값을 무시합니다. 버전 5.0에서 변경: | |
$group
및 메모리 제한
$group
단계가 100MB의 RAM을 초과하면 MongoDB는 임시 파일에 데이터를 씁니다. 그러나 allowDiskUse 옵션이 false
로 설정되어 있으면 $group
은 오류를 반환합니다. 자세한 내용은 집계 파이프라인 제한을 참조하세요.
$group
성능 최적화
이 섹션에서는 $group
의 성능을 개선하기 위한 최적화에 대해 설명합니다. 수동으로 수행할 수 있는 최적화와 MongoDB가 내부적으로 수행할 수 있는 최적화가 있습니다.
각 그룹의 첫 번째 문서 반환 최적화
파이프라인이 동일한 필드를 기준으로 sorts
및 groups
를 지정하고 $group
단계에서 $first
축적자 연산자만 사용하는 경우 정렬 순서와 일치하는 그룹화된 필드에 인덱스 를 추가하는 것이 좋습니다. 경우에 따라 $group
단계에서는 인덱스를 사용하여 각 그룹의 첫 번째 문서를 빠르게 찾을 수 있습니다.
예시
foo
이라는 컬렉션에 { x: 1, y: 1 }
인덱스가 포함된 경우 다음 파이프라인은 해당 인덱스를 사용하여 각 그룹의 첫 번째 문서를 찾을 수 있습니다.
db.foo.aggregate([ { $sort:{ x : 1, y : 1 } }, { $group: { _id: { x : "$x" }, y: { $first : "$y" } } } ])
슬롯 기반 쿼리 실행 엔진
버전 5.2 부터 MongoDB는 다음 중 하나에 해당하는 경우 슬롯 기반 실행 쿼리 엔진을 사용하여 $group
단계를 실행합니다.
$group
파이프라인의 첫 번째 단계입니다.파이프라인의 모든 이전 단계는 슬롯 기반 엔진으로 실행할 수도 있습니다.
자세한 내용은 $group
최적화를 참조하세요.
예시
컬렉션 내의 문서 수 계산
mongosh
에서 다음 문서를 사용하여 sales
라는 이름의 샘플 컬렉션을 만듭니다.
db.sales.insertMany([ { "_id" : 1, "item" : "abc", "price" : Decimal128("10"), "quantity" : Int32("2"), "date" : ISODate("2014-03-01T08:00:00Z") }, { "_id" : 2, "item" : "jkl", "price" : Decimal128("20"), "quantity" : Int32("1"), "date" : ISODate("2014-03-01T09:00:00Z") }, { "_id" : 3, "item" : "xyz", "price" : Decimal128("5"), "quantity" : Int32( "10"), "date" : ISODate("2014-03-15T09:00:00Z") }, { "_id" : 4, "item" : "xyz", "price" : Decimal128("5"), "quantity" : Int32("20") , "date" : ISODate("2014-04-04T11:21:39.736Z") }, { "_id" : 5, "item" : "abc", "price" : Decimal128("10"), "quantity" : Int32("10") , "date" : ISODate("2014-04-04T21:23:13.331Z") }, { "_id" : 6, "item" : "def", "price" : Decimal128("7.5"), "quantity": Int32("5" ) , "date" : ISODate("2015-06-04T05:08:13Z") }, { "_id" : 7, "item" : "def", "price" : Decimal128("7.5"), "quantity": Int32("10") , "date" : ISODate("2015-09-10T08:43:00Z") }, { "_id" : 8, "item" : "abc", "price" : Decimal128("10"), "quantity" : Int32("5" ) , "date" : ISODate("2016-02-06T20:20:13Z") }, ])
다음 집계 작업은 $group
단계를 사용하여 sales
컬렉션의 문서 수를 계산합니다.
db.sales.aggregate( [ { $group: { _id: null, count: { $count: { } } } } ] )
이 연산은 다음과 같은 결과를 반환합니다.
{ "_id" : null, "count" : 8 }
이 집계 작업은 다음 SQL 문과 동일합니다.
SELECT COUNT(*) AS count FROM sales
Retrieve Distinct Values
다음 집계 연산은 $group
단계를 사용하여 sales
컬렉션에서 고유 항목 값을 조회합니다.
db.sales.aggregate( [ { $group : { _id : "$item" } } ] )
이 연산은 다음과 같은 결과를 반환합니다.
{ "_id" : "abc" } { "_id" : "jkl" } { "_id" : "def" } { "_id" : "xyz" }
항목별 그룹화
다음 집계 작업은 item
필드를 기준으로 문서를 그룹화하여 품목당 총 판매 금액을 계산하고 총 판매 금액이 100 이상인 품목만 반환합니다.
db.sales.aggregate( [ // First Stage { $group : { _id : "$item", totalSaleAmount: { $sum: { $multiply: [ "$price", "$quantity" ] } } } }, // Second Stage { $match: { "totalSaleAmount": { $gte: 100 } } } ] )
- 첫 번째 단계:
$group
단계는 고유 항목 값을 검색하기 위해 문서를item
으로 그룹화합니다. 이 단계에서는 각 항목에totalSaleAmount
를 반환합니다.- 두 번째 단계:
$match
단계는 결과 문서를 필터링하여totalSaleAmount
이(가) 100보다 크거나 같은 항목만 반환합니다.
이 연산은 다음과 같은 결과를 반환합니다.
{ "_id" : "abc", "totalSaleAmount" : Decimal128("170") } { "_id" : "xyz", "totalSaleAmount" : Decimal128("150") } { "_id" : "def", "totalSaleAmount" : Decimal128("112.5") }
이 집계 작업은 다음 SQL 문과 동일합니다.
SELECT item, Sum(( price * quantity )) AS totalSaleAmount FROM sales GROUP BY item HAVING totalSaleAmount >= 100
개수, 합계 및 평균 계산
mongosh
에서 다음 문서를 사용하여 sales
라는 이름의 샘플 컬렉션을 만듭니다.
db.sales.insertMany([ { "_id" : 1, "item" : "abc", "price" : Decimal128("10"), "quantity" : Int32("2"), "date" : ISODate("2014-03-01T08:00:00Z") }, { "_id" : 2, "item" : "jkl", "price" : Decimal128("20"), "quantity" : Int32("1"), "date" : ISODate("2014-03-01T09:00:00Z") }, { "_id" : 3, "item" : "xyz", "price" : Decimal128("5"), "quantity" : Int32( "10"), "date" : ISODate("2014-03-15T09:00:00Z") }, { "_id" : 4, "item" : "xyz", "price" : Decimal128("5"), "quantity" : Int32("20") , "date" : ISODate("2014-04-04T11:21:39.736Z") }, { "_id" : 5, "item" : "abc", "price" : Decimal128("10"), "quantity" : Int32("10") , "date" : ISODate("2014-04-04T21:23:13.331Z") }, { "_id" : 6, "item" : "def", "price" : Decimal128("7.5"), "quantity": Int32("5" ) , "date" : ISODate("2015-06-04T05:08:13Z") }, { "_id" : 7, "item" : "def", "price" : Decimal128("7.5"), "quantity": Int32("10") , "date" : ISODate("2015-09-10T08:43:00Z") }, { "_id" : 8, "item" : "abc", "price" : Decimal128("10"), "quantity" : Int32("5" ) , "date" : ISODate("2016-02-06T20:20:13Z") }, ])
한 해의 날짜별로 그룹화하기
다음 파이프라인은 2014년 한 해 동안의 총 판매 금액, 평균 판매량 및 일일 판매 횟수를 계산합니다.
db.sales.aggregate([ // First Stage { $match : { "date": { $gte: new ISODate("2014-01-01"), $lt: new ISODate("2015-01-01") } } }, // Second Stage { $group : { _id : { $dateToString: { format: "%Y-%m-%d", date: "$date" } }, totalSaleAmount: { $sum: { $multiply: [ "$price", "$quantity" ] } }, averageQuantity: { $avg: "$quantity" }, count: { $sum: 1 } } }, // Third Stage { $sort : { totalSaleAmount: -1 } } ])
- 첫 번째 단계:
$match
단계는 2014년에 작성된 문서만 다음 단계로 전달하도록 문서를 필터링합니다.- 두 번째 단계:
$group
단계에서는 문서를 날짜별로 그룹화하고 각 그룹에 있는 문서의 총 판매 금액, 평균 수량 및 총 개수를 계산합니다.- 세 번째 단계:
$sort
단계는 각 그룹의 총 판매 금액을 기준으로 결과를 내림차순으로 정렬합니다.
이 연산은 다음과 같은 결과를 반환합니다.
{ "_id" : "2014-04-04", "totalSaleAmount" : Decimal128("200"), "averageQuantity" : 15, "count" : 2 } { "_id" : "2014-03-15", "totalSaleAmount" : Decimal128("50"), "averageQuantity" : 10, "count" : 1 } { "_id" : "2014-03-01", "totalSaleAmount" : Decimal128("40"), "averageQuantity" : 1.5, "count" : 2 }
이 집계 작업은 다음 SQL 문과 동일합니다.
SELECT date, Sum(( price * quantity )) AS totalSaleAmount, Avg(quantity) AS averageQuantity, Count(*) AS Count FROM sales WHERE date >= '01/01/2014' AND date < '01/01/2015' GROUP BY date ORDER BY totalSaleAmount DESC
그룹화 기준 null
다음 집계 작업은 null
의 그룹 _id
을(를) 지정하여 collection의 총 판매 금액, 평균 수량 및 모든 문서의 개수를 계산합니다.
db.sales.aggregate([ { $group : { _id : null, totalSaleAmount: { $sum: { $multiply: [ "$price", "$quantity" ] } }, averageQuantity: { $avg: "$quantity" }, count: { $sum: 1 } } } ])
이 연산은 다음과 같은 결과를 반환합니다.
{ "_id" : null, "totalSaleAmount" : Decimal128("452.5"), "averageQuantity" : 7.875, "count" : 8 }
이 집계 작업은 다음 SQL 문과 동일합니다.
SELECT Sum(price * quantity) AS totalSaleAmount, Avg(quantity) AS averageQuantity, Count(*) AS Count FROM sales
Pivot Data
mongosh
에서 다음 문서를 사용하여 books
라는 이름의 샘플 컬렉션을 만듭니다.
db.books.insertMany([ { "_id" : 8751, "title" : "The Banquet", "author" : "Dante", "copies" : 2 }, { "_id" : 8752, "title" : "Divine Comedy", "author" : "Dante", "copies" : 1 }, { "_id" : 8645, "title" : "Eclogues", "author" : "Dante", "copies" : 2 }, { "_id" : 7000, "title" : "The Odyssey", "author" : "Homer", "copies" : 10 }, { "_id" : 7020, "title" : "Iliad", "author" : "Homer", "copies" : 10 } ])
별로 그룹화 title
author
다음 집계 작업은 books
collection의 데이터를 피봇하여 작성자별로 제목을 그룹화합니다.
db.books.aggregate([ { $group : { _id : "$author", books: { $push: "$title" } } } ])
이 작업은 다음 문서를 반환합니다.
{ "_id" : "Homer", "books" : [ "The Odyssey", "Iliad" ] } { "_id" : "Dante", "books" : [ "The Banquet", "Divine Comedy", "Eclogues" ] }
문서 그룹화 기준 author
다음 집계 작업은 author
를 기준으로 문서를 그룹화합니다.
db.books.aggregate([ // First Stage { $group : { _id : "$author", books: { $push: "$$ROOT" } } }, // Second Stage { $addFields: { totalCopies : { $sum: "$books.copies" } } } ])
- 첫 번째 단계:
$group
은$$ROOT
시스템 변수를 사용하여 전체 문서를 작성자별로 그룹화합니다. 이 단계에서는 다음 문서를 다음 단계로 전달합니다.{ "_id" : "Homer", "books" : [ { "_id" : 7000, "title" : "The Odyssey", "author" : "Homer", "copies" : 10 }, { "_id" : 7020, "title" : "Iliad", "author" : "Homer", "copies" : 10 } ] }, { "_id" : "Dante", "books" : [ { "_id" : 8751, "title" : "The Banquet", "author" : "Dante", "copies" : 2 }, { "_id" : 8752, "title" : "Divine Comedy", "author" : "Dante", "copies" : 1 }, { "_id" : 8645, "title" : "Eclogues", "author" : "Dante", "copies" : 2 } ] } - 두 번째 단계:
$addFields
은(는) 각 저자의 총 책 부수를 포함하는 필드를 출력에 추가합니다.참고
결과 문서는 BSON 문서 크기 제한인 16메가바이트를 초과하지 않아야 합니다.
이 작업은 다음 문서를 반환합니다.
{ "_id" : "Homer", "books" : [ { "_id" : 7000, "title" : "The Odyssey", "author" : "Homer", "copies" : 10 }, { "_id" : 7020, "title" : "Iliad", "author" : "Homer", "copies" : 10 } ], "totalCopies" : 20 } { "_id" : "Dante", "books" : [ { "_id" : 8751, "title" : "The Banquet", "author" : "Dante", "copies" : 2 }, { "_id" : 8752, "title" : "Divine Comedy", "author" : "Dante", "copies" : 1 }, { "_id" : 8645, "title" : "Eclogues", "author" : "Dante", "copies" : 2 } ], "totalCopies" : 5 }
추가 리소스
우편번호 데이터 세트를 사용한 집계 튜토리얼에서는 $group
연산자에 관한 광범위한 일반 사용 사례 예시를 설명합니다.