$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
및 누산기 연산자는 유효한 expression
을 모두 허용할 수 있습니다. 표현식에 대한 자세한 내용은 표현식 연산자를 참조하세요.
고려 사항
성능
$group
데이터를 처리하기 전에 파이프라인이 블로킹 단계에서 모든 입력 데이터가 조회될 때까지 대기하도록 하는 블로킹 단계입니다. 블로킹 단계는 여러 단계가 있는 파이프라인에 대한 병렬 처리를 줄이기 때문에 성능을 저하시킬 수 있습니다. 블로킹 단계는 대규모 데이터 세트에 대해 상당한 양의 메모리를 사용할 수도 있습니다.
누산기 연산자
<accumulator>
연산자는 다음 누산기 연산자 중 하나이어야 합니다.
버전 5.0에서 변경됨
이름 | 설명 |
---|---|
사용자 정의 누산기 함수의 결과를 반환합니다. | |
각 그룹에 대한 고유한 표현식 값의 배열을 반환합니다. 배열 요소의 순서가 정의되지 않았습니다. 버전 5.0에서 변경: | |
숫자 값의 평균을 반환합니다. 숫자가 아닌 값을 무시합니다. 버전 5.0에서 변경: | |
지정된 정렬 순서에 따라 그룹 내 하위 개 요소의 집계를 반환합니다. 버전 5.2에 추가되었습니다. 2} 및 단계에서 | |
그룹에 있는 문서 수를 반환합니다.
버전 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에서 변경: | |
지정된 정렬 순서에 따라 그룹 내 상위 개 요소의 집계를 반환합니다. 버전 5.2에 추가되었습니다. 2} 및 단계에서 |
$group
및 메모리 제한
$group
단계가 100MB의 RAM을 초과하면 MongoDB는 임시 파일에 데이터를 씁니다. 그러나 allowDiskUse 옵션이 false
로 설정되어 있으면 $group
은 오류를 반환합니다. 자세한 내용은 집계 파이프라인 제한을 참조하세요.
$group
성능 최적화
이 섹션에서는 $group
의 성능을 개선하기 위한 최적화에 대해 설명합니다. 수동으로 수행할 수 있는 최적화와 MongoDB가 내부적으로 수행할 수 있는 최적화가 있습니다.
각 그룹의 첫 번째 또는 마지막 문서 반환 최적화
파이프라인이 동일한 필드를 기준으로 sorts
(정렬) 및 groups
(그룹화)를 수행하고, $group
단계에서 $first
또는 $last
누산기 연산자만 사용하는 경우 정렬 순서와 일치하는 그룹화된 필드에 인덱스 를 추가하는 것이 좋습니다. 어떤 경우에는 $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" }
참고
을 사용하여 샤딩된 컬렉션 에서 고유 값을 조회 할 때 작업 결과 가 발생하면 결과에 $group
DISTINCT_SCAN
고아 문서가포함될 수 있습니다.
The only semantically correct 파이프라인 that is impacted is effectively a logical equivalent of a command, where there is distinct
a $group
stage at or near the beginning of the 파이프라인 and the $group
is not preceded by a stage $sort
.
예를 예시, 다음 형식의 $group
연산은 DISTINCT_SCAN
(이)가 될 수 있습니다.
{ $group : { _id : "$<field>" } }
고유 값을 검색하기 위한 동작에 대한 자세한 내용은 고유 명령 동작을 참조하세요.
작업 결과가 인지 DISTINCT_SCAN
확인하려면작업의 설명 결과를 확인하세요.
항목별 그룹화
다음 집계 작업은 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
연산자에 관한 광범위한 일반 사용 사례 예시를 설명합니다.