본문 바로가기

MongoDB

[MongoDB] 확장 검색 쿼리 - Aggregation 파이프라인 스테이지(Pipline Stage)와 표현식

Aggregation의 파이프 라인은 스테이지의 배열로 작성되며,

 

MongoDB 서버가 지원하는 스테이지는 다음과 같이 다양한 연산 과정들이 있다.

 

스테이지

설명

$project

입력으로 주어진 도큐먼트에서 필요한 필드만 선별해서 다음 스테이즈로 넘겨주는 작업을 처리한다.

물론 기존의 필드 이름을 변경하거나 필드를 제거하는 처리도 가능하다.

$addFields

입력으로 주어진 도큐먼트에 새로운 필드를 추가한다.

$replaceRoot

입력으로 주어진 도큐먼트에서 특정 필드를 최상위 도큐먼트로 만들고, 나머지는 버린다.

$match

컬렉션 또는 직전 스테이지에서 넘어온 도큐먼트에서 조건에 일치하는 도큐먼트만 다음 스테이지로 전달한다.

$redact

도큐먼트의 각 필드 또는 서브 도큐먼트의 포맷이 다양한 경우에 지정된 형태의 포맷과 일치하는 서브 도큐먼트 또는 필드만으로 도큐먼트를 재구성한다.

$limit

입력으로 주어진 도큐먼트에서 처음 몇 건의 도큐먼트만 다음 스테이지로 전달한다.

$skip

입력으로 주어진 도큐먼트에서 처음 몇 건의 도큐먼트는 버리고 나머지 도큐먼트만 다음 스테이지로 전달한다.

$out

처리의 결괄르 컬렉션으로 저장하거나 클라이언트로 직접 전달한다.

$unwind

입력 도큐먼트가 배열로 구성된 필드를 가지고 있으며 이를 여러 도큐먼트로 풀어서 다음 스테이지로 전달한다.  즉, 입력 도큐먼트가 10개의 엘리먼트를 가진 배열로 구성된 필드를 가진다면 $unwind 스테이지는 이를 10개의 도큐먼트로 만들어서 다음 스테이지로 전달한다.

$group

입력으로주어진 도큐먼트를 지정된 조건에 맞게 그룹핑해서 카운트나 합계또는 평균 등의 계산을 처리한다.

$sample

주어진 입력 도큐먼트 중에서 임의로 몇 개의 도큐먼트만 샘플링해서 다음 스테이지로 전달한다.

$sort

주어진 입력 도큐먼트 중에서 임의로 몇 개의 도큐먼트만 샘플링해서 다음 스테이지로 전달한다.

$count

주어진 입력 도큐먼의 개수를 세어서 다음 스테이지로 전달한다.

$geoNear

주어진 위치를 기준으로 위치 기반의 검색을 수행해서 일정 반경 이내의 결과만 다음 스테이지로 전달

$lookup

주어진 입력 도큐먼트와 다른 컬렉션( 동일 데이터베이스 내의 컬렉션 )과 레프트 아우터( LEFT OUTER  )조인을 실행해서 결과를 다음 스테이지로 전달한다.

$facet

하나의 스테이지로 다양한 차원의 그룹핑 작업을 수행한다.

$facet 스테이지는 $bucket과 $bucketAuto 그리고 $sortByCount 등의 서브 스테이지를 가진다.

$bucket

입력 도큐먼트를 여러 범위로 그룹핑한다.

$group 스테이지는 유니크한 모든 값에 대해서 그룹을 생성하지만, $bucket은사용자가 임의로 각 그룹의 범위를 설정할 수 있다.

$bucketAuto

$bucket 스테이지와 동일하지만, $bucketAuto는 사용자가 아닌 MongoDB 서버가 자동으로 그룹의 범위를 설정한다.

$sortByCount

주어진 도큐먼트의 필드를 기준으로 그룹핑해서 개수의 역순으로 정렬한 결과를 다음 스테이지로 전달한다.

$collStats

컬렉션의 상태 정보를 조회해서 다음 스테이지로 전달한다.

$indexStats

인덱스의 상태 정보를 조회해서 다음 스테이지로 전달한다.

 

각 스테이지르 조합해서 이용하면 매우 다양한 Aggregation 파이프라인을 작성할 수 있는데,

 

모든 케이스를 살펴보기는 어려울 것이다.

 

그래서 여기에서는 각 스테이지를 사용하는 간단한 예제를 살펴보겠다.

 

그리고 여기에서 언급하지 않은 스테이지에 대한 상세한 설명은 메뉴얼을 참조하도록 하자.

( https://docs.mongodb.com/manual/meta/aggregation-quick-reference/ )

 

 

아래에 소개하는 스테이지 중에서도 $match, $group, $project, $sort, $limit, $unwind, $out은

 

필수로 사용되는 경우가 많으니 한 번씩 예제를 직접 만들어서 작동 방식을 확이해볼 것을 권장한다.

 

 


 

$project 스테이지

 

$project 스테이지는 입력 도큐먼트에서 필요한 필드들을 선별해서 다음 스테이지로 넘겨주는 처리를 수행한다.

 

다음 스테이지로 전달하고자 하는 필드에 대해서 "1"또는 true를 설정하면된다.

 

// 입력 도큐먼트
mongo> db.books.insertOne(
  {
      _id : 1
    , title : "abc123"
    , isbn : "0001122223334"
    , author : { last : "zzz", first : "aaa" }
    , copies : 5
  }
);
{ "acknowledged" : true, "insertedId" : 1 }

// Aggregation 파이프라인 예제
mongo> db.books.aggregate( [ { $project : { title : 1, author : 1 } } ] );

// Aggregation 결과
{ "_id" : 1, "title" : "abc123", "author" : { "last" : "zzz", "first" : "aaa" } }

 

$project  스테이지는 단수히 필드를 필터링 하는 역할뿐만 아니라 기존의 필드들을 조합하거나

 

상수값으로 새로운 필드를 만들어서 다음 스테이지로 전달하는 처리도 할 수 있다.

 

다음 예제는 books 컬렉션의 isbn 필드에 저장된 문자열을 잘라서 publisher와 title 그리고

 

checkDigit 필드를 새로 만들고, isbn이라는 새로운 서브 도큐먼트를 추가했다.

 

그리고 price와 copies 필드의 곱으로 totalPrices라는 전체 금액 필드를 새로 추가했다.

 

mongo> db.books.insertOne(
  {
      _id : 1
    , title : "MongoDB Applied Design Patterns"
    , isbn : "9781449340049"
    , author : { last : "Copeland", first : "Rick" }
    , price : 19.68
    , copies : 5
  }
);
{ "acknowledged" : true, "insertedId" : 1 }

mongo> db.books.aggregate(
  [
    {
      $project : {
          title : 1
        , isbn : {
            publisher : { $substr : [ "$isbn", 5, 4 ] }
          , title : { $substr : [ "$isbn", 9, 3 ] }
          , checkDigit : { $substr : [ "$isbn", 12, 1 ] }
        }
        , lastName : "$author.last"
        , copiesSold : "$copies"
        , totalPrice : { $multiply : [ "$price", "$copies" ] }
      }
    }
  ]
);

{
    "_id" : 1
  , "title" : "MongoDB Applied Design Patterns"
  , "isbn" : {
      "publisher" : "4934"
    , "title" : "004"
    , "checkDigit" : "9"
  }
  , "lastName" : "Copeland"
  , "copiesSold" : 5
  , "totalPrice" : 98.4
}

 

또한 기존 필드의 값을 이용해서 새로운 서브 도큐먼트나 배열 필드를 생성할 수도 있다.

 

다음 예제는 도큐먼트에 저장된 위도 값과 경도 값을 이용해서 도큐먼트에 배열 필드를 새로 생성하고

 

다음 스테이지로 전달하도록 $project를 작성하는 방법을 보여주고있다.

 

mongo> db.example.insertOne( { "longitude" : 126.98955, "latitude" : 37.56511 } );
{ "acknowledged" : true, "insertedId" : ObjectId( "5faa53fc4da1c867a0091e7d" ) }

mongo> db.example.aggregate(
  [
    { $project : { location : [ "latitude", "$longitude" ] } }
  ]
);
{ "location" : [ "latitude", 126.98955 ] }

 

 


 

$match 스테이지

 

$match 스테이지는 입력 컬렉션이나 도큐먼트에서 조건에 일치하는 도큐먼트만 필터링해서

 

다음 스테이지로 전달하는 처리를 수행한다.

 

// 입력 도큐먼트
mongo> db.articles.insertMany(
  [
      { "_id" : ObjectId("512bc95fe835e68f199c8686"), "author" : "dave", "score" : 80, "views" : 100 }
    , { "_id" : ObjectId("55f5a192d4bede9ac365b257"), "author" : "ahn", "score" : 60, "views" : 1000 }
    , { "_id" : ObjectId("55f5a192d4bede9ac365b258"), "author" : "li", "score" : 55, "views" : 5000 }
  ]
);

// Aggregation 파이프라인 예제
mongo> db.articles.aggregate(
  [ { $match : { author : "dave" } } ]
);

// Aggregation 출력결과
{ "_id" : ObjectId("512bc95fe835e68f199c8686"), "author" : "dave", "score" : 80, "views" : 100 }

 

 

 


 

$unwind 스테이지

 

$unwind 스테이지는 하나의 도큐먼트에서 배열로 구성된 필드를 펼쳐서 여러 도큐먼트로 변환하는 처리를 수행한다.

 

그래서 $unwind 스테이지로 입력되는 도큐먼트 하나가 여러 도큐먼트로 변환돼서 출력되는 것이다.

 

// 입력 도큐먼트
mongo> db.inventory.insertOne(
  { "_id" : 1, "item" : "ABC1", "sizes" : [ "S", "M", "L" ] }
);

// Aggregation 파이프라인 예제
mongo> db.inventory.aggregate(
  [ { $unwind : "$sizes" } ]
);

// Aggregation 출력결과
{ "_id" : 1, "item" : "ABC1", "sizes" : "S" }
{ "_id" : 1, "item" : "ABC1", "sizes" : "M" }
{ "_id" : 1, "item" : "ABC1", "sizes" : "L" }

 

$unwind 스테이지는 path와 includeArrayIndex 옵션을 이용해서 배열의 인덱스를 출력 도큐먼트에 포함할 수도 있다.

// 입력 도큐먼트
mongo> db.inventory.insertOne(
  { "_id" : 1, "item" : "ABC1", "sizes" : [ "S", "M", "L" ] }
);

// Aggregation 파이프라인 예제
mongo> db.inventory.aggregate(
  [ { $unwind : { path : "$sizes", includeArrayIndex : "arrayIndex" } } ]
);

// Aggregation 출력결과
{ "_id" : 1, "item" : "ABC1", "sizes" : "S", "arrayIndex" : NumberLong(0) }
{ "_id" : 1, "item" : "ABC1", "sizes" : "M", "arrayIndex" : NumberLong(1) }
{ "_id" : 1, "item" : "ABC1", "sizes" : "L", "arrayIndex" : NumberLong(2) }

 


 

$group 스테이지

 

$group 스테이지는 SQL에서 자주 사용하던 GROUP BY와 같은 처리를 수행할 수 있는데,

 

MongoDB의 $group 스테이지에서는 다음과 같은 집계 연산이 가능합니다.

 

집계 연산자

설명

$sum

숫자 타입의 필드 합계를 계산한다.

만약 도큐먼트의 개수가 필요하다면 필드의 값 대신 1을 사용한다.

$avg

숫자 타입의 필드 평균을 계산한다.

$first

각 그룹의 첫 번째 값을 반환한다.

$last

각 그룹의 마지막 값을 반환한다.

$max

각 그룹의 최대값을 반환한다.

$min

각 그룹의 최소값을 반환한다.

$push

각 그룹에 속한 모든 도큐먼트의 필드 값을 배열로 반환한다.

$addToSet

각 그룹에 속한 모든 도큐먼트의 필드 값 중에서 유니크한 값을 배열로 반환한다.

$stdDevPop

각 그룹의 표준 편차를 반환한다.

$stdDevSamp

각 그룹의 표본 표준 편차( sample standard deviation )를 반환한다.

 

다음 예제는 판매 일자를 일별( 더 정확히는 년 / 월 / 일)로 그룹핑해서 총 판매 실적과 평균 판매 수량 그리고 판매 건수를 구하는 예제다.

 

판매 실적을 위해서 그룹별 아이템의 각격과 판매 수량을 곱한 값의 합( $sum )을 totalPrice 필드에 할당했다.

 

그리고 마지막으로 그룹별로 상수 값 1의 합( $sum )을 곘나해서 count 필드에 할당했다.

 

// 입력 도큐먼트
mongo> db.sales.insertMany(
  [
      { "_id" : 1, "item" : "abc", "price" : 10, "quantity" : 2, "date" : ISODate("2014-03-01T08:00:00Z") }
    , { "_id" : 2, "item" : "jkl", "price" : 20, "quantity" : 1, "date" : ISODate("2014-03-01T09:00:00Z") }
    , { "_id" : 3, "item" : "xyz", "price" : 5, "quantity" : 10, "date" : ISODate("2014-03-15T09:00:00Z") }
    , { "_id" : 4, "item" : "xyz", "price" : 5, "quantity" : 20, "date" : ISODate("2014-04-01T11:21:39.736Z") }
    , { "_id" : 5, "item" : "abc", "price" : 10, "quantity" : 10, "date" : ISODate("2014-04-04T21:23:13.331Z") }
  ]
);

// Aggregation 파이프라인 예제
mongo> db.sales.aggregate(
  [
    {
      $group : {
        _id : { 
              month : { $month : "$date" } 
            , day : { $dayOfMonth : "$date" }
            , year : { $year : "$date" }
        }
        , totalPrice : { $sum : { $multiply : [ "$price", "$quantity" ] } }
        , averageQuantity : { $avg : "$quantitu" }
        , count : { $sum : 1 }
      }
    }
  ]
);

// Aggregation 출력결과
{ "_id" : { "month" : 4, "day" : 4, "year" : 2014 }, "totalPrice" : 100, "averageQuantity" : null, "count" : 1 }
{ "_id" : { "month" : 3, "day" : 15, "year" : 2014 }, "totalPrice" : 50, "averageQuantity" : null, "count" : 1 }
{ "_id" : { "month" : 3, "day" : 1, "year" : 2014 }, "totalPrice" : 40, "averageQuantity" : null, "count" : 2 }
{ "_id" : { "month" : 4, "day" : 1, "year" : 2014 }, "totalPrice" : 100, "averageQuantity" : null, "count" : 1 }

 

$group 스테이지 명령으로 특정 필드를 이용한 그룹핑이 일반적이지만,

 

 그룹 없이 전체 합계나 평균균을 계산하고자 할 때도 있다.

 

 이런 경우에는 다음과 같이 $group 스테이지의 "_id” 필드를 null(또는 값이 변하지 않는 상수 값)로 설정하면된다.

 

// Aggregation 파이프라인 예제
mongo> db.sales.aggregate(
  [
    {
      $group : {
          _id : null
        , totalPrice : { $sum : { $multiply : [ "$price", "$quantity" ] } }
        , averageQuantity : { $avg : "$quantity" }
        , count : { $sum : 1 }
      }
    }
  ]
);

// Aggregation 출력결과
{ "_id" : null, "totalPrice" : 290, "averageQuantity" : 8.6, "count" : 5 }

 

$group 스테이지로 유니크한 값만 추출하는 처리( DISTINCT )도 할 수  있다.

 

// Aggregation 파이프라인 예제
mongo> db.sales.aggregate( [ { $group : { _id : "$item" } } ] );

// Aggregation 출력결과
{ "_id" : "abc" }
{ "_id" : "jkl" }
{ "_id" : "xyz" }

 


$sample 스테이지

 

$sample 스테이지는 입력 도큐먼트에서 랜덤하게 지정된 건수의 도큐먼트만 다음 스테이지로 전달한다.

 

// 입력 도큐먼트
mongo> db.users.insertMany(
  [
        { "_id" : 1, "name" : "dave123", "q1" : true, "q2" : true }
      , { "_id" : 2, "name" : "dave2", "q1" : true, "q2" : false }
      , { "_id" : 3, "name" : "ahn", "q1" : true, "q2" : true }
      , { "_id" : 4, "name" : "ahn", "li" : true, "q2" : false }
      , { "_id" : 5, "name" : "ahn", "annT" : true, "q2" : true }
      , { "_id" : 6, "name" : "ahn", "li" : true, "q2" : true }
      , { "_id" : 7, "name" : "ahn", "ty" : true, "q2" : true }
  ]
);

// Aggregation 파이프라인 예제
mongo> db.users.aggregate(
  [ { $sample : { size : 3 } } ]
);

// Aggregation 결과
{ "_id" : 2, "name" : "dave2", "q1" : false, "q2" : false }
{ "_id" : 3, "name" : "ahn", "li" : true, "q2" : true }
{ "_id" : 7, "name" : "ty", "q1" : false, "q2" : true }

 

$sample 스테이지는 어떤 형태로 사용하느냐에 따라서 매우 큰 성능 차이를 보인다.

 

$sample 스테이지가 다음 3가지 조건을 만족하는 경우에는 랜덤 커서( Pseudo Random Cusor )를 이용해서 도큐먼트를 조회한다.

 

MongoDB 서버의 랜덤 커서는 별도의 정렬이나 컬렉션의 풀 스캔 없이 입력 컬렉션으로붙부터 

 

임의의 도큐먼트를 빠르게 가져올 수 있다.

 

 

  • $sample이 파이프라인의 첫 스테이지로 사용된 경우
  • 샘플링하고자 하는 도큐먼트가 컬렉션의 전체 도큐먼트의 5% 미만인 경우
  • 컬렉션이 100개 이상의 도큐먼트를 가진 경우

 

만약 위의 3가지 조건을 만족하지 못한다면 $sample 스테이지는

 

입력된 도큐먼트에 랜덤 값의 필드( Pseudo random number )를 부여한 다음 그 필드를 기준으로 정렬을 수행하고,

 

지정된 건수의 도큐먼트만 다음 스테이지로 전달한다.

 

그래서 이렇게 정렬이 수행된느 경우에는 입력 도큐먼트 건수가 많다면 정렬 과정이 상당한 성성능 정하를 유발한다.

 

결국 $sample이 파이프라인의 첫 스테이지가 아닌 경우에는 SQL에서 다음과 같은 처리를 수행하게 된다.

 

SQL> SELECT * FROM tab ORDER BY RAND() LIMIT N;

 

만약 $sample 스테이지로 입력되는 도큐먼트 건수가 많다면 별도의 정렬 작업이 필요해서 Aggregation의 전체 처리 성능을 떨어뜨린다.

 

그 뿐만 아니라 입력 도큐먼트가 많다면 MongoDB 서버는 정렬을 위해서 추가 메모리를 할당해야 하는데

 

Aggregation은 100MB의 메모리만 사용할 수 있기 때문에 Aggregation 쿼리가 실패 하거나

( 쿼리 실패 : Aggregation의 allowDiskUse 옵션이 false인 경우 )

 

 정렬을 위해서 디스크를 사용( Aggregation의 allowDiskUse 옵션이 true인 경우 )하게 된다.

 

 쿼리에 실패하는 경우도 문제지만, Aggregation의 allowDiskUse 옵션을 true로 설정해서

 

 쿼리는 실패하지 않는다 하더라도 정렬을 위해서 디스크를 사용하므로 쿼리의 성능이 현저하게 떨어질 것이다.

 


$out 스테이지

 

$out 스테이지는 Aggregation 처리 결과를 클라이언트로 즉시 전송하지 않고, 별도의 컬렉션에 저장하도록 한다.

 

$out 옵션은 Aggregation 처리의 결과가 매우 큰 경우에 사용할 수 있으며,

 

$out 옵션에 샤딩된 컬렉션이나 Capped 컬렉션을 지정할 수는 없다.

 

다음 예제는 books 컬렉션이 위치한 데이터 베이스에 authors 라는 컬렉션으로 Aggregation 처리 결과를 저장한다.

 

// Aggregation 파이프라인 예제
mongo> db.books.aggregate(
  [
      { $group : { _id : "$author", books { $push : "$title" } } }
    , { $out : { "authors" }
  ]
);

 

이때 만약 authors 컬렉션이 존재하지 않는다면 Aggregation 처리는 자동으로 authors 컬렉션을 생성해서

 

데이터를 저장하며, 이렇게 자동으로 컬렉션을 생성하는 경우에는 _id 인인덱스( 프라이머리 키 )만 자동 생성한다.

 

그런데 Aggregation이 실행되기 전부터 authors 컬렉션이 있었다면

 

MongoDB는 authors 컬렉션의 데이터를 모두 삭제하고 새롭게 데이터를 저장한다.

 

만약 authours 컬렉션에 도큐먼트 유효성 체크 제약 사항이 있다면

 

Aggregation 실행 시 bypassDocumentValidation 옵션을 true로 설정해서 이를 무시하도록 할 수 있다.

 


$addFields 스테이지

 

$addFields 스테이지는 입력 도큐먼트에 새로운 필드를 추가할 수 있게 해준다.

 

새롭게 추가되는 필드는 기존 필드를 이용해서 연산된 새로운 값일 수도 있으며, 단순히 상수값을 가지는 필드일 수도 있다.

 

다음 예제는 $addFields 스테이지가 2번 사용됐는데,

 

첫 번째 스테이지에서는 totalHomework와 totalQuiz 필드를 새롭게 추가했으며

 

두 번째 스테이지에서는 totalScore 필드를 새롭게 추가했다.

 

// 입력 도큐먼트
mongo> db.scores.insertOne(
  {
      _id : 1
    , student : "Maya"
    , homework : [ 10, 5, 10 ]
    , quiz : [ 10, 8 ]
    , extraCredit : 0
  }
);

// Aggregation 파이프라인 예제
mongo> db.scores.aggregate(
  [
    {
      $addFields : {
          totalHomework : { $sum : "$homework" }
        , totalQuiz : { $sum : "$quiz" }
      }
    }
    , {
      $addFields : {
        totalScore : {
          $add : [ "$totalHomework", "$totalQuiz", "$extraCredit" ]
        }
      }
    }
  ]
);

// Aggregation 결과
{
    "_id" : 1
  , "student" : "Maya"
  , "homework" : [ 10, 5, 10 ]
  , "quiz" : [ 10, 8 ]
  , "extraCredit" : 0
  , "totalHomework" : 25
  , "totalQuiz" : 18
  , "totalScore" : 43
}

 

$addFields 스테이지에서 기존 도큐먼트가 가지고 있던 필드와 동일한 필드명을 새롭게 추가하면

 

기존 필드의 값은 사라지고 새로운 값으로 덮어써진다.

 

// Aggregation 파이프라인 예제
mongo> db.scores.aggregate(
  {
      $addFields : { "extraCredit" : 20 }
  }
);

// Aggregation 결과
{
    "_id" : 1
  , "student" : "Maya"
  , "homework" : [ 10, 5, 10 ]
  , "quiz" : [ 10, 8 ]
  , "extraCredit" : 20
}

 

$addFields 스테이지는 기존 도큐먼트의 서브 도큐먼트에 새로운 필드를 추가할 수도 있다.

 

// 입력 도큐먼트
mongo> db.scores.insertOne(
  { _id : 1, type : "car", specs : { doors : 4, wheels : 4 } }
);

// Aggregation 파이프라인 예제
mongo> db.scores.aggregate(
  {
    $addFields : { "specs.fuel_type" : "unleaded" }
  }
);

// Aggregation 결과
{
    "_id" : 1
  , "type" : "car"
  , "specs" : {
      "doors" : 4
    , "wheels" : 4
    , "fuel_type" : "unleaded"
  }
}

$replaceRoot 스테이지

 

$replaceRoot 스테이지는 입력된 도큐먼트에서 특정 필드를 출력 도큐먼트의 새로운 루트 도큐먼트로 대체한다.

 

// 입력 도큐먼트
mongo> db.produce.insertMany(
  [
    {
        "_id" : 1
      , "fruit" : [ "apples", "oranges" ]
      , "in_stock" : { "oranges" : 20, "apples" : 60 }
      , "on_order" : { "oranges" : 35, "apples" : 75 }
    }
    , {
        "_id" : 2
      , "vgetables" : [ "beets", "yams" ]
      , "in_stock" : { "beets" : 130, "yams" : 200 }
      , "on_order" : { "beets" : 90, "yams" : 145 }
    }
  ]
);

// Aggregation 파이프라인 예제
mongo> db.produce.aggregate(
  {
    $replaceRoot : { newRoot : "$in_stock" }
  }
);

// Aggregation 결과
{ "oranges" : 20, "apples" : 60 }
{ "beets" : 130, "yams" : 200 }

 

$replaceRoot 스테이지를 이용해서 입력 도큐먼트의 필드를 새로운 도큐먼트로 설정하는 경우,

 

$match 스테이지를 이용해서 기존 필드가 존재하는 도큐먼트만 $replaceRoot 스테이지가 처리되게 할 수 도 있다.

 

// Aggregation 파이프라인 예제
mongo> db.produce.aggregate(
    { $match : { in_stock : { $exists : true } } }
  , { $replaceRoot : { newRoot : "$in_stock" } }
);

// Aggregation 결과
{ "oranges" : 20, "apples" : 60 }
{ "beets" : 130, "yams" : 200 }

 


 

$count 스테이지

 

$count 스테이지는 입력 도큐먼트의 개수를 다음 스테이지로 전달한다.

 

// 입력 도큐먼트
mongo> db.scores.insertMany(
  [
      { "_id" : 1, "subject" : "History", "score" : 88 }
    , { "_id" : 2, "subject" : "History", "score" : 92 }
    , { "_id" : 3, "subject" : "History", "score" : 97 }
    , { "_id" : 4, "subject" : "History", "score" : 71 }
    , { "_id" : 5, "subject" : "History", "score" : 79 }
    , { "_id" : 6, "subject" : "History", "score" : 83 }
  ]
);

// Aggregation 파이프라인 예제
mongo> db.scores.aggregate(
  [
      { $match : { score : { $gt : 80 } } }
    , { $count : "passing_scores" }
  ]
);

// Aggregation 결과
{ "passing_scores" : 4 }

 

지금까지 몇 가지 자주 사용되면서 추가 설명이 필요한 스테이지를 살펴봤다.

 

여기에서 언급한 예제들의 더 자세한 설명과 다양한 응용 형태는 MongoDB의 매뉴얼이나

 

검색 엔진을 통한 예제들을 참조하도록 하자.

 

 

파이프라인의 각 스테이지에서는 연산을 수행하거나 그룹핑을 수행하는 등의 내용을 작성해야 하는,

 

이때 Aggregation 연산자( Operator )가 사용된다.

 

MongoDB Aggregation에서는 일반적으로 사용되는 사칙연산 연산자(  +, -, *, /  )를 그대로 사용할 수 없다.

 

이것이 MongoDB의 Aggregation 개발이 SQL보다는 조금 더 어려운 이유이기도 하다.

 

실제 사칙연산자 대신 지정된 연산자를 대체해서 사용해야 하는데, 이는 문장의 복잡잡도를 높이기도 한다.

 

 

파이프 라인에서 사용할 수 있는 연산자의 종류는 다음 포스팅을 참고하자.

 

 

[MongoDB] 확장 검색 쿼리 - 집계 파이프 라인연산자 종류

 

[MongoDB] 확장 검색 쿼리 - 집계 파이프 라인연산자 종류

파이프라인에서 사용할 수 있는 연산자를 종류별로 나눠서 구분해 보자. STEP#01. 문자열 연산자 $concat 연산자 $concat 연산자는 두 개 이상의 문자열을 하나의 문자열로 연결한다. 예시> { $concat : { "

secretartbook.tistory.com