集計

説明

aggregate ステージは、前のステージで返されたドキュメントから集計結果(カウント、合計など)を計算します。

必要に応じて、グループ化式が指定されている場合は、指定された式に基づいてドキュメントをグループ化し、各グループにアキュムレータ関数を適用します。

構文

グループ化を使用しない集計の場合、aggregate ステージは 1 つ以上のエイリアス付きアグリゲータ式を受け取ります。

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 ステージで有効な式を、グループ化キーとして使用できます。これにより、計算値や条件に基づいて柔軟にグループ化できます。

たとえば、州フィールドが 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}

アグリゲータの動作

サポートされている各関数(countsumavg など)の集計動作については、集計関数専用のページをご覧ください。

グループ キーの動作

ドキュメントをグループ化する場合、Firestore は等価セマンティクスを使用して、値が同じグループに属するかどうかを判断します。

つまり、元の型(32 ビット整数、64 ビット整数、浮動小数点数、decimal128 など)に関係なく、同等の値(数学的に同等の数値など)はすべてグループ化されます。

たとえば、32 ビット整数 1、64 ビット整数 1L、浮動小数点数 1.0foo 値が格納されたさまざまなドキュメントを含むコレクション numerics では、これらはすべて同じグループに集計されます。foo でグループ化してカウントを実行すると、次の結果が返されます。

{foo: 1.0, count: 3}

データセットに異なる同等の値が存在する場合、グループの出力値はこれらの同等の値のいずれかになります。 この例では、foo11L、または 1.0 になります。

決定的に見える場合でも、特定の値が選択される動作に依存しようとすべきではありません

メモリ使用量

集計の実行方法は、使用可能なインデックスによって異なります。クエリ オプティマイザーによって適切なインデックスが選択されていない場合、集計はメモリ内のすべてのグループをバッファリングする必要があります。

グループの数が非常に多い場合や、各グループが非常に大きい場合(非常に大きな値でグループ化しているなど)、このステージでメモリ不足が発生する可能性があります。

このような場合は、フィルタを適用して集計するデータセットを制限するか、より小さいまたは少ないフィールドでグループ化するか、推奨されるようにインデックスを作成して、メモリ使用量を抑える必要があります。Query Explain では、実際のクエリ実行プランとプロファイリング データに関する情報が提供され、デバッグに役立ちます。