Tổng hợp

Mô tả

Giai đoạn aggregate tính toán kết quả tổng hợp (ví dụ: số lượng, tổng) từ các tài liệu do giai đoạn trước trả về.

Nếu bạn cung cấp biểu thức nhóm, thì biểu thức này sẽ nhóm các tài liệu dựa trên biểu thức đã cung cấp, sau đó áp dụng các hàm tích luỹ cho từng nhóm.

Cú pháp

Đối với các phép tổng hợp không có group-by, giai đoạn aggregate sẽ lấy một hoặc nhiều biểu thức tổng hợp được đặt biệt hiệu:

Node.js

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

Đối với các phép tổng hợp có nhóm, cần có các nhóm bổ sung ngoài các đơn vị tập hợp:

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

Hành vi

Tổng hợp không phân nhóm

Tạo một bộ sưu tập cities bằng các tài liệu sau:

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

Để biết tổng số thành phố và dân số trung bình của các thành phố đó:

Node.js

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

tạo ra:

{avg_population: 5100000, total: 5}

Thực hiện các phép tổng hợp trên nhóm

Bằng cách cung cấp đối số groups, bạn có thể thực hiện các phép tổng hợp trên từng nhóm riêng biệt.

Ví dụ: để tìm thành phố có dân số lớn nhất ở mỗi quốc gia và mỗi tiểu bang:

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

tức là:

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

Biểu thức phức tạp về việc nhóm

Ngoài việc chỉ nhóm theo giá trị trường, giai đoạn aggregate còn hỗ trợ việc nhóm theo kết quả của các biểu thức phức tạp. Bạn có thể dùng mọi biểu thức hợp lệ trong giai đoạn select làm khoá nhóm. Điều này cho phép bạn nhóm một cách linh hoạt dựa trên các giá trị hoặc điều kiện được tính toán.

Ví dụ: để nhóm theo trường trạng thái có giá trị rỗng hay không và tìm ra tổng dân số trong mỗi nhóm:

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

sẽ trả về:

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

Hành vi của đơn vị tổng hợp

Bạn có thể xem hành vi tổng hợp của từng hàm được hỗ trợ (ví dụ: count, sum, avg) trong trang dành riêng cho Hàm tổng hợp.

Các hành vi chính của nhóm

Khi nhóm tài liệu, Firestore sử dụng ngữ nghĩa về tính bằng nhau để xác định xem các giá trị có thuộc cùng một nhóm hay không.

Điều này có nghĩa là các giá trị tương đương (ví dụ: các giá trị số học tương đương), bất kể loại ban đầu (số nguyên 32 bit, số nguyên 64 bit, số dấu phẩy động, decimal128, v.v.) đều được nhóm lại với nhau.

Ví dụ: trong một tập hợp numerics có nhiều tài liệu chứa các giá trị foo lần lượt là số nguyên 32 bit 1, số nguyên 64 bit 1L và số dấu phẩy động 1.0, tất cả các giá trị này sẽ được tích luỹ vào cùng một nhóm. Chạy một nhóm đếm theo foo sẽ trả về:

{foo: 1.0, count: 3}

Trong những trường hợp có nhiều giá trị tương đương trong tập dữ liệu, giá trị đầu ra của nhóm có thể là bất kỳ giá trị nào trong số các giá trị tương đương này. Trong ví dụ này, foo có thể là 1, 1L hoặc 1.0.

Ngay cả khi có vẻ như là xác định, bạn không nên cố gắng dựa vào hành vi của một giá trị cụ thể được chọn.

Mức sử dụng bộ nhớ

Cách thực hiện việc tổng hợp sẽ phụ thuộc vào các chỉ mục có sẵn. Khi trình tối ưu hoá truy vấn không chọn được chỉ mục phù hợp, tổng hợp cần phải đệm tất cả các nhóm trong bộ nhớ.

Trong trường hợp có số lượng nhóm rất lớn hoặc mỗi nhóm có kích thước rất lớn (ví dụ: nhóm theo các giá trị lớn), giai đoạn này có thể hết bộ nhớ.

Trong những trường hợp như vậy, bạn nên áp dụng bộ lọc để giới hạn tập dữ liệu cần tổng hợp, nhóm trên các trường nhỏ hơn/ít hơn hoặc tạo chỉ mục theo đề xuất để tránh sử dụng nhiều bộ nhớ. Tính năng Giải thích truy vấn sẽ cung cấp thông tin về kế hoạch thực thi truy vấn thực tế và dữ liệu lập hồ sơ để hỗ trợ gỡ lỗi.