説明
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}
アグリゲータの動作
サポートされている各関数(count、sum、avg など)の集計動作については、集計関数専用のページをご覧ください。
グループ キーの動作
ドキュメントをグループ化する場合、Firestore は等価セマンティクスを使用して、値が同じグループに属するかどうかを判断します。
つまり、元の型(32 ビット整数、64 ビット整数、浮動小数点数、decimal128 など)に関係なく、同等の値(数学的に同等の数値など)はすべてグループ化されます。
たとえば、32 ビット整数 1、64 ビット整数 1L、浮動小数点数 1.0 の foo 値が格納されたさまざまなドキュメントを含むコレクション numerics では、これらはすべて同じグループに集計されます。foo でグループ化してカウントを実行すると、次の結果が返されます。
{foo: 1.0, count: 3}
データセットに異なる同等の値が存在する場合、グループの出力値はこれらの同等の値のいずれかになります。
この例では、foo は 1、1L、または 1.0 になります。
決定的に見える場合でも、特定の値が選択される動作に依存しようとすべきではありません。
メモリ使用量
集計の実行方法は、使用可能なインデックスによって異なります。クエリ オプティマイザーによって適切なインデックスが選択されていない場合、集計はメモリ内のすべてのグループをバッファリングする必要があります。
グループの数が非常に多い場合や、各グループが非常に大きい場合(非常に大きな値でグループ化しているなど)、このステージでメモリ不足が発生する可能性があります。
このような場合は、フィルタを適用して集計するデータセットを制限するか、より小さいまたは少ないフィールドでグループ化するか、推奨されるようにインデックスを作成して、メモリ使用量を抑える必要があります。Query Explain では、実際のクエリ実行プランとプロファイリング データに関する情報が提供され、デバッグに役立ちます。