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.

Ví dụ

Đố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 bộ tổng 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 mà không cầ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 một đố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 nhóm theo các 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 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ố tương đương về mặt toán học), 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ó các giá trị tương đương khác nhau trong tập dữ liệu, giá trị đầu ra của nhóm có thể là bất kỳ giá trị tương đương nào trong số 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.