Mongo DB Aggregation

균비·2021년 2월 11일
0

mongodb

목록 보기
1/2

TL;DR

이전 프로젝트를 하면서 aggregate에 대하여 알게 되어 살짝 정리함

+) Reference: jaehun2841.github.io

Basic

기본표기: [stageNameaccumulator]:[stageName || accumulator]: '[fieldname]' || 'string'

ex)

$sum: 1 -> 문서의 개수마다 1씩 +

sum:"sum: "abc" -> abc 필드의

Stages

  1. $match

find 느낌. 조건 만족 Document filtering


	db.products.insert([{
    	name: "제품A",
        type: "음료",
        salePrice: 5000,
        originPrice: 3000,
        purchaseQuantity: 500,
        remainQuantity: 100
    }, {
    	name: "제품A",
        type: "과자",
        salePrice: 3000,
        originPrice: 2000,
        purchaseQuantity: 100,
        remainQuantity: 10
    }, {
    	name: "제품B",
        type: "음료",
        salePrice: 1500,
        originPrice: 100,
        purchaseQuantity: 100,
        remainQuantity: 10
    }])
	db.sales.aggregate([
    	{
        	$match: {
            	type: "음료"
            }
        }
    ])
  1. $group

기존 DB 값을 기반으로 새로운 데이터 생성.
생성된 Group에 대한 id 지정필요, 특정 필드에 대한 집계연산 가능
BUT 정렬 X


	db.products.insert([{
    	name: "제품A",
        type: "음료",
        salePrice: 5000,
        originPrice: 3000,
        purchaseQuantity: 500,
        remainQuantity: 100
    }, {
    	name: "제품A",
        type: "과자",
        salePrice: 3000,
        originPrice: 2000,
        purchaseQuantity: 100,
        remainQuantity: 10
    }, {
    	name: "제품B",
        type: "음료",
        salePrice: 1500,
        originPrice: 100,
        purchaseQuantity: 100,
        remainQuantity: 10
    }])
	db.products.aggreate([
    	{
        	$group: {
            	_id: {
                	prodName: "$name",
                    type: "$type"
                } // 기존 Documents의 name의 필드값과 type의 필드값을 기준으로 grouping
                totalCount: { $sum: 1 } // 해당 Documents에 대한 개수 count,
                revenue: { $sum: {
                	$multiply: [
                    	$substract: [
                        	"$salePrice",
                            "$originPrice"
                        ],
                        $substract: [
	                        "$purchaseQuantity",
                            "$remainQuantity"
                        ]
                    ]
                } } // revenue = ∑{(salePrice - originPrice) * (purchaseQuantity - remainQuantity)}
            }
        }
    
    ])
  1. $project

이 단계에서 지정한 필드값을 다음 파이프라인으로 전달
0: hide || 1: visible

	db.school.insert([{
    	name: "테스트고등학교",
        type: "HS",
        age: [17,18,19],
        student: [...],
        teacher: [...]
    }])
	db.school.aggregate([
    	{
        	$project: {
            	name: 1,
                type: 0
            }, // -> ERROR: Cannot do exclusion on field type in inclusion projection
            $project: {
            	name: 1,
                age: 1
            }, // _id & name & age만 표시
            $project: {
            	name: 0
            } // name 빼고 나머지 데이터 전부 표시
        }
    ])
  1. $sort

정렬.
js의 Array.proptypes.sort와 동일.

  1. $skip

입력수만큼 Document skip

  1. $count

필터링 결과 등 pipeline 내에서 처리된 문서들의 개수를 count하여 입력된 변수명에 담음

// ex: jaehun2841.github.io


db.scores.aggregate(
  [
    {
      $match: {
        score: {
          $gt: 80
        }
      }
    },
    {
      $count: "passing_scores"
    }
  ]
) // -> match의 결과는 안나오고 count의 결과만 'passing_scores'에 담겨 나옴
  1. $addFields

Document에 새로운 필드 추가 (기존필드 + 새 필드)

db.worker.insert([
	{ _id: ObjectId(...), username: "a", workHours: [...] },
    { _id: ObjectId(...), username: "b", workHours: [...] }
])
db.worker.aggregate([
	{
    	$addFields: {
        	workHour: { $sum: "$workHours" } // workHours Array 내의 숫자값들을 ∑
        }
    }
])
  1. $limit

Document 개수제한

  1. $unwind

특정 필드값이 배열인 Document를 각 배열 내부의 값으로 풀어 해치기
배열 내부에 값이 존재할 경우 연산이 불편한 경우가 있으므로 풀어 해치는 것이 연산속도 향상에 도움이 되는 경우도 존재 (*)

{
	_id: ObjectId(...),
    name: "ABC",
    nickNames: ["A", "B", "C"]
},
{
	_id: ObjectId(...),
    name: "가나다",
    nickNames: ["라", "마", "바"]
},
{
	_id: ObjectId(...),
    name: "밁",
    nickNames: []
},
{
	_id: ObjectId(...),
    name: "킴",
    nickNames: "킼"
},
{
	_id: ObjectId(...),
    name: "너는",
    nickNames: null
}

db.users.aggregate([
	{
    	$unwind: {
        	path: "$nickNames", // 풀어 해칠 배열의 필드명
            includeArrayIndex: "nickNamesIDX", // 필드명의 배열에서의 Index값
            preserveNullAndEmptyArrays: true 
            /*
            	true: null / [](빈 배열) 이어도 결과에 포함 (기존 Document 그대로 표시)
                false: null / [](빈 배열) 이면 결과에 포함안됨
            */
        }
    }
])
  1. $sample

아무거나 랜덤으로 데이터 n개 출력


	db.users.aggregate([
    	{
        	$sample: { size: 5 } // 5개 랜덤 출력
        }
    ])
  1. $merge

[prepare...]

  1. $lookup

[prepare...]

accumulator

$group 등에서 Document 값을 가공할 때 사용


$addToSet: (?)

<사칙연산>

$sum: 합(+)

$subtract: 차(-)

$multiply: 곱(*)

$divide: 나누기 (÷)

<기타>

'$substr: [A, B, C]': A 필드의 값을 B인덱스부터 C인덱스 앞까지 가져옴

'$strcasecmp: [A, B]' A 필드와 B 필드의 값을 서로 비교 (1: true, -1: false)

'$toLower: A || $toUpper: A' A 필드의 값을 소문자화 || 대문자화

'$ifNull: [A, B]': A필드의 값을 기본으로 표시하되, A필드값이 없다면 B의 값으로 대체하여 표시

'$convert: { input: "$A", to: "[type]", onError: [value_OE] onNull: [value_ON]}': A필드의 값을 [type]으로 전환하되, 에러가 발생할 경우 [value_OE]
로, onNull일 경우 [value_ON]으로 반환

'$toInt: "$A"': $convert에서 to가 정해져 있는 명령. A필드의 값을 Int형으로 변환 (단, 특정 Document에 Int로 불가능한 값이 있을 경우 에러 발생)

profile
뒹굴뒹굴 고3 개발자!

0개의 댓글