Reference & Engineering Notes

    Trang tra cứu: cấu hình, ví dụ API, mẫu code, thông báo lỗi, thuật ngữ và lệnh thường dùng. Nhập từ khóa vào ô tìm kiếm để lọc nội dung.

    Trần Ngọc Đăng KhôiChương 06 / 0710 phút đọc
    Mục lục chương
      Ý chính của chươngChương này dùng để tra cứu, không cần đọc tuần tự. Có thể mở chương song song với editor để tìm và sao chép mục cần thiết.
      06.1

      Cấu hình hệ thống

      Cấu hình nằm trong cube.js, hoặc cube.py từ v0.34.

      Ví dụ dưới đây thực hiện hai chức năng. Hàm queryRewrite tự động thêm filter tenant từ securityContext vào mọi truy vấn, nên mỗi tenant chỉ thấy dữ liệu của mình (RLS). Hàm scheduledRefreshContexts liệt kê các tenant để refresh worker build pre-aggregations riêng cho từng tenant.

      cube.js
      cube.js · query_rewrite theo tenant
      module.exports = {
        queryRewrite: (query, { securityContext }) => {
          // inject tenant filter (RLS) into every query
          if (securityContext && securityContext.tenant_id) {
            query.filters.push({
              member: 'orders.tenant_id',
              operator: 'equals',
              values: [securityContext.tenant_id]
            });
          }
          return query;
        },
        scheduledRefreshContexts: () => [
          { securityContext: { tenant_id: 'tenant_a' } },
          { securityContext: { tenant_id: 'tenant_b' } }
        ]
      };
      cube.py
      cube.py · từ v0.34
      def query_rewrite(query, context):
          security_context = context.get('securityContext', {})
          if 'tenant_id' in security_context:
              query['filters'].append({
                  'member': 'orders.tenant_id',
                  'operator': 'equals',
                  'values': [security_context['tenant_id']]
              })
          return query

      Bản Python trong handbook chỉ có thân hàm. Cách đăng ký hàm với Cube trong cube.py nên đối chiếu tài liệu chính thức của phiên bản đang dùng.

      Hình 1Cùng một query_rewrite, viết bằng JavaScript và Python.
      06.2

      Ví dụ API và truy vấn

      Cùng một truy vấn “doanh thu theo vùng”, viết cho ba API.

      SQL API dùng phương ngữ SQL tương thích Postgres, gọi là Semantic SQL; measure được lấy qua hàm MEASURE(). REST API nhận JSON tại POST /cubejs-api/v1/load, trục thời gian được khai báo trong timeDimensions. GraphQL API dùng tên camelCase và lọc thời gian bằng inDateRange.

      SQL API
      Semantic SQL · cổng Postgres
      SELECT
        "orders".region AS "region",
        MEASURE("orders".total_sales) AS "total_sales"
      FROM "orders" AS "orders"
      WHERE "orders".order_date >= '2026-01-01'
      GROUP BY 1
      ORDER BY 2 DESC;
      REST API
      POST /cubejs-api/v1/load · Authorization: <JWT_TOKEN>
      {
        "query": {
          "measures": ["orders.total_sales"],
          "dimensions": ["orders.region"],
          "timeDimensions": [{
            "dimension": "orders.order_date",
            "dateRange": ["2026-01-01", "2026-12-31"],
            "granularity": "month"
          }],
          "order": { "orders.total_sales": "desc" }
        }
      }
      GraphQL API
      GraphQL
      query LoadSalesData {
        cube(where: { orders: { orderDate: { inDateRange: ["2026-01-01", "2026-12-31"] } } }) {
          orders {
            region
            totalSales
            orderDate {
              month
            }
          }
        }
      }
      Hình 2Chuyển tab để so sánh. Ví dụ REST và GraphQL chia theo tháng, còn ví dụ SQL không chia theo tháng.
      06.3

      Mẫu code thường dùng

      Cấu trúc thư mục chuẩn, phép chia an toàn với NULLIF, và segment nhiều điều kiện.

      Từ v0.33, Cube đưa ra cấu trúc thư mục theo quy ước: model/cubes cho cube và model/views cho view. Đây là khuyến nghị, không bắt buộc. Hai mẫu YAML dưới đây thường được tái sử dụng.

      Cấu trúc thư mục
      model/
      ├── cubes/
      │   └── orders.yml
      └── views/
          └── sales_mart.yml
      Safe division · NULLIF
      cubes:
        - name: sales_metrics
          sql_table: public.sales
      
          measures:
            - name: total_revenue
              sql: revenue
              type: sum
      
            - name: total_orders
              type: count
      
            - name: average_order_value
              type: number
              sql: "{total_revenue} * 1.0 / NULLIF({total_orders}, 0)"
      Segment nhiều điều kiện
      segments:
        - name: high_value_completed
          sql: "{CUBE}.status = 'completed' AND {CUBE}.amount > 1000"
      Hình 3Đặt số đơn bằng 0 và bỏ NULLIF để thấy vai trò của hàm này. Số liệu minh họa.
      Ghi nhớNULLIF(x, 0) trả về NULL khi x bằng 0. Phép chia cho NULL trả về NULL thay vì báo lỗi, nên truy vấn vẫn thực thi bình thường.
      06.4

      Thông báo lỗi và cách sửa

      Ba lỗi thường gặp nhất và cú pháp sửa tương ứng.

      Chọn lỗi để xem nguyên nhân và cách sửa: thiếu FILTER_GROUP, build pre-aggregation bị timeout (cần export bucket), hoặc sai tên member. Khi tìm kiếm, cả ba lỗi được hiển thị đồng thời.

      Filter group error
      Nguyên nhânDùng nhiều FILTER_PARAMS nhưng không bọc trong FILTER_GROUP.
      Cách sửaBọc các điều kiện bằng FILTER_GROUP trong SQL nguồn.
      sql
      WHERE ${FILTER_GROUP(FILTER_PARAMS.a.filter('a'), FILTER_PARAMS.b.filter('b'))}
      Pre-aggregation building error
      Nguyên nhânQuét dữ liệu thô vượt quá timeout của data warehouse khi build rollup.
      Cách sửaCấu hình export bucket trong cấu hình driver của data warehouse. Từ v0.28, external: true đã là mặc định nên không cần khai báo thêm.
      Unknown member 'Orders.count'
      Nguyên nhânSai chữ hoa/thường, hoặc tên cũ còn sót sau khi chuyển sang quy ước v0.33.
      Cách sửaDùng tên snake_case đúng như khai báo: Orders.count → orders.count.
      Hình 4Lỗi → nguyên nhân → cú pháp sửa.
      06.5

      Checklist khi kết quả sai hoặc chậm

      Kiểm tra lần lượt SQL sinh ra, trạng thái cache và múi giờ.

      Áp dụng khi số liệu sai lệch hoặc P95 tăng đột ngột. Đánh dấu từng bước sau khi kiểm tra xong.

      Hình 5Ba bước, sắp theo chi phí kiểm tra tăng dần. Trạng thái không được lưu.
      Ghi nhớKhi cache miss, đối chiếu truy vấn với định nghĩa pre-aggregation; refresh_key không quyết định việc khớp. Khi lệch múi giờ, tìm convertTz() trên dimension đang dùng trong timeDimensions.
      06.6

      Thuật ngữ

      Tám thuật ngữ xuất hiện nhiều nhất trong handbook.

      Người đọc có thể tự đoán nghĩa trước, sau đó chọn thẻ để xem định nghĩa. Ba thuật ngữ liên quan chặt chẽ với nhau: pre-aggregations được lưu trong Cube Store, và aggregate awareness là cơ chế Cube dùng để tự chọn đúng bảng pre-aggregation khi trả lời truy vấn.

      Chọn thẻ để lật
      Hình 6Glossary dạng thẻ lật.
      06.7

      Lệnh và công cụ

      Health check, kiểm tra schema, mở Playground.

      Endpoint /readyz cho biết Cube đã sẵn sàng nhận request. Handbook ghi hai lệnh cubejs-cli check và cubejs-cli dev-playground, nhưng Cube CLI không có hai lệnh này; lệnh bên dưới lấy theo mã nguồn của cubejs-cli và @cubejs-backend/server. Tên lệnh có thể khác giữa các phiên bản; chạy --help để xác nhận trước khi đưa vào CI.

      Health check API
      curl http://localhost:4000/readyz
      Kiểm tra cú pháp schema (linting)
      npx cubejs-cli validate
      Chạy Developer Playground (dev mode)
      npx cubejs-dev-server
      Hình 7Nhấn Copy để sao chép lệnh.
      06.8

      Tài liệu tham khảo

      Bốn nguồn để đối chiếu khi handbook thiếu thông tin hoặc đã lỗi thời.

      Cube thay đổi nhanh giữa các phiên bản. Với cấu hình và tên biến môi trường, tài liệu chính thức đáng tin cậy hơn handbook này.

      Hình 8Liên kết mở trong tab mới.
      Chương tiếp theo07 · Applied Case Studies→
      Không tìm thấy mục nào khớp. Có thể dùng từ khóa ngắn hơn, ví dụ “lỗi”, “jwt”, “readyz”.