이전 프로젝트를 하면서 aggregate에 대하여 알게 되어 살짝 정리함
+) Reference: jaehun2841.github.io
기본표기: [fieldname]' || 'string'
ex)
$sum: 1 -> 문서의 개수마다 1씩 +
abc" -> abc 필드의
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: "음료"
}
}
])
기존 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)}
}
}
])
이 단계에서 지정한 필드값을 다음 파이프라인으로 전달
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 빼고 나머지 데이터 전부 표시
}
])
정렬.
js의 Array.proptypes.sort와 동일.
입력수만큼 Document skip
필터링 결과 등 pipeline 내에서 처리된 문서들의 개수를 count하여 입력된 변수명에 담음
// ex: jaehun2841.github.io
db.scores.aggregate(
[
{
$match: {
score: {
$gt: 80
}
}
},
{
$count: "passing_scores"
}
]
) // -> match의 결과는 안나오고 count의 결과만 'passing_scores'에 담겨 나옴
Document에 새로운 필드 추가 (기존필드 + 새 필드)
db.worker.insert([
{ _id: ObjectId(...), username: "a", workHours: [...] },
{ _id: ObjectId(...), username: "b", workHours: [...] }
])
db.worker.aggregate([
{
$addFields: {
workHour: { $sum: "$workHours" } // workHours Array 내의 숫자값들을 ∑
}
}
])
Document 개수제한
특정 필드값이 배열인 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 / [](빈 배열) 이면 결과에 포함안됨
*/
}
}
])
아무거나 랜덤으로 데이터 n개 출력
db.users.aggregate([
{
$sample: { size: 5 } // 5개 랜덤 출력
}
])
[prepare...]
[prepare...]
$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로 불가능한 값이 있을 경우 에러 발생)