티스토리 뷰
728x90
반응형
7.1 파이프라인, 단계 및 조정 가능 항목
- 집계 프레임워크는 파이프라인 개념을 기반
- 단계마다 해당 입력에 다른 작업을 수행
- 모든 단계의 입력과 출력은 도큐먼트(도큐먼트 스트림)
- 집계 파이프라인의 개별 단계는 데이터 처리 단위
- 한 번에 입력 도큐먼트 스트림을 하나씩 가져와서, 각 도큐먼트를 하나씩 처리하고, 출력 도큐먼트 스트림을 하나씩 생성

- 각 단계는 knobs 또는 tunables 셋을 제공
- 이 항목들을 조정해 각 단계를 매개변수로 지정함으로써 원하는 작업을 수행 가능
- tuables은 필드를 수정, 산술 연산, 도큐먼트 재구성, 누산 작업 등 연산자 형태
- 동일한 유형의 단계를 단일 파이프라인의 여러 번 포함 가능

7.2 단계 시작하기
- aggregate는 집계 쿼리를 실행할 때 호출하는 메서드
- 일치 단계는 컬렉션에 대해 필터링, 결과 도큐먼트를 한번에 하나씩 선출 단계로 전달
- 선출 단계는 작업을 수행하고 도큐먼트 모양을 변경한 후 출력을 파이프라인에서 다시 전달
db.students.aggregate({$match : {class_id : 8919}}); //일치
db.students.aggregate([
{$match : {class_id : 8919}},
{$project : { //선출
_id:0,
student_id: 1
}}
]);
- 선출 단계를 먼저 실행하고 제한을 실행해도 동일한 결과
- 효율성을 위해 다른 단계로 전달해야 하는 도큐먼트 수 제한을 먼저 수행
- 순서가 중요하다면 제한 단계 전에 정렬 수행
db.students.aggregate([
{$match : {class_id : 8919}},
{$limit : 5}, //제한
{$project : {
_id:0,
student_id: 1
}}
]);
db.students.aggregate([
{$match : {class_id : 8919}},
{$project : {
_id:0,
student_id: 1
}},
{$limit : 5} //동일한 결과
]);
db.students.aggregate([
{$match : {class_id : 8919}},
{$sort : {student_id : 1}}, //제한 전에 정렬
{$limit : 5},
{$project : {
_id:0,
student_id: 1
}}
]);
7.4 $project 연산자
- 필드 선택: 특정 필드만을 출력에 포함하고자 할 때 사용
- 필드 이름 변경: 출력에 포함되는 필드의 이름을 변경하고자 할 때 사용
- 계산된 필드 추가: 새로운 필드를 추가하고, 해당 필드에 대한 계산을 수행하고자 할 때 사용
- $add, $subtract, $multiply, $divide 등의 연산자를 사용하여 필드의 값을 계산
{ $project: { name: 1, age: 1 } } //"name"과 "age" 필드만을 출력으로 선택
{ $project: { _id: 0, productName: "$name" } } //"_id" 필드를 제외하고 "name" 필드를 "productName"으로 이름을 변경하여 출력
{ $project: { totalPrice: { $multiply: ["$price", "$quantity"] } } } //"price"와 "quantity" 필드를 곱한 값을 "totalPrice" 필드로 추가
중첩 필드 승격
- 중첩된 필드를 승격하면 중첩 구조를 제거하고 평면적인 필드 구조로 변환
- $문자는 선출 단계에서 값을 지정하는데 사용, 해당 값이 필드 경로로 해석되고 각 필드에서 선출할 값을 선택하는데 사용됨을 나타냄
{
_id: 1,
info: {
name: "John",
age: 30
}
}
db.collection.aggregate([
{
$project: {
_id: 1,
name: "$info.name", //"info" 필드를 승격하여 "name"과 "age" 필드로 변환
age: "$info.age"
}
}
])
{
_id: 1,
name: "John",
age: 30
}
7.5 $unwind 연산자
- 배열 필드를 각 원소로 분리하여 여러 개의 문서로 변환하는 집계 연산자
- 배열 필드를 활용하여 집계 작업을 수행하는 데에 유용
{
$unwind: {
path: "<arrayField>", //분리할 배열 필드의 이름
includeArrayIndex: "<indexField>", //배열의 인덱스를 저장할 필드의 이름을 지정
preserveNullAndEmptyArrays: <boolean> // true인 경우 비어 있는 배열이나 null 값이 포함된 문서도 결과에 포함
}
}
{
_id: 1,
customer: "John",
products: ["Apple", "Banana", "Orange"]
},
{
_id: 2,
customer: "Jane",
products: ["Grapes", "Mango"]
}
db.orders.aggregate([
{ $unwind: "$products" } //"products" 필드를 분리하여 각 원소를 개별 문서로 만든다
])
{
_id: 1,
customer: "John",
products: "Apple"
},
{
_id: 1,
customer: "John",
products: "Banana"
},
{
_id: 1,
customer: "John",
products: "Orange"
},
{
_id: 2,
customer: "Jane",
products: "Grapes"
},
{
_id: 2,
customer: "Jane",
products: "Mango"
}
- $unwind를 먼저 적용한 후에 $match를 적용하는 것이 일반적
{
_id: 1,
fruits: ["apple", "banana", "orange"]
}
db.collection.aggregate([
{ $match: { fruits: "apple" } }, //$match를 먼저 적용하고 $unwind를 적용
{ $unwind: "$fruits" }
]);
{
_id: 1,
fruits: ["apple", "banana", "orange"] //"fruits" 필드의 값이 "apple"인 문서는 선택되지만, $unwind 이전의 형태를 유지
}
db.collection.aggregate([
{ $unwind: "$fruits" }, //$unwind를 적용하고, 그 다음에 $match를 적용
{ $match: { fruits: "apple" } }
])
{
_id: 1,
fruits: "apple" //"fruits" 필드의 값이 "apple"인 문서만을 선택
}
7.6 배열 표현식
- 선출 단계에서 배열 표현식을 사용하여 데이터를 가공하고 변환
- 배열 표현식은 집계 파이프라인의 초기 단계에서 사용되며, 주로 $project 연산자와 함께 사용
- 일반적으로 배열 표현식은 $map, $filter, $reduce와 같은 연산자와 함께 사용되어 배열 필드의 원소를 조작하고 변환하는 데 사용
{
_id: 1,
customer: "John",
prices: [10, 15, 20]
},
{
_id: 2,
customer: "Jane",
prices: [5, 12, 8]
}
db.orders.aggregate([
{
$project: {
_id: 1,
customer: 1,
multipliedPrices: {
$map: {
input: "$prices",
as: "price",
in: { $multiply: ["$$price", 10] } } } //"prices" 필드의 각 원소에 10을 곱한 값을 "multipliedPrices" 필드로 추가
}
}
])
{
_id: 1,
customer: "John",
multipliedPrices: [100, 150, 200]
},
{
_id: 2,
customer: "Jane",
multipliedPrices: [50, 120, 80]
}
- $$ 는 작업 중인 표현식 내에서 정의된 변수를 참조하는데 사용 ("as"에 정의된 변수를 참조하는 데 사용)
- input 은 배열을 지정
- as 는 배열에 사용할 이름을 지정
- cond 는 조건을 지정
{
_id: 1,
customer: "John",
products: [
{ name: "Apple", price: 10 },
{ name: "Banana", price: 15 },
{ name: "Orange", price: 20 }
]
},
{
_id: 2,
customer: "Jane",
products: [
{ name: "Grapes", price: 5 },
{ name: "Mango", price: 12 },
{ name: "Kiwi", price: 8 }
]
}
db.orders.aggregate([
{
$project: {
_id: 1,
customer: 1,
filteredProducts: {
$filter: {
input: "$products",
as: "product",
cond: { $gte: ["$$product.price", 10] } //"products" 필드에서 가격이 10 이상인 상품들만을 선택
}
}
}
}
])
{
_id: 1,
customer: "John",
filteredProducts: [
{ name: "Apple", price: 10 },
{ name: "Banana", price: 15 },
{ name: "Orange", price: 20 }
]
},
{
_id: 2,
customer: "Jane",
filteredProducts: [
{ name: "Mango", price: 12 }
]
}
- $arrayElemAt 연산자를 사용하면 배열 내 특정 슬롯에서 요소를 선택 가능
- 인덱스는 0부터 시작하며, 음수 인덱스는 배열의 끝에서부터 역순으로 원소를 선택 (마지막 요소는 -1)
{
_id: 1,
customer: "John",
products: ["Apple", "Banana", "Orange"]
}
db.orders.aggregate([
{
$project: {
_id: 1,
customer: 1,
secondProduct: { $arrayElemAt: ["$products", 1] }
}
}
])
{
_id: 1,
customer: "John",
secondProduct: "Banana"
}
- $slice 표현식은 배열 필드의 원하는 범위나 개수의 원소를 선택하여 반환하는 데 활용
{
_id: 1,
customer: "John",
products: ["Apple", "Banana", "Orange", "Grapes", "Mango"]
}
db.orders.aggregate([
{
$project: {
_id: 1,
customer: 1,
selectedProducts: { $slice: ["$products", 1, 3] }
}
}
])
{
_id: 1,
customer: "John",
selectedProducts: ["Banana", "Orange", "Grapes"]
}
- $size 표현식은 배열 요소 개수 값을 제공
- $project 단계에서 배열 필드 $max 사용
- $project 단계에서 $reduce와 $cond 연산자를 사용하여 "products" 배열에서 최댓값을 선택합니다. $reduce 연산자는 input으로 배열을 받고, initialValue로 첫 번째 원소를 설정합니다. in 절에서는 $cond 연산자를 사용하여 현재 원소 $$this와 누적된 최댓값 $$value를 비교하여 더 큰 값을 선택
{
_id: 1,
customer: "John",
products: [5, 10, 8, 12, 7]
},
{
_id: 2,
customer: "Jane",
products: [15, 9, 11, 6, 14]
},
{
_id: 3,
customer: "Alice",
products: [3, 7, 13, 9, 4]
}
db.orders.aggregate([
{
$project: {
_id: 1,
customer: 1,
maxProduct: {
$reduce: {
input: "$products",
initialValue: { $arrayElemAt: ["$products", 0] },
in: {
$cond: {
if: { $gt: ["$$this", "$$value"] },
then: "$$this",
else: "$$value"
}
}
}
}
}
}
])
{
_id: 1,
customer: "John",
maxProduct: 12
},
{
_id: 2,
customer: "Jane",
maxProduct: 15
},
{
_id: 3,
customer: "Alice",
maxProduct: 13
}
7.7 누산기
- 집계 프레임워크가 제공하는 누산기를 사용하면 특정 필드의 모든 값 합산, 평균 계산 등 작업 가능
- 선출 단계에서는 $sum, $avg와 같은 누산기가 단일 도큐먼트 내 배열에서만 작동
- 그룹단계에서는 누산기가 여러 도큐먼트 값에 걸쳐 계산 수행
db.companies.aggregate([
{$match: {"funding_rounds" : {$exists: true, $ne: []}},
{$project: {
_id : 0,
name : 1,
largest_round : {$max: "$funding_rounds.raised_amount"}
}}
])
7.8 그룹단계
- $group 연산자를 사용하여 데이터를 그룹화하는 단계
- $group 연산자는 특정 필드를 기준으로 그룹을 생성하고, 그룹 내에서 필요한 계산이나 집계 작업을 수행
{
$group: {
_id: <expression>, // 그룹화할 기준 필드 또는 표현식
field1: { <accumulator1> : <expression1> }, // 필요한 누산기와 표현식
field2: { <accumulator2> : <expression2> },
...
}
}
- $sum: 필드 값을 합산합니다.
- $avg: 필드 값을 평균 계산합니다.
- $min: 필드의 최솟값을 선택합니다.
- $max: 필드의 최댓값을 선택합니다.
- $first: 그룹 내에서 첫 번째 문서의 값을 선택합니다.
- $last: 그룹 내에서 마지막 문서의 값을 선택합니다.
- $push: 그룹 내의 값들을 배열로 수집합니다.
- $addToSet: 그룹 내의 고유한 값들을 배열로 수집합니다.
db.orders.aggregate([
{
$group: {
_id: "$customer", //"orders" 컬렉션을 "customer" 필드를 기준으로 그룹화
totalAmount: { $sum: "$amount" } //그룹 내에서 "amount" 필드 값을 합산하여 "totalAmount" 필드를 계산
}
}
])
7.8.1 그룹 단계의 _id 필드
그룹 값에 레이블을 지정하지 않으면 그룹화한다는 점이 분명하지 않기 때문에 그룹화할 값에 명시적으로 레이블 지정
db.companies.aggregate([
{$match: {founded_year: {$gte: 2010}},
{$group: {
_id: {founded_year: "$founded_year", category_code: "$category_code"}, //레이블 지정
companies: {$push : "$name"}
}},
{$sort: {"_id.founded_year" : 1}}
]).pretty()
7.8.2 그룹 vs. 선출
- $push, $first, $last 표현식은 그룹 단계에서만 작동
- 그룹 단계가 도큐먼트의 입력 스트림을 가져와 각 도큐먼트를 처리해 값을 축적하도록 설계
- 선출 단계는 도큐먼트를 개별적으로 재구성하도록 설계
7.9 집계 파이프라인 결과를 컬렉션에 쓰기
- 집계 파이프라인에서 생성된 도큐먼트를 컬렉션에 쓸 수 있는 두가지 단계
- 집계 파이프라인 마지막 단계에서 수행
- $out
- 동일한 데이터베이스에만 쓸 수 있음
- 기존 컬렉션이 있으면 덮어씀
- 샤딩된 컬렉션 쓸 수 없음
- $merge
- 샤딩 여부에 관계없이 모든 데이터베이스와 컬렉션에 쓸 수 있음
- 기존 컬렉션으로 작업할 때 결과를 통합할 수 있음
- 출력 컬렉션의 내용이 점진적으로 갱신되는 주문식의 구체화된 뷰를 생성
728x90
반응형
'DB > MongoDB' 카테고리의 다른 글
[MongoDB] 트랜잭션 (0) | 2023.07.02 |
---|---|
[MongoDB] 애플리케이션 설계 (0) | 2023.06.20 |
[MongoDB] 공간 정보 인덱스 (0) | 2023.04.26 |
[MongoDB] 인덱싱2 (0) | 2023.04.18 |
[MongoDB] 인덱싱 (0) | 2023.03.22 |
반응형
300x250