Zbiorczo

Opis

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

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

Przykłady

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 oprócz agregatorów przyjmuje dodatkowe grupy:

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 dowiedzieć się, jaka jest łączna liczba miast i średnia liczba ich mieszkańców:

Node.js

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

który generuje:

{avg_population: 5100000, total: 5}

Przeprowadzanie agregacji w grupach

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

Aby na przykład znaleźć miasto z największą liczbą mieszkańców w każdym kraju i każdym 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();

który 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 klucz 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ć według tego, czy pole stanu ma wartość null, i dowiedzieć się, jaka jest łączna liczba 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 agregatorów

Informacje o zachowaniu agregacji każdej obsługiwanej funkcji (np. count, sum, avg) znajdziesz na stronie poświęconej funkcjom agregacji.

Zachowania kluczy grup

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 (liczba całkowita 32-bitowa, liczba całkowita 64-bitowa, liczby zmiennoprzecinkowe, decimal128 itp.), są grupowane razem.

Na przykład w kolekcji numerics z różnymi dokumentami zawierającymi foo wartości: liczbę całkowitą 32-bitową 1, liczbę całkowitą 64-bitową 1L i reprezentację zmiennoprzecinkową 1.0, wszystkie te wartości zostaną zgromadzone w tej samej grupie. Uruchomienie grupowania zliczania 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 tych równoważnych wartości. W tym przykładzie foo może mieć wartość 1, 1L lub 1.0.

Nawet jeśli wydaje się to deterministyczne, nie należy polegać na zachowaniu polegającym na wybraniu 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, agregacja będzie musiała buforować wszystkie grupy w pamięci.

W przypadku bardzo dużej liczby grup lub bardzo dużej liczby elementów w każdej grupie (np. grupowania według bardzo dużych wartości) ten etap może wyczerpać pamięć.

W takich przypadkach należy zastosować filtry, aby ograniczyć zbiór danych do agregacji, grupować według mniejszej liczby pól lub utworzyć indeksy zgodnie z zaleceniami, aby uniknąć dużego zużycia pamięci. Funkcja Query Explain zawiera informacje o rzeczywistym planie wykonania zapytania i dane profilowania, które ułatwiają debugowanie.