Notepad
Published 2021. 10. 28. 13:25
MongoDB - 집계1 dev/DB

집계

  • MongoDB는 집계를 수행하는 세 가지 방법을 제공
    • 집계 파이프라인
    • Map-reduce(5.0 버전부터 사용 X)
    • 단일 목적 집계 연산

1. 집계 프레임워크 개요

1.1 집계 프레임워크 개요

  • 파이프라인의 각 단계에서의 출력이 다음 단계로의 입력으로 제공되는 집계 파이프라인을 정의
  • 집계 파이프라인 작업 요약
    • $project: 출력 도큐먼트상에 배치할 피드를 지정
    • $match: 처리될 도큐먼트를 선택하는 것(find() 와 비슷한 역할을 수행)
    • $limit: 다음 단계에 전달될 도큐먼트의 수를 제한
    • $skip: 지정된 수의 도큐먼트를 건너뜀
    • $unwind: 배열을 확장하여 각 배열 항목에 대해 하나의 출력 도큐먼트를 생성
    • $group: 지정된 키로 도큐먼트를 그룹화
    • $sort: 도큐먼트를 정렬
    • $geoNear: 지리 공간위치 근처의 도큐먼트를 선택
    • $out: 파이프라인의 결과(출력)를 컬렉션에 추가
    • $redact: 특정 데이터에 대한 접근을 제어
  • 집계 파이프라인 예
db.products.aggregate([ // 1. 전체 products 전달
    {$match: ...},      // 2. 처리할 도큐먼트를 선택 및 결과 전달
    {$group: ...},      // 3. 특정 키 기준으로 도큐먼트 그룹화 및 결과 전달
    {$sort: ...}        // 4. 도큐먼트를 정렬 및 최종 결과 반환
 ])
  • SQL 과 집계 프레임워크 비교
SQL 명령어 집계 프레임워크 연산자
SELECT $project / $group 함수: $sum, $min, $avg 등
FROM db.collectionName.aggregate(...)
JOIN $unwind
WHERE $match
GROUP BY $group
HAVING $match

2. 전자상거래 집계 예제

  • 예시 전자상거래 데이터모델
    • products: 제품
    • reviews: 리뷰
    • categories: 카테고리
    • orders: 주문

2.1. 상품, 카테고리, 리뷰

리뷰 수 조회 예시

// 쿼리
product = db.products.findOne( {'slug': 'wheelbarrow-9092'});
reviews_count = db.reviews.count( {'product_id': product['_id']} );

// 집계
db.reviews.aggregate([
    {
        $group : { _id:'$product_id',   // product_id 로 그룹화
                    count:{$sum:1} }    // 각 제품에 대한 리뷰 수 카운트
    }
]);

하나의 상품에 대한 리뷰 수 조회 예시

product = db.products.findOne({'slug': 'wheelbarrow-9092'});
// 쿼리
db.reviews.count({'product_id': product['_id']});

// 집계
ratingSummary = db.reviews.aggregate([
    {$match : { product_id: product['_id']} },  // 하나의 상품 _id 선택
    {$group : { _id:'$product_id',         // 결과의 첫번째 도큐먼트 반환
                count:{$sum:1} }}
]).next();

평균 리뷰 계산

product = db.products.findOne({'slug': 'wheelbarrow-9092'});

ratingSummary = db.reviews.aggregate([
    {$match : {'product_id': product['_id']}},
    {$group : { _id:'$product_id',
                average:{$avg:'$rating'},   // 제품 평균 평점 계산
                count: {$sum:1}}}
]).next();

등급별 리뷰 계산

// 집계
countsByRating = db.reviews.aggregate([
    {$match : {'product_id': product['_id']}},  // 제품 선택
    {$group : { _id:'$rating',                  // rating 값별로 그룹화
                count:{$sum:1}}}                // 각 등급별 리뷰 수 계산
]).toArray();   // 결과 커서 배열 반환

// SQL
SELECT RATING, COUNT(*) AS COUNT
  FROM REVIEWS
 WHERE PRODUCT_ID = '4c4b1476238d3b4dd5003981'
 GROUP BY RATING

컬렉션 조인

  • 각각의 주요 카테고리의 제품 수 계산
  • 제품과 카테고리는 1:1 관계
  • 예시
// 집계
db.products.aggregate([
    {$group : { _id:'$main_cat_id',
                count:{$sum:1}}}
]);

// 결과
{ "_id" : ObjectId("6a5b1476238d3b4dd5000048"), "count" : 2 }
  • 의사 조인(pseudo-join)
    • 조회 및 가공 후 요약 컬렉션에 저장하여 조인된 결과와 같이 생성
    • 2.6부터 기능 지원
    • 시간이 오래 걸리 수 있음
db.mainCategorySummary.remove({});  // mainCategorySummary 컬렉션에서 기존 도큐먼트 제거

db.products.aggregate([
    {$group : { _id:'$main_cat_id',
                count:{$sum:1}}}
]).forEach(function(doc){
    var category = db.categories.findOne({_id:doc._id});  // 결과에 대한 카테고리 조회
    if (category !== null) {                // 카테고리가 실제로 존재한다는 보장 X
        doc.category_name = category.name;
    }
    else {
        doc.category_name = 'not found';
    }
    db.mainCategorySummary.insert(doc);     // 결합된 결과를 요약 컬렉션에 삽입
})

$out과 $project

  • $out
db.mainCategorySummary.insert(doc);     // 결합된 결과를 요약 컬렉션에 삽입

// $out
db.products.aggregate([
    {$group : { _id:'$main_cat_id',
                count:{$sum:1}}},
    {$out : 'mainCategorySummary'}  // 파이프라인 결과를 
])                                  // mainCategorySummary 컬렉션에 저장
  • $product
    • 파이프라인의 다음 단계로 절달할 필드를 필터링
db.products.aggregate([
    ... {$project : {category_ids:1}}   // category_ids
    ... ]);

// 결과 category_ids로 전달 필드 제한
{ "_id" : ObjectId("4c4b1476238d3b4dd5003981"),
"category_ids" : [  ObjectId("6a5b1476238d3b4dd5000048"),
                    ObjectId("6a5b1476238d3b4dd5000049") ] }
{ "_id" : ObjectId("4c4b1476238d3b4dd5003982"),
"category_ids" : [  ObjectId("6a5b1476238d3b4dd5000048"),
                    ObjectId("6a5b1476238d3b4dd5000049") ] }

$unwind 로 더 빨라진 조인

  • 배열을 확장하여 모든 입력 도큐먼트 배열 항목에 대해 하나의 출력 도큐먼트를 생성
  • 하위 도큐먼트가 나타날 떄마다 도큐먼트를 조인 가능
db.products.aggregate([
    {$project : {category_ids:1}},   // category_ids 만 다음으로 전달
    {$unwind : '$category_ids'},     // category_ids 의 
    {$group : { _id:'$category_ids', // 모든 배열 항목에 대한 출력 도큐먼트 생성
                count:{$sum:1}}},
    {$out : 'countsByCategory'}      // 집계 결과를 컬렉션에 저장
]);

2.2. 사용자와 주문

연도별, 원별 판매 요약

db.orders.aggregate([
    {$match: {purchase_data: {$gte: new Date(2010, 0, 1)}}}, // 날짜 이후 데이터
    {$group: {
        _id: {year : {$year :'$purchase_data'}, // year, month 복합키
              month: {$month :'$purchase_data'}},
        count: {$sum:1},                        // 주문 수
        total: {$sum:'$sub_total'}}},           // 주문 총 합계
    {$sort: {_id:-1}}
]);

맨해튼 최고의 고객 찾기

  • 북부 맥해튼에서 가장 높은 지출을 보이는 소비자 찾는 쿼리 예제
  • 쿼리 구성
    • $match: Upper Manhattan으로 선적된 주문 찾기
    • $group: 각 고객별 주문 금액을 합산
    • $match: 주문 총액이 $100보다 큰 고객 선택
    • $sort: 고객 주문 합계 금액으로 결과 내림차순 정렬
// 각 단계로 전달할 매개변수 정의
upperManhattanOrders = {'shipping_address.zip': {$gte: 10019, $lt: 10040}};
sumByUserId = {_id: '$user_id',
                total: {$sum:'$sub_total'}, };
orderTotalLarge = {total: {$gt:10000}};
sortTotalDesc = {total: -1};

// 집계
db.orders.aggregate([
    {$match: upperManhattanOrders},
    {$group: sumByUserId},
    {$match: orderTotalLarge},
    {$sort: sortTotalDesc}
]);

// 테스트
db.orders.aggregate([
    {$group: sumByUserId},
    {$match: orderTotalLarge},
    {$limit: 10}
]);
// 테스트 - 주문수 유지 결정
sumByUserId = {_id: '$user_id',
    total: {$sum:'$sub_total'},
    count: {$sum: 1}};

// $out으로 저장
db.orders.aggregate([
    {$match: upperManhattanOrders},
    {$group: sumByUserId},
    {$match: orderTotalLarge},
    {$sort: sortTotalDesc},
    {$out: 'targetedCustomers'}
]);    

3. 집계 파이프라인 연산자

  • 집계 프레임워크는 10개 연산자 지원
    • $project: 처리할 도큐먼트 필드 지정
    • $group: 지정된 키로 도큐먼트를 그룹화
      • $addToSet: 그룹에 고유한 값의 배열 생성(중복 값 X)
      • $first: 그룹의 첫번째 값($sort 선행 필요)
      • $last: 그룹의 마지막 값($sort 선행 필요)
      • $max: 그룹의 필드 최대값
      • $min: 그룹의 필드 최소값
      • $avg: 필드의 평균값
      • $push: 그룹의 모든 값의 배열을 반환(중복 값 포함)
      • $sum: 그룹의 모든 값의 합계
    • $match: 처리될 도큐먼트를 선택하는 것(find() 와 비슷한 역할을 수행)
    • $limit: 다음 단계에 전달될 도큐먼트의 수를 제한
    • $skip: 지정된 수의 도큐먼트를 건너뜀
    • $unwind: 배열을 확장하여 각 배열 항목에 대해 하나의 출력 도큐먼트를 생성
    • $sort: 도큐먼트를 정렬
    • $geoNear: 지리 공간위치 근처의 도큐먼트를 선택
    • $out: 파이프라인의 결과(출력)를 컬렉션에 추가
    • $redact: 특정 데이터에 대한 접근을 제어

'dev > DB' 카테고리의 다른 글

MongoDB - 업데이트, 원자적 연산, 삭제  (0) 2021.11.11
MongoDB - 집계2  (0) 2021.11.04
MongoDB - Query Selectors(1)  (0) 2021.10.22
[MongoDB] 도큐먼트 지향 데이터  (0) 2021.10.15
자바스크립트 셸을 통한 MongoDB  (0) 2021.09.30
profile

Notepad

@Apio

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!