Docs Menu
Docs Home
/
MongoDB 매뉴얼
/ / /

어큐뮬레이터(집계)

이 페이지의 내용

  • 정의
  • 구문
  • 행동
  • 예시
$accumulator

중요

서버 측 JavaScript는 더 이상 사용되지 않습니다

MongoDB 8.0부터 서버 측 JavaScript 함수($accumulator, $function, $where)가 더 이상 사용되지 않습니다. 이러한 함수를 실행하면 MongoDB가 경고를 기록합니다.

사용자 지정 누산기 연산자를 정의합니다. 축적자는 문서가 파이프라인을 통해 진행됨에 따라 상태를 유지하는 연산자입니다(예: 총계, 최대값, 최소값 및 관련 데이터). $accumulator 연산자로 자체 JavaScript 함수를 실행하여 MongoDB 쿼리 언어에서 지원하지 않는 동작을 구현합니다. $function도 참조하세요.

$accumulator 이 단계에서 사용할 수 있습니다:

  • $bucket

  • $bucketAuto

  • $group

중요

집계 연산자 내에서 JavaScript를 실행하면 성능이 저하될 수 있습니다. 제공된 파이프라인 연산자로 애플리케이션의 요구 사항을 충족할 수 없는 경우에만 $accumulator 연산자를 사용합니다.

$accumulator 연산자의 구문은 다음과 같습니다.

{
$accumulator: {
init: <code>,
initArgs: <array expression>, // Optional
accumulate: <code>,
accumulateArgs: <array expression>,
merge: <code>,
finalize: <code>, // Optional
lang: <string>
}
}
필드
유형
설명

문자열 또는 코드

상태 를 초기화하는 데 사용되는 함수입니다. 함수는 init initArgs 배열 표현식 에서 인수를 받습니다. 함수 정의를 BSON type 코드 또는 문자열로 지정할 수 있습니다.

init 함수의 형식은 다음과 같습니다.

function (<initArg1>, <initArg2>, ...) {
...
return <initialState>
}

디스크로 유출하거나 샤딩된 클러스터에서 쿼리를 실행하면 축적자가 각각 init()을(를) 호출하여 시작하는 여러 하위 누적의 병합으로 계산될 수 있습니다. init(), accumulate()merge() 함수가 이 실행 모델과 호환되는지 확인합니다.

배열

선택 사항. init 함수에 전달된 인수입니다.

initArgs 의 형식은 다음과 같습니다.

[ <initArg1>, <initArg2>, ... ]

중요: $bucketAuto 단계에서 사용되는 경우 initArgs는 그룹 키를 참조할 수 없습니다(즉, $<fieldName> 구문을 사용할 수 없음). 대신 $bucketAuto 단계에서는 initArgs에 상수 값만 지정할 수 있습니다.

문자열 또는 코드

문서를 누적하는 데 사용되는 accumulate 함수입니다. 함수는 현재 상태 와 aggregateArgs 배열 표현식 에서 인수를 accumulate 받습니다. 함수의 결과는 새 상태 가 됩니다. 함수 정의를 BSON type 코드 또는 문자열로 지정할 수 있습니다.

accumulate 함수의 형식은 다음과 같습니다.

function(state, <accumArg1>, <accumArg2>, ...) {
...
return <newState>
}

배열

accumulate 함수에 전달된 인수입니다. accumulateArgs을 사용하여 accumulate 함수에 전달할 필드 값을 지정할 수 있습니다.

accumulateArgs 의 형식은 다음과 같습니다.

[ <accumArg1>, <accumArg2>, ... ]

문자열 또는 코드

두 개의 내부 상태를 병합하는 데 사용되는 함수입니다. merge 은(는) 문자열 또는 코드 BSON 유형이어야 합니다. merge 은 병합된 두 상태의 결합된 결과를 반환합니다. 병합 함수가 호출되는 경우에 대한 자세한 내용은 로 두 가지 상태 병합 항목을 참조하세요.$merge

merge 함수의 형식은 다음과 같습니다.

function (<state1>, <state2>) {
<logic to merge state1 and state2>
return <newState>
}

문자열 또는 코드

선택 사항. 누적 결과를 업데이트하는 데 사용되는 함수입니다.

finalize 함수의 형식은 다음과 같습니다.

function (state) {
...
return <finalState>
}

문자열

코드에 $accumulator 사용된 언어 입니다.

중요: 현재 lang에 대해서는 js만 지원됩니다.

다음 단계에서는 $accumulator 연산자가 문서를 처리하는 방법을 간략하게 설명합니다.

  1. 연산자는 init 함수에 의해 정의된 초기 상태에서 시작됩니다.

  2. 각 문서에 대해 연산자는 accumulate 함수를 기반으로 상태를 업데이트합니다. accumulate 함수의 첫 번째 인수는 현재 상태이며 추가 인수는 accumulateArgs 배열에 지정됩니다.

  3. 연산자는 여러 중간 상태를 병합해야 하는 경우 병합 함수를 실행합니다. 병합 함수가 호출되는 경우에 대한 자세한 내용은 $merge으로 두 가지 상태 병합 항목을 참조하세요.

  4. finalize 함수가 정의되어 있는 경우 모든 문서가 처리되고 그에 따라 상태가 업데이트되면 finalize 함수가 상태를 최종 출력으로 변환합니다.

내부 연산의 일부로 $accumulator 연산자는 두 개의 개별적인 중간 상태를 병합해야 할 수 있습니다. merge 함수는 연산자가 두 상태를 병합하는 방법을 지정합니다.

병합 함수는 항상 한 번에 상태 2개를 병합합니다. 상태를 3개 이상 병합해야 하는 경우 상태 2개의 결과 병합은 단일 상태로 병합됩니다. 이 프로세스는 모든 상태가 병합될 때까지 반복됩니다.

예를 들어 $accumulator는 다음 시나리오에서 두 상태를 결합해야 할 수 있습니다.

  • $accumulator 샤딩된 클러스터에서 실행됩니다. 연산자는 최종 결과를 얻기 위해 각 샤드의 결과를 병합해야 합니다.

  • 단일 $accumulator 작업이 지정된 메모리 제한을 초과했습니다. allowDiskUse 옵션을 지정하면 연산자는 진행 중인 작업을 디스크에 저장하고 메모리에서 작업을 완료합니다. 연산이 완료되면 병합 기능을 사용하여 디스크와 메모리의 결과를 함께 병합합니다.

MongoDB가 init(), accumulate(), merge() 함수에 대해 문서를 처리하는 순서는 다를 수 있으며, $accumulator 함수에 해당 문서가 지정된 순서와 다를 수 있습니다.

예를 들어 _id 필드가 알파벳 문자인 일련의 문서를 생각해 보세요.

{ _id: 'a' },
{ _id: 'b' },
{ _id: 'c' }
...
{ _id: 'z' }

다음으로 _id 필드를 기준으로 문서를 정렬한 다음 $accumulator 함수를 사용하여 _id 필드 값을 연결하는 집계 파이프라인을 고려합니다.

[
{
$sort: { _id: 1 }
},
{
$group: {
_id: null,
alphabet: {
$accumulator: {
init: function() {
return ""
},
accumulate: function(state, letter) {
return(state + letter)
},
accumulateArgs: [ "$_id" ],
merge: function(state1, state2) {
return(state1 + state2)
},
lang: "js"
}
}
}
}
]

MongoDB는 문서가 정렬된 순서로 처리되는 것을 보장하지 않으므로 alphabet 필드가 반드시 abc...z로 설정되는 것은 아닙니다.

이 동작으로 인해 $accumulator 함수가 특정 순서로 문서를 처리하고 반환할 필요가 없는지 확인합니다.

$accumulator를 사용하려면 서버측 스크립팅을 활성화해야 합니다.

$accumulator (또는 $function, $where 또는 mapReduce)을 사용하지 않는 경우 서버 측 스크립팅을 비활성화합니다.

또한 ➤ 보안 구성 옵션으로 MongoDB 실행을 참조하세요.

MongoDB 6.0 은 서버 측 JavaScript, $accumulator, $function$where 표현식에 사용되는 내부 JavaScript 엔진 을 MozJS-60에서 MozJS-91로 업그레이드합니다. MozJS-60에 존재했던 몇 가지 사용 중단된 비표준 배열 및 문자열 함수는 MozJS-91에서 제거되었습니다.

제거된 배열 및 문자열 함수의 전체 목록은 6.0 호환성 정보를 참조하십시오.

참고

이 예시에서는 $accumulator 연산자를 사용해 MongoDB에서 이미 지원되고 있는 $avg 연산자를 구현하는 방법을 안내합니다. 이 예시의 목표는 새로운 기능을 구현하는 것이 아니라 $accumulator 연산자의 동작과 구문을 익숙한 논리로 설명하는 것입니다.

mongosh에서 다음 문서를 사용하여 books라는 이름의 샘플 컬렉션을 만듭니다.

db.books.insertMany([
{ "_id" : 8751, "title" : "The Banquet", "author" : "Dante", "copies" : 2 },
{ "_id" : 8752, "title" : "Divine Comedy", "author" : "Dante", "copies" : 1 },
{ "_id" : 8645, "title" : "Eclogues", "author" : "Dante", "copies" : 2 },
{ "_id" : 7000, "title" : "The Odyssey", "author" : "Homer", "copies" : 10 },
{ "_id" : 7020, "title" : "Iliad", "author" : "Homer", "copies" : 10 }
])

다음 연산은 groups(을)를 author로 문서화하고 $accumulator(을)를 사용하여 각 저자의 전체 평균 사본 수를 계산합니다.

db.books.aggregate([
{
$group :
{
_id : "$author",
avgCopies:
{
$accumulator:
{
init: function() { // Set the initial state
return { count: 0, sum: 0 }
},
accumulate: function(state, numCopies) { // Define how to update the state
return {
count: state.count + 1,
sum: state.sum + numCopies
}
},
accumulateArgs: ["$copies"], // Argument required by the accumulate function
merge: function(state1, state2) { // When the operator performs a merge,
return { // add the fields from the two states
count: state1.count + state2.count,
sum: state1.sum + state2.sum
}
},
finalize: function(state) { // After collecting the results from all documents,
return (state.sum / state.count) // calculate the average
},
lang: "js"
}
}
}
}
])

이 연산은 다음과 같은 결과를 반환합니다.

{ "_id" : "Dante", "avgCopies" : 1.6666666666666667 }
{ "_id" : "Homer", "avgCopies" : 10 }

$accumulatorcountsum이 모두 0으로 설정된 초기 상태를 정의합니다. $accumulator가 처리하는 각 문서에 대해 다음을 통해 상태를 업데이트합니다.

  • count를 1씩 증가

  • 문서의 copies 필드 값을 sum에 추가합니다. accumulate 함수는 accumulateArgs 필드에 전달되므로 copies 필드에 액세스할 수 있습니다.

문서가 처리될 때마다 accumulate 함수는 업데이트된 상태를 반환합니다.

모든 문서가 처리되면 finalize 함수는 사본의 sum을 문서의 count로 나누어 평균을 구합니다. 이렇게 하면 finalize 함수가 모든 문서의 누적 sumcount를 수신하므로 실행 중인 계산된 평균을 계속 유지할 필요가 없습니다.

이 작업은 $avg 연산자를 사용하는 다음 파이프라인과 동일합니다.

db.books.aggregate([
{
$group : {
_id : "$author",
avgCopies: { $avg: "$copies" }
}
}
])

initArgs 옵션을 사용하여 $accumulator의 초기 상태를 변경할 수 있습니다. 예를 들어 다음과 같은 경우에 유용합니다.

  • 사용자의 상태에 없는 필드 값을 사용하여 사용자의 상태에 영향을 미치거나

  • 처리 중인 그룹에 따라 초기 상태를 다른 값으로 설정합니다.

mongosh에서 다음 문서를 사용하여 restaurants라는 이름의 샘플 컬렉션을 만듭니다.

db.restaurants.insertMany([
{ "_id" : 1, "name" : "Food Fury", "city" : "Bettles", "cuisine" : "American" },
{ "_id" : 2, "name" : "Meal Macro", "city" : "Bettles", "cuisine" : "Chinese" },
{ "_id" : 3, "name" : "Big Crisp", "city" : "Bettles", "cuisine" : "Latin" },
{ "_id" : 4, "name" : "The Wrap", "city" : "Onida", "cuisine" : "American" },
{ "_id" : 5, "name" : "Spice Attack", "city" : "Onida", "cuisine" : "Latin" },
{ "_id" : 6, "name" : "Soup City", "city" : "Onida", "cuisine" : "Chinese" },
{ "_id" : 7, "name" : "Crave", "city" : "Pyote", "cuisine" : "American" },
{ "_id" : 8, "name" : "The Gala", "city" : "Pyote", "cuisine" : "Chinese" }
])

사용자가 이 데이터를 쿼리하여 레스토랑을 찾을 수 있는 애플리케이션이 있다고 가정해 보겠습니다. 사용자가 거주하고 있는 도시에 대한 결과를 더 많이 표시하는 것이 유용할 수 있습니다. 이 예시에서는 사용자의 도시가 userProfileCity이라는 변수에서 호출된다고 가정합니다.

다음 집계 파이프라인은 city별로 문서를 groups 작업합니다. 이 연산은 $accumulator를 사용하여 레스토랑의 도시가 사용자 프로필의 도시와 일치하는지 여부에 따라 각 도시에서 다른 수의 결과를 표시합니다.

참고

mongosh에서 이 예시를 실행하려면 initArgs<userProfileCity>을 실제 도시 값이 포함된 string(예: Bettles)으로 바꿉니다.

1db.restaurants.aggregate([
2{
3 $group :
4 {
5 _id : { city: "$city" },
6 restaurants:
7 {
8 $accumulator:
9 {
10 init: function(city, userProfileCity) { // Set the initial state
11 return {
12 max: city === userProfileCity ? 3 : 1, // If the group matches the user's city, return 3 restaurants
13 restaurants: [] // else, return 1 restaurant
14 }
15 },
16
17 initArgs: ["$city", <userProfileCity>], // Argument to pass to the init function
18
19 accumulate: function(state, restaurantName) { // Define how to update the state
20 if (state.restaurants.length < state.max) {
21 state.restaurants.push(restaurantName);
22 }
23 return state;
24 },
25
26 accumulateArgs: ["$name"], // Argument required by the accumulate function
27
28 merge: function(state1, state2) {
29 return {
30 max: state1.max,
31 restaurants: state1.restaurants.concat(state2.restaurants).slice(0, state1.max)
32 }
33 },
34
35 finalize: function(state) { // Adjust the state to only return field we need
36 return state.restaurants
37 }
38
39 lang: "js"
40 }
41 }
42 }
43}
44])

userProfileCity의 값이 Bettles인 경우, 연산은 다음과 같은 결과를 반환합니다.

{ "_id" : { "city" : "Bettles" }, "restaurants" : { "restaurants" : [ "Food Fury", "Meal Macro", "Big Crisp" ] } }
{ "_id" : { "city" : "Onida" }, "restaurants" : { "restaurants" : [ "The Wrap" ] } }
{ "_id" : { "city" : "Pyote" }, "restaurants" : { "restaurants" : [ "Crave" ] } }

userProfileCity의 값이 Onida인 경우, 연산은 다음과 같은 결과를 반환합니다.

{ "_id" : { "city" : "Bettles" }, "restaurants" : { "restaurants" : [ "Food Fury" ] } }
{ "_id" : { "city" : "Onida" }, "restaurants" : { "restaurants" : [ "The Wrap", "Spice Attack", "Soup City" ] } }
{ "_id" : { "city" : "Pyote" }, "restaurants" : { "restaurants" : [ "Crave" ] } }

userProfileCity의 값이 Pyote인 경우, 연산은 다음과 같은 결과를 반환합니다.

{ "_id" : { "city" : "Bettles" }, "restaurants" : { "restaurants" : [ "Food Fury" ] } }
{ "_id" : { "city" : "Onida" }, "restaurants" : { "restaurants" : [ "The Wrap" ] } }
{ "_id" : { "city" : "Pyote" }, "restaurants" : { "restaurants" : [ "Crave", "The Gala" ] } }

userProfileCity 값이 다른 값인 경우 이 작업은 다음 결과를 반환합니다.

{ "_id" : { "city" : "Bettles" }, "restaurants" : { "restaurants" : [ "Food Fury" ] } }
{ "_id" : { "city" : "Onida" }, "restaurants" : { "restaurants" : [ "The Wrap" ] } }
{ "_id" : { "city" : "Pyote" }, "restaurants" : { "restaurants" : [ "Crave" ] } }

init 함수는 maxrestaurants 필드가 포함된 초기 상태를 정의합니다. max 필드는 해당 그룹의 최대 레스토랑 수를 설정합니다. 문서의 city 필드가 userProfileCity와 일치하는 경우 해당 그룹에는 최대 3개의 레스토랑이 포함됩니다. 그렇지 않고 문서 _iduserProfileCity와 일치하지 않으면 그룹에는 최대 1개의 레스토랑이 포함됩니다. init 함수는 cityuserProfileCity 인수 모두를 initArgs 배열에서 받습니다.

$accumulator가 처리하는 각 문서에 대해 레스토랑의 namerestaurants 배열로 푸시합니다. 단 해당 이름의 길이가 restaurants 길이의 max 값을 넘지 않아야 합니다. 문서가 처리될 때마다 accumulate 함수는 업데이트된 상태를 반환합니다.

병합 함수는 두 상태를 병합하는 방법을 정의합니다. 이 함수는 restaurant 각 상태의 배열을 함께 연결하고, 결과 배열의 길이는 slice() 메서드를 사용하여 max 값을 초과하지 않는지 확인합니다.

모든 문서가 처리되면 finalize 함수는 레스토랑 이름만 반환하도록 결과 상태를 수정합니다. 이 기능이 없으면 max 필드도 출력에 포함되어 해당 애플리케이션에 대한 요구 사항을 충족하지 못합니다.

돌아가기

$abs

이 페이지의 내용