Zbiorczo

Opis

Etap aggregate oblicza zagregowane wyniki (np. liczbę lub sumę) na podstawie dokumentów zwróconych przez poprzedni etap.

Opcjonalnie, gdy podane jest wyrażenie grupowania, grupuje dokumenty na podstawie podanych wyrażeń, a następnie stosuje funkcje akumulatora do każdej grupy.

Składnia

W przypadku agregacji bez grupowania etap aggregate przyjmuje co najmniej 1 wyrażenie agregatora z aliasem:

Node.js

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

W przypadku agregacji z grupowaniem potrzebne są dodatkowe grupy oprócz agregatorów:

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

Zachowanie

Agregacje bez grupowania

Utwórz kolekcję cities z tymi dokumentami:

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

Aby sprawdzić łączną liczbę miast i średnią liczbę mieszkańców:

Node.js

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

która daje:

{avg_population: 5100000, total: 5}

Wykonywanie agregacji w grupach

Podając argument groups, możesz przeprowadzać agregacje w każdej odrębnej grupie.

Aby na przykład znaleźć miasto o największej liczbie mieszkańców w każdym kraju i stanie:

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

co daje:

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

Złożone wyrażenia w grupowaniu

Oprócz grupowania tylko według wartości pól etap aggregate obsługuje grupowanie według wyników złożonych wyrażeń. Jako klucza grupowania można użyć dowolnego wyrażenia, które jest prawidłowe na etapie select. Umożliwia to elastyczne grupowanie na podstawie obliczonych wartości lub warunków.

Aby na przykład pogrupować dane według tego, czy pole stanu ma wartość null, i sprawdzić łączną liczbę mieszkańców w każdej grupie:

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

zwróci:

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

Zachowania agregatora

Zachowanie agregacji każdej obsługiwanej funkcji (np. count, sum, avg) można znaleźć na stronie poświęconej funkcjom agregującym.

Najważniejsze zachowania grupy

Podczas grupowania dokumentów Firestore używa semantyki równości, aby określić, czy wartości należą do tej samej grupy.

Oznacza to, że równoważne wartości, np. równoważne matematycznie wartości liczbowe, niezależnie od pierwotnego typu (32-bitowa liczba całkowita, 64-bitowa liczba całkowita, liczby zmiennoprzecinkowe, decimal128 itp.), są grupowane razem.

Na przykład w kolekcji numerics z różnymi dokumentami zawierającymi wartości foo w postaci 32-bitowej liczby całkowitej 1, 64-bitowej liczby całkowitej 1L i liczby zmiennoprzecinkowej 1.0 zostaną one zgrupowane w tej samej grupie. Uruchomienie zliczania grupowania według foo zwróci:

{foo: 1.0, count: 3}

W takich przypadkach, gdy w zbiorze danych występują różne równoważne wartości, wartość wyjściowa grupy może być dowolną z nich. W tym przykładzie foo może być równe 1, 1L lub 1.0.

Nawet jeśli wydaje się deterministyczne, nie należy polegać na zachowaniu polegającym na wyborze jednej konkretnej wartości.

Wykorzystanie pamięci

Sposób wykonania agregacji zależy od dostępnych indeksów. Jeśli optymalizator zapytań nie wybierze odpowiedniego indeksu, agregat musi buforować wszystkie grupy w pamięci.

Jeśli masz bardzo dużą liczbę grup lub każda grupa jest bardzo duża (np. grupowanie według ogromnych wartości), na tym etapie może zabraknąć pamięci.

W takich przypadkach zastosuj filtry, aby ograniczyć zbiór danych do agregacji, grupuj według mniejszej liczby pól lub twórz indeksy zgodnie z zaleceniami, aby uniknąć dużego zużycia pamięci. Funkcja Query Explain dostarczy informacji o rzeczywistym planie wykonywania zapytania i danych profilowania, które pomogą w debugowaniu.