본문 바로가기

DB/MongoDB

[MongoDB] 13. 집계명령어 - aggregate, $project, $match, $group

728x90
반응형

MySQL의 집단 함수들

GROUP BY - COUNT(), MAX(), MIN(), SUM(), AVG()

여러개의 row를 그룹화(group by)하여 숫자를 세거나 평균을 구하는 등의 기능을 하는 함수이다.

집계 명령어의 필요성

  • 기존의 find로는 원하는 데이터를 가공하는데 한계가 있다.
  • 빅데이터를 다루려면 새로운 데이터 가공 방식이 필요하다.
  • MongoDB의 aggregation을 사용하여 grouping, fitering 등 다양한 연산을 적용할 수 있다.

MongoDB의 집계 방법론

데이터베이스의 모든 정보를 불러와 어플리케이션에서 집계하는 방법이다.

1) 맵 - 리듀스

  • Javascript 함수를 정의하여 그 함수를 호출하여 집계한다.
  • map 함수와 reduce 함수를 정의하여 mapReduce() 함수를 이용해 사용한다.
var map_func = function(){ // 고객별, 구매 금액 추출
	emit(this.cust_id,this.price);
}
var reduce_func = function(key,value){ // 주문 가격을 더하는 처리
	return Array.sum(value);
}
db.order.mapReduce(
	//map
    map_func,
    //reduce
    reduce_func,
    //option
    {out:"order_total"}
)
db.order_total.find()
  • Javascript 엔진과 데이터 교환을 위해 램 사용량이 많아진다.
  • Javascript 함수를 정의해 사용하므로 자유도가 높다.

2) 집계 파이프라인

  • Pipeline : 선행하는 데이터 처리 단계의 출력이 다음 단계의 입력으로 이어지는 구조이다.
    • 출력 -> 입력 출력 -> 입력 출력
  • 집계 명령어 사용 - 데이터를 미리 처리해서 작게 만들어 어플리케이션으로 전달 - 효율적이다.
  • MongoDB 내부에서 집계처리를 하므로 램 용량이 많이 필요 하지 않다.
  • MongoDB 내부에서 작동하기 때문에 정해진 연산자만 사용가능 - 자유도가 낮다.
  • Storage engine과 가장 가까운 곳에서 연산을 하므로 가장 효율적이다.
  • 맵-리듀스와 비교해 봤을 때 합리적인 처리 방법이다.

MongoDB 아키텍처

MongoDB 집계방법론 비교

  어플리케이션 맵-리듀스 집계 파이프라인
자유도 좋다 좋다 나쁘다
처리속도 가장 나쁘다 보통 가장 좋다
램 사용량 매우 높음 높음 낮음
처리 위치 어플리케이션 내부 자바스크립트 엔진 MongoDB 내부

MongoDB에서 집계 파이프라인

  • SQL에서의 집계 처리 예시 - 한 문장으로 해결
SELECT dept_id, AVG(salary) from employee group by (dept_id)
  • MongoDB에서는 이렇게 하나의 단계(명령어)로 처리하지 않는다.
  • 최종 결과를 만들기 까지의 연산을 여러 개의 "스테이지(  {   }  )"로 나누어 연산한다.

스테이지

  • 각 스테이지 단계는 특정 형태의 도큐먼트를 입력 받고 "도큐먼트 스트림"을 출력한다.
  • 집계 파이프라인의 개별 단계는 데이터 처리 단위이다.
  • 한 번에 하나씩 도큐먼트 스트림을 가져와 처리하고, 출력 스트림을 생성한다.
  • 각 단계는 knobs 또는 tunable(조정할 수 있는) 셋을 제공한다.
    • tunable : 필드 수정, 산술 연산, 도큐먼트 재구성 등의 연산자의 형태이다.
  • 하나의 단계가 여러 번 반복해서 포함될 수 있다.

스테이지 종류

스테이지 내용
$project 출력 도큐먼트 상에 배치할 필드 지정 (SELECT)
$match 처리될 도큐먼트를 선택, find와 비슷한 역할(HAVING)
$limit 다음 단계에 전달될 도큐먼트 수를 제한
$skip 지정된 수의 도큐먼트를 건너뜀
$unwind 배열을 확장하여 각 배열 항목에 대해 하나의 출력 도큐먼트를 생성
$group 지정된 키로 도큐먼트를 그룹화 함
$sort 도큐먼트를 정렬, 1: 오름차순 ,-1: 내림차순
$geoNear 지리 공간위치 근처의 도큐먼트 선택
$out 파이프라인의 결과를 컬렉션에 씀
$redact 특정 데이터에 대한 접근을 제어

스테이지 기술 순서

  1. $project -> $match -> $group -> $sort -> $skip -> $limit -> $unwind -> $out
    • $project 먼저 수행 - 필요한 열(필드)을 먼저 추출하고 다음 스테이지 진행.
  2. $match -> $project -> $sort -> $skip -> $limit
    • $match 먼저 수행 - 필요한 행(도큐먼트)을 먼저 추출하고 다음 스테이지 진행.

집계 명령어 aggregate() 형식

db.컬렉션.aggregate([
    {
    	$match : {  }//스테이지1
    },
    {
    	$project : {  }//스테이지2
    },
    {
    	//스테이지..
    }
 ]) // end of aggregate

스테이지 연습

$project

//db.컬렉션.aggregate([{$project:{ }}])
db.rating.aggregate([{$project:{_id:0,rating:1,hello:"new field"}}])
// _id는 출력하지 않음
// rating은 출력
// hello 라는 필드 새로 만들어 모든 도큐먼트에 적용 - 값은 new field

$match

//db.컬렉션.aggregate([ {$match:{ }} ])
db.rating.aggregate([{$match:{rating:{$gte:4}}}])
// rating이 4이상인 도큐먼트만 찾음

$limit

 // db.컬렉션.aggregate([{$limit:개수}])
 db.rating.aggregate([{$limit:5}])
 // 처음부터 5개의 도큐먼트만 다음 스테이지로 전달

$skip

 //db.컬렉션.aggregate([ { $skip: 개수 }])
 db.rating.aggregate([ { $skip: 3 }])
 // 처음부터 도큐먼트 3개를 건너뛰고 나머지 도큐먼트를 전달함

$unwind

//db.컬렉션.aggregate([{$unwind:"$배열 필드 명"}])
db.tmp.aggregate([{$unwind:"$sizes"}])
// sizes 필드의 요소들을 쪼개어 새로운 도큐먼트 생성

$sort

// db.컬렉션.aggregate([ { $sort: {정렬 할 기준 필드:1 or -1} }])
db.rating.aggregate([ { $sort: {rating:1} }])
// rating을 기준으로 오름차순 정렬
db.rating.aggregate([ { $sort: {_id:-1} }])
// _id를 기준으로 내림차순 정렬

$out

db.컬렉션.aggregate([ { 스테이지},{$out:"저장할 컬렉션 이름"}])
db.rating.aggregate([ { $group: {_id:"$rating", count_rating:{$sum:1}}},{$out:"re"}])
// 앞의 스테이지의 결과를 "re"라는 컬렉션으로 저장

$group

$group과 같이 사용되는 연산자들

연산자 분류 연산자 설명
산술연산 $sum 각 문서에서 해당 필드의 값을 합함(count는 {sum:1}로 구할 수 있음)
$avg 각 문서에서 해당 필드의 평균을 구함
극한 연산 $max 입력값 중 최대값 반환
$min 입력값 중 최소값 반환
$first 그룹의 첫번쨰 값 반환(보통 sort 후 사용)
$last 그룹의 마지막 값 반환(보통 sort 후 사용)
배열 연산 $addToSet 해당 값이 없는 경우, 배열에 추가, 순서 보장하지 않음
$push 차례대로 배열에 추가

$group을 사용할 때는 항상 _id 필드의 값이 있어야 한다.

_id 필드의 값 == 기준이되는 필드(없는 경우 == 전체 도큐먼트 대상 == null)

ex. _id:"$rating", _id:null

//db.컬렉션.aggregate([ { $group: {_id:"그룹을 나눌 필드",연산자들}}])
db.rating.aggregate([ { $group: {_id:"$rating", count_rating:{$sum:1}}}])
// rating 필드의 값 별로 그룹을 만든다.
// count_rating이라는 필드를 새로 만들어 값을 1로 하고 모든 도큐먼트에 적용
// $sum으로 count_rating 필드의 값들을 합친 값을 그룹별 count_rating 필드의 값으로 함.
// == 그룹별 개수 세기

 스테이지의 사용 방법

각 스테이지는 작성 순서대로 하나하나 동작한다. (순차 실행)

  • $project 스테이지를 단독 사용한 경우
    • $project에서 선택한 필드들은 다음 스테이지의 입력이된다.
  • $match 스테이지를 단독 사용한 경우
    • $match 조건을 만족하는 도큐먼트의 모든 필드가 출력된다.
  • $group 스테이지를 단독 사용한 경우
    • 그룹핑 기준을 설정하는 기능 뿐 아니라, 쿼리의 결과에 포함될 필드를 제어하는 기능도 수행한다.
728x90
반응형