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 daje:

{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 o największej populacji 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 populacja 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

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

Zachowania klucza 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 (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 liczby całkowitej 32-bitowej 1, liczby całkowitej 64-bitowej 1L i reprezentacji zmiennoprzecinkowej 1.0 odpowiednio, 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. Gdy optymalizator zapytań nie wybierze odpowiedniego indeksu, agregacja musi buforować wszystkie grupy w pamięci.

W przypadku bardzo dużej liczby grup lub bardzo dużej liczby grup (np. grupowania według 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 tworzyć 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.