집계

설명

aggregate 단계는 이전 단계에서 반환된 문서에서 집계된 결과(예: 개수, 합계)를 계산합니다.

선택적으로 그룹화 표현식이 제공되면 제공된 표현식을 기반으로 문서를 그룹화한 다음 각 그룹에 누산기 함수를 적용합니다.

구문

그룹화가 없는 집계의 경우 aggregate 단계는 하나 이상의 별칭 집계기 표현식을 사용합니다.

Node.js

const cities = await db.pipeline()
  .collection("/cities")
  .aggregate(
      countAll().as("total"),
      average("population").as("avg_population")
  )
  .execute();

그룹화가 있는 집계의 경우 집계기 외에 추가 그룹이 필요합니다.

Node.js

const result = await db.pipeline()
  .collectionGroup('citites')
  .aggregate({
    accumulators: [
      countAll().as('cities'),
      field('population').sum().as('total_popoluation')
    ],
    groups: [field('location.state').as('state')]
  })
  .execute();

동작

그룹화 없는 집계

다음 문서로 cities 컬렉션을 만듭니다.

Node.js

await db.collection('cities').doc('SF').set({name: 'San Francisco', state: 'CA', country: 'USA', population: 870000});
await db.collection('cities').doc('LA').set({name: 'Los Angeles', state: 'CA', country: 'USA', population: 3970000});
await db.collection('cities').doc('NY').set({name: 'New York', state: 'NY', country: 'USA', population: 8530000});
await db.collection('cities').doc('TOR').set({name: 'Toronto', state: null, country: 'Canada', population: 2930000});
await db.collection('cities').doc('MEX').set({name: 'Mexico City', state: null, country: 'Mexico', population: 9200000});

총 도시 수와 각 도시의 평균 인구를 알아보려면 다음 단계를 따르세요.

Node.js

const cities = await db.pipeline()
  .collection("/cities")
  .aggregate(
      countAll().as("total"),
      average("population").as("avg_population")
  )
  .execute();

그러면 다음과 같은 결과가 표시됩니다.

{avg_population: 5100000, total: 5}

그룹에서 집계 수행

groups 인수를 제공하면 각 고유 그룹에 대해 집계를 수행할 수 있습니다.

예를 들어 각 국가와 각 주에서 인구가 가장 많은 도시를 찾으려면 다음 단계를 따르세요.

Node.js

const cities = await db.pipeline()
  .collection("/cities")
  .aggregate({
      accumulators: [
          countAll().as("number_of_cities"),
          maximum("population").as("max_population")
      ],
      groups: ["country", "state"]
  })
  .execute();

다음과 같은 결과가 나옵니다.

{country: "USA", state: "CA", max_population: 3970000, number_of_cities: 2},
{country: "USA", state: "NY", max_population: 8530000, number_of_cities: 1},
{country: "Canada", state: null, max_population: 2930000, number_of_cities: 1},
{country: "Mexico", state: null,  max_population: 9200000, number_of_cities: 1}

그룹화에 대한 복잡한 표현식

필드 값으로만 그룹화하는 것 외에도 aggregate 단계에서는 복잡한 표현식의 결과로 그룹화하는 것을 지원합니다. select 단계에서 유효한 표현식은 그룹화 키로 사용할 수 있습니다. 이를 통해 계산된 값 또는 조건을 기반으로 유연하게 그룹화할 수 있습니다.

예를 들어 state 필드가 null인지 여부로 그룹화하고 각 그룹의 총 인구를 알아보려면 다음 단계를 따르세요.

Node.js

const cities = await db.pipeline()
  .collection("/cities")
  .aggregate({
      accumulators: [
         sum("population").as("total_population")
      ],
      groups: [equal(field("state"), null).as("state_is_null")]
  })
  .execute();

다음 결과가 반환됩니다.

{state_is_null: true, total_population: 12130000}
{state_is_null: false, total_population: 13370000}

집계기 동작

지원되는 각 함수(예: count, sum, avg)의 집계 동작은 집계 함수 전용 페이지에서 확인할 수 있습니다.

그룹 핵심 동작

Firestore는 문서를 그룹화할 때 동등성 시맨틱을 사용하여 값이 동일한 그룹에 속하는지 확인합니다.

즉, 원래 유형(32비트 정수, 64비트 정수, 부동 소수점 숫자, decimal128 등)에 관계없이 수학적으로 동등한 숫자 값과 같은 동등한 값이 모두 함께 그룹화됩니다.

예를 들어 32비트 정수 1, 64비트 정수 1L, 부동 소수점 숫자 1.0foo 값이 각각 포함된 여러 문서가 있는 컬렉션 numerics에서 이들은 모두 동일한 그룹으로 누적됩니다. foo로 그룹화하여 개수를 실행하면 다음이 반환됩니다.

{foo: 1.0, count: 3}

데이터 세트에 서로 다른 동등한 값이 있는 경우 그룹의 출력 값은 이러한 동등한 값 중 하나가 될 수 있습니다. 이 예에서 foo1, 1L 또는 1.0일 수 있습니다.

결정론적인 것처럼 보이더라도 특정 값이 선택되는 동작에 의존하려고 하면 안 됩니다.

메모리 사용량

집계가 실행되는 방식은 사용 가능한 색인에 따라 다릅니다. 쿼리 최적화 도구에서 적절한 색인을 선택하지 않으면 집계는 메모리에 모든 그룹을 버퍼링해야 합니다.

그룹 수가 매우 많거나 각 그룹이 매우 큰 경우(예: 큰 값으로 그룹화) 이 단계에서 메모리가 부족할 수 있습니다.

이러한 경우 필터를 적용하여 집계할 데이터 세트를 제한하거나, 더 적은 필드로 그룹화하거나, 권장되는 대로 색인을 만들어 메모리 사용량이 많아지는 것을 방지해야 합니다. 쿼리 설명은 디버깅에 도움이 되는 실제 쿼리 실행 계획 및 프로파일링 데이터에 대한 정보를 제공합니다.