Aggregazione

Descrizione

La fase aggregate calcola i risultati aggregati (ad es. conteggio, somma) dai documenti restituiti dalla fase precedente.

Se viene fornita un'espressione di raggruppamento, i documenti vengono raggruppati in base alle espressioni fornite e poi vengono applicate le funzioni di accumulatore a ogni gruppo.

Sintassi

Per le aggregazioni senza raggruppamento, la fase aggregate accetta una o più espressioni di aggregazione con alias:

Node.js

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

Per gli aggregatori con raggruppamento, sono necessari gruppi aggiuntivi oltre agli aggregatori:

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();

Comportamento

Aggregazioni senza raggruppamento

Crea una raccolta cities con i seguenti documenti:

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});

Per scoprire il numero totale di città e la loro popolazione media:

Node.js

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

che produce:

{avg_population: 5100000, total: 5}

Eseguire aggregazioni sui gruppi

Fornendo un argomento groups, puoi eseguire aggregazioni su ogni gruppo distinto.

Ad esempio, per trovare la città con la popolazione più numerosa in ogni paese e in ogni stato:

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();

che dà come risultato:

{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}

Espressioni complesse nel raggruppamento

Oltre al raggruppamento in base ai valori dei campi, la fase aggregate supporta il raggruppamento in base ai risultati di espressioni complesse. Qualsiasi espressione valida in una fase select può essere utilizzata come chiave di raggruppamento. Ciò consente un raggruppamento flessibile in base a valori o condizioni calcolati.

Ad esempio, per raggruppare in base al valore Null del campo stato e scoprire la popolazione totale in ogni gruppo:

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();

restituirà:

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

Comportamenti degli aggregatori

Il comportamento di aggregazione di ogni funzione supportata (ad es. count, sum, avg) è disponibile nella pagina dedicata alle funzioni di aggregazione.

Comportamenti principali del gruppo

Quando raggruppa i documenti, Firestore utilizza la semantica di uguaglianza per determinare se i valori appartengono allo stesso gruppo.

Ciò significa che i valori equivalenti, ad esempio i valori numerici matematicamente equivalenti, indipendentemente dal tipo originale (intero a 32 bit, intero a 64 bit, numeri in virgola mobile, decimal128 e così via), vengono raggruppati.

Ad esempio, in una raccolta numerics con documenti diversi contenenti valori foo di numeri interi a 32 bit 1, numeri interi a 64 bit 1L e numeri in virgola mobile 1.0 rispettivamente, verranno tutti accumulati nello stesso gruppo. L'esecuzione di un conteggio raggruppato per foo restituirà:

{foo: 1.0, count: 3}

In questi casi, in cui nel set di dati sono presenti valori equivalenti diversi, il valore di output del gruppo può essere uno qualsiasi di questi valori equivalenti. In questo esempio, foo potrebbe essere 1, 1L o 1.0.

Anche se sembra deterministico, non devi tentare di fare affidamento sul comportamento di un valore specifico selezionato.

Utilizzo memoria

La modalità di esecuzione dell'aggregazione dipende dagli indici disponibili. Quando l'ottimizzatore di query non sceglie un indice appropriato, l'aggregato deve memorizzare nel buffer tutti i gruppi nella memoria.

Nel caso di un numero molto elevato di gruppi o di gruppi molto grandi (ad es. raggruppamento per valori enormi), questa fase potrebbe esaurire la memoria.

In questi casi, devi applicare filtri per limitare il set di dati su cui eseguire l'aggregazione, raggruppare i dati in base a un numero inferiore di campi o campi più piccoli oppure creare indici come consigliato per evitare un utilizzo elevato della memoria. Query Explain fornisce informazioni sul piano di esecuzione della query effettivo e sui dati di profilazione per facilitare il debug.