Aggregation
- 하나 이상의 collection에 대해 분석 가능
- stage별로 분석되는 pipeline의 개념
- 각 stage는 tuning 가능한 process unit
- 모든 stage에서 data input과 output은 document 형태임
- 마지막 stage에서 생성된 document에 대한 cursor가 return됨
Aggregation pipeline
Pipeline stages : $match, $project, $count, $limt, $sort, $skip, $geoNear, $lookup ...
- $match : 조건 검색
> db.c.find({ A : 'B' })
> db.c.aggregate([{$match:{A:'B'}}])
- $count : 개수 세기
> db.c.find({ A : 'B' }).count()
> db.c.count({ A : 'B' })
> db.c.aggregate([{$match : {A : 'B'}}, {$count : 'count_B'}])
- $project : 필요한 field만 projection
> db.c.find({ A : 'B' }, {_id:0, A:1})
> db.c.aggregate([{$match:{A:'B'}}, {$project : {_id:0, A:1}}])
- $limt : return되는 문서 개수 제한
> db.c.find({ A : 'B' }).limit(5)
> db.c.aggregate([{$match:{A:'B'}}, {$limit : 5}])
- $sort : 주어진 field에 대해 정렬
> db.c.find({ A : 'B' }).sort({A:1})
> db.c.aggregate([{$match:{A:'B'}}, {$sort : {A :1}}])
- $skip : 주어진 개수만큼 skip하고 return
> db.c.find({ A : 'B' }).skip(2)
> db.c.aggregate([{$match:{A:'B'}}, {$skip : 2}])
- $unwind : array field의 각 element를 하나씩 return
> db.c.aggregate([{$match:{A:'B'}}, {$unwind : '$name'}])
-> name 내부 array의 각 element마다 결과가 출력
- stage의 순서 확인하기
> db.c.find({ A : 'B' }).sort({A:1}).limit(5), db.c.find({ A : 'B' }).limit(5).sort({A:1})
-> 두개 결과 같음. 쓴 순서와 상관없이 MongoDB 내부적으로 sort 후 limit를 함
> db.c.aggregate([{$match:{A:'B'}}, {$sort : {A :1}}, {$limit : 5}]) -> sort 후 limt를 함
> db.c.aggregate([{$match:{A:'B'}}, {$limit : 5}, {$sort : {A :1}}]) -> limit 후 sort를 함
Projection으로 New fields 만들기
- $project에 기존 field로 new field name 정의할 수 있음('$기존필드' 사용)
> db.c.find({ A : 'B' }, {_id:0, A:1, 'B.d':1}) -> projection
> db.c.aggregate([{$match:{A:'B'}}, {$project : {_id:0, A:1, B_name : '$B.d'}}])
- $를 사용 안하면 ' ' 안의 이름 자체가 새로운 field에 대한 value로 들어감
- 해당 field에 내용이 없으면 결과가 안나옴
Project내부 operator
Pipeline operator : $arrayElemAt , $size , $slice , $filter , $cond, $switch, $avg, $push, $switch, $min, $max
- $arrayElemAt : array element 중 주어진 index에 해당하는 element값을 새로운 field 이름의 value로 반환
> db.c.aggregate([{$project:{ new_B : { $arrayElemAt : ['$B', 0]} }} ])
-> B field의 0번째 index의 값을 new_B라는 새로운 field의 value로 return
- $size : array의 총 element 개수를 return
> db.c.aggregate([{$project:{ num_B : { $size : '$B'} }} ])
-> B field의 크기(element 수)를 num_B로 return
- $slice : array 내 선택한 index의 element만 return
> db.c.aggregate([{$project:{ new_B : { $slice : '$B', 0, 2} }} ])
-> B field에서 0, 2번째 index에 해당하는 element만 new_B로 return
- $filter : 주어진 조건에 해당하는 subset만 return함. as로 내부변수 정의, $$this로 as 대체 가능
> db.c.aggregate([{$project:{ new_B : { $filter : {input : '$B.c', as : 'B_c', cond : {$gt : '$$B_c', 10}} }}} ])
-> B.c를 내부변수 B_c로 rename하고 B_c가 10보다 큰 것만 new_B로 filter하여 return
> db.c.aggregate([{$project:{ new_B : { $filter : {input : '$B.c', cond : {$gt : '$$this', 10}} }}} ])
-> B.c를 B_c로 rename하고 B_c가 10보다 큰 것만 new_B로 filter하여 return
- $cond : 주어진 조건에 맞거나 맞지 않을 때 지정한 값 return
> db.c.aggregate([{$project : {A : {$cond : {if: {$gt : ['$a', '$b']}, then: 'good' else: 'bad'}}}} ])
-> cond 내부 if 조건에 맞으면 then 뒤('good'), 아니면 else 뒤 ('bad') return
> db.c.aggregate([{$project : {A : {$cond : [{$gt : ['$a', '$b']}, 'good', 'bad'] }}} ])
-> cond 내부 array의 조건에 맞으면 조건 뒤 첫번째 값 return, 아니면 두번째 값 return
- $switch : case expression이 true값을 찾으면 flow를 breakout하고 빠져나옴
> db.c.aggregate([{$project : A: {$switch : {branches : [
{case : {$eq : ['$B', 10]}, then : 1},
{case : {$eq : ['$B', 20]}, then : 2}],
default : 0}} }} ])
-> case가 조건에 맞으면 빠져나옴. 맞는 조건이 없으면 default 값 return
- $max, $min, $avg, $sum : array에서 해당 연산 가능
> db.c.aggregate([{$project:{ max_B : { $max : '$B'}, min_B : {$min : '$B'} }} ])
-> B field의 max, min값을 max_B, min_B로 return
- $ne:[]
> db.c.aggregate([{$match : { A : {$exists:true, $ne: [] }}} ])
-> A값이 없는 document는 return안됨
Grouping
- $group : _id 에 해당하는 field로 groupyby함
> db.c.aggregate([{$group: {_id : '$A', avg : {$avg : 'num' }, min : {$min : 'num'}}}, {$sort : {$avg : -1}} ])
-> _id로 설정한 A field로 group by해서 avg, min값을 계산하고 avg값으로 내림차순으로 return
Geospatial in aggregation
- $match stage에서 $geoWithin 사용 가능
- $near나 $nearSphere 사용 불가
> db.c.find([{'coord' : {$geoWithin : {$center : [[10.1, 11.1], 0.1/111.1]}}} ])
> db.c.aggregate([{$match : {'coord' : {$geoWithin : {$center : [[10.1, 11.1], 0.1/111.1]}}} } ])
- $match 대신 $geoNear 사용 가능
> db.c.aggregate([{$geoNear: {near : {type : 'Point', coordinates : [10.1, 11.1]}, key : 'address.coord', distanceField : "DIST", maxDistance : 100} } ])
$match stage에서의 restriction
- $match stage에서는 $<field> 사용 안됨
- $expr을 사용해서 $<field> 사용 가능
> db.c.aggregate([{$match : {$expr : {$gt : ["$A", "$num"]}}} ])
'NoSQL' 카테고리의 다른 글
MongoDB - text search (0) | 2023.08.08 |
---|---|
MongoDB - Geospatial data (0) | 2023.08.08 |
MongoDB - Indexing (0) | 2023.08.07 |
MongoDB - Querying (0) | 2023.08.07 |
MongoDB - Crud operations (0) | 2023.08.07 |