MongoDB
→ 집계 처리. 그 중 Aggregate에 대하여

Aggregate

기본적인 스테이지 종류

  • $project : 어떤 해당필드의 값을 어떻게 표현할지 결정하는 스테이지

    db.rating.aggregate([
      {
        $project : {_id:0, rating: 1, hello: "new field"} <- hello는 새로운 필드
      }
    ])
    ↓↓↓ 결과 ↓↓↓
    {"rating" : 1, "hello" : "new field"}
    {"rating" : 2, "hello" : "new field"}
    {"rating" : 3, "hello" : "new field"}
    {"rating" : 3, "hello" : "new field"}
    {"rating" : 4, "hello" : "new field"}
    {"rating" : 4, "hello" : "new field"}
    {"rating" : 5, "hello" : "new field"}
    {"rating" : 5, "hello" : "new field"}
    {"rating" : 5, "hello" : "new field"}
    {"rating" : 5, "hello" : "new field"}
      
    // 곱셈도 가능
    db.rating.aggregate([
      {
        $project : {
          _id : 0,
          multiply : {$multiply: ["$_id","$user_id"]}
        }
      }
    ])
    ↓↓↓ 결과 ↓↓↓
    {"multiply" : 2}
    {"multiply" : 6}
    {"multiply" : 12}
    
  • $group : _id 값에 그룹화의 기준이 되는 값 설정 필요

    db.rating.aggregate([
      {
        $group: {_id : "$rating", count: {$sum : 1}}
      }
    ])
    
  • $match : 앞서 배운 query 부분과 동일한 역할

    db.rating.aggreagate([
      {
        $match : {rating : {$gte : 4}}
      },
      {
        $group : {_id : "$rating", user_ids : {$push : "$user_id"}}
      }
    ])
    ↓↓↓ 결과 ↓↓↓
    {"_id" : 5, "user_ids" : [11,12,10,9]}
    {"_id" : 4, "user_ids" : [8, 5]}
    
  • $unwind : 하나의 도큐먼트에 들어있는 배열 요소들을 각각의 도큐먼트로 만드는 기능 (SQL 식으로 설명하자면 배열에 들어있는 값을 각각의 row로 분리하는 기능)

     위의 match 샘플 코드에 unwind만 적용했을  결과값에 주목
    db.rating.aggreagate([
      {
        $match : {rating : {$gte : 4}}
      },
      {
        $group : {_id : "$rating", user_ids : {$push : "$user_id"}}
      },
      {
        $unwind : "$user_ids"
      }
    ])
    ↓↓↓ 결과 ↓↓↓
    {"_id" 5: , "user_ids" : 11}
    {"_id" 5: , "user_ids" : 12}
    {"_id" 5: , "user_ids" : 10}
    {"_id" 5: , "user_ids" : 9}
    {"_id" 4: , "user_ids" : 8}
    {"_id" 4: , "user_ids" : 5}
    
  • $out : 입력받은 도큐먼트를 out필드의 값으로 지정한 이름의 컬렉션에 저장하는 역할

    db.rating.aggreagate([
      {
        $group : {_id : "$rating", user_ids : {$push : "$user_id"}}
      },
      {
        $out : "user_ids_by_rating"
      }
    ])
    

    이 경우, 결과값을 “user_ids_by_rating”라는 컬렉션에 저장.
    맵-리듀스에서는 컬렉션에 저장할 때 덮어쓰는 방식 외에도 기존의 컬렉션에 도큐먼트를 추가하는 방식이 있었지만 집계 파이프라인은 해당 기능이 없다는 점 참고.


입력도큐먼트 제어 스테이지

  • $limit : { $limit : <number> }
  • $skip : 입력받은 도큐먼트(결과값 도큐먼트) 중 지정된 숫자만큼 건너뛰고 출력. { $skip : <number> }
  • $sort : { $limit : <number> }
    ( ※ 처리하는 도큐먼트의 총 크기가 100MB를 넘을 수 없음. 100MB 이상의 용량을 가진 도큐먼트들을 정렬하고자 할 때에는 {allowDiskUse : true} 옵션 추가필요)

고급스테이지 (164p)

  • $bucket : 범위에 따라 그룹화하는 기능
    db.rating.aggregate([
      {
        $bucket : {
          groupby : "$rating",
          boundaries : [2, 3, 5],
          default : "Others",
          output : {
            count : {$sum : 1},
            user_ids : {$push : "$user_id"}
          }
        }
      }
    ])
    {"_id" : 2, "count" : 1, "user_ids" : [3]}
    {"_id" : 3, "count" : 4, "user_ids" : [4, 1, 5, 8]}
    {"_id" : "Others", "count" : 1, "user_ids" : [2, 9, 10 ,11, 12]}
    
    • groupby 파라미터 > “rating”필드를 기준으로 그룹화
    • boundaries 파라미터 > 범위를 지정
      위의 예시는 “2이상 3미만”, “3이상 5미만” 두 구간으로 나누겠다고 설정하였고,
      2이상 3미만 범위 내 도큐먼트들은 “_id” : 2 에 그룹화된 결과.
    • default 파라미터 > (선택) 정한 구간 외 도큐먼트를 모을 필드명 지정
    • output 파라미터 > (선택) 그룹화를 하고 난 후 출력 결과를 어떻게 표시할지 결정
  • $addFields : 필드를 추가하는 기능
    db.rating.aggregate([
      {
        $addFields : { hello : "world" }
      },
      {
        $project : { hello : 1}
      },
      {
        $limit : 3
      }
    ])
    {"_id" : 1, "hello" : "world"}
    {"_id" : 2, "hello" : "world"}
    {"_id" : 3, "hello" : "world"}
    

    addFields를 통해 추가한 필드가 이미 존재하고있다면 덮어쓰기가 된다.
    이 스테이지와 점 연산자를 이용하면 임베디드 도큐먼트에도 필드 추가 가능

    db.rating.aggregate([
      {
        $addFields : { "hi.hello" : "world" }
      },
      {
        $project : { hi : {hello : 1} }
      },
      {
        $limit : 3
      }
    ])
    {"_id" : 1, "hi" : {"hello" : "world"} }
    {"_id" : 2, "hi" : {"hello" : "world"} }
    {"_id" : 3, "hi" : {"hello" : "world"} }
    
  • $facet : SQL문에서 union all 과 비슷한 맥락
    공식 문서 링크 : https://www.mongodb.com/docs/manual/reference/operator/aggregation/facet/
  • $lookup : SQL문에서 join과 비슷한 맥락
    →공식문서를 보면 다양한 사용법이 있기 때문에, 자세한 내용은 다른포스팅에 따로 정리해볼 예정
  • $replaceRoot : 도큐먼트의 모양을 바꾸는 스테이지. 도큐먼트의 내용을 newRoot 파라미터에 설정한 값으로 대체할 수 있으며, 서브 도큐먼트를 도큐먼트 전체의 내용으로 바꾸려고 할 때 특히 더 유용하다.
  • $sortByCount : 기존 $group + $sort 스테이지를 통해 표현했던 의미를 좀 더 간편하게 처리할 수 있는 스테이지.
    // $sortByCount 방식
    { $sortByCount : <expression> }
      
    // 기존 방식
    { $group : {_id : <expression>, count : { $sum : 1 } } },
    { $sort : { count : 1 } }
       
    위의  방식은 동일한 결과를 출력
      
     실제 사용 예시
    db.rating.aggregate([
      {
        $sortByCount : "$rating"
      }
    ])
     결과
    {"_id" : 5, "count" : 4}
    {"_id" : 3, "count" : 2}
    {"_id" : 4, "count" : 2}
    {"_id" : 2, "count" : 1}
    {"_id" : 1, "count" : 1}
    
  • $unset : 지정한 필드를 없애고 싶을 때 사용. 특정 조건을 만족하는 도큐먼트에만 적용할 수도 있고 모든 도큐먼트에 적용할 수도 있음

  • $let : var라는 표현식을 사용하여 값을 담은 변수를 만들고 in을 사용하여 최종 return

aggregate 집계 쿼리를 구현 중에 ‘다른 컬렉션’에 들어있는 특정 필드의 모든 값을 일단 배열형태로 가져와야하는 경우가 있었다. 이때 사용할 수 있는 스테이지로는 $lookup, 그리고 $group 스테이지 + $push 연산자. 이 중 $group 스테이지를 사용할때 그룹핑할 기준이 담기는 _id 필드에 공백(““)만 설정하여 배열에 담고싶은 값이 담긴 필드를 $push로 지정하여 처리하였다.

ex)

{
  "$group" : {
    "_id" : "",
    "testCreateField" : {
      "$push" : "$usage_cost"
    }
}

집계 파이프라인 연산자

  • 문자열연산자

    • concat

    • strcasecmp

    • substr

    • substrCP

    • strLenCP

    • toLower

    • toUpper

  • 산술연산자
    • add
    • divide
    • mod
    • multiply
    • subtract
  • 날짜, 시간 연산자
    • toDate
    • dayOfYear
    • dayOfMonth
    • dayOfWeek
    • year
    • month
    • week
    • hour
    • minute
    • second
    • millisecond
  • 논리연산자
    • cond
    • ifNull
    • not
    • or
    • and
    • nor
  • 비교연산자
    • cmp
    • eq
    • ne
    • gt
    • gte
    • lt
    • lte
    • in
    • nin
  • 배열연산자
    • setEquals
    • setIsSubset
    • setDifference
    • setUnion
    • arrayElemAt
  • 쿼리속 집계파이프라인 연산자
    • expr

Leave a comment