Creación de módulos de productos de datos

Se requiere la creación de un módulo de producto de datos personalizado para definir tu propia lógica empresarial y modelos analíticos, lo que te permite ejecutar cálculos en tus tablas de base y empaquetarlos en conjuntos de datos implementables.

Requisitos previos

Cuando crees un módulo de producto de datos personalizado, te recomendamos que uses un espacio de nombres personalizado exclusivo para empaquetarlo. Además, asegúrate de que la tabla de origen que planeas usar exista en el conjunto de datos de la base de datos.

Creación de un módulo de producto de datos

La definición del módulo de producto de datos requiere los siguientes pasos:

  • Registro del módulo de producto de datos en el archivo config/config.yaml, extendiendo la lista data.modules.products con la entrada:
[...]
data:
  [...]
  # Configuration for data foundation and product modules.
  modules:
    # List of foundation modules.
    foundation:
      [...]
    # List of data product modules.
    product:
      [...]
      - moduleId:  product_module_id
        type:  custom_namespace.flight_usd
        dependsOn:
          sapModule: erp
          sapModuleCustNS:  foundation_module_id
        dataTargetId: product_target
        enabled: true
        tableSettings: "table_settings.yaml"
        # Optional, references file in `config/custom_namespace_path/data_product/product_module_id/`
        # If omitted, defaults to src/data_modules/custom_namespace_path/data_product/table_settings.default.yaml.
[...]
  • Creación del archivo tableSettings (p.ej., config/custom_namespace_path/data_product/product_module_id/table_settings.yaml). Este archivo YAML controla la configuración de la tabla, como las materializaciones y los detalles de optimización de BigQuery:
common:
  custom_sales_summary:
    materialization_type: "table"
    tags: ["custom", "sales", "reporting"]
    partition_details:
      column: "created_date"
      partition_type: "date"
      time_grain: "day"
    cluster_details:
      columns:
        - "customer_id"
  • Creación de un archivo de anotaciones

El archivo de anotación <tablename>.yaml se crea para cada artefacto de salida del producto de datos (tabla, vista) y describe las columnas y los campos en formato YAML. Durante la compilación, el compilador busca automáticamente anotaciones en la carpeta annotations/ del producto (p.ej., annotations/custom_sales_summary.yaml) y combina estas cadenas directamente en las definiciones del esquema de Dataform de salida para que se conserven en los metadatos de la tabla de BigQuery.

Un archivo de anotación config/custom_namespace_path/data_product/product_module_id/annotations/'tablename'.yaml tiene el siguiente formato:

description: "Description of the table or view purpose"
fields:
  - name: "customer_id"                     # column name
    description: "Customer identifier"      # column description
  - name: "column2"
    description: "Description of Column 2"
  - name: "column3"
    description: "Description of Column 3"
  • Crea un archivo manifest.yaml en la carpeta de tu producto de datos config/custom_namespace_path/data_product/product_module_id/, y mantén el tipo, las tablas y las dependencias del módulo. El archivo de manifiesto sigue este formato:

type: sales_performance
builder: sap_product     # Automatically resolves to the global SapProductBuilder fallback
dependencies:
  sapModule:
    type: sap
    supportedVersions:
      - ecc
      - s4

Ejemplo de módulo de producto de datos

Para el ejemplo de vuelos, crearemos src/data_modules/custom_namespace_path/data_product/product_module_id/manifest.yaml con el contenido

type: product_module_id
dependencies:
  sapModule:
    type: cortex.sap
    supported_versions:
      - ecc
      - s4
    tables:
      common:
        - tcurr
  sapModuleCustNS:
    type:  custom_namespace .sap
    supported_versions:
      - ecc
      - s4
    tables:
      common:
        - sflight
builder: sap_product
  • En el siguiente paso, extiende el archivo de configuración de la tabla de referencia para las tablas de productos de datos.

En el ejemplo utilizado, crea config/custom_namespace_path/data_product/product_module_id/table_settings.yaml con el siguiente contenido:

ecc:
  flights_usd:
    materializationType: incremental
    tags: [sap, dataproduct, masterdata]
s4:
  flights_usd:
    materializationType: incremental
    tags: [sap, dataproduct, masterdata]

  • Crea anotaciones para las tablas de productos de datos para enriquecer el esquema de almacenamiento con descripciones.

En el ejemplo utilizado, crea el archivo src/data_modules/custom_namespace_path/data_product/product_module_id/annotations/flights_usd.yaml con el siguiente contenido:

description: "Flight scheduling and pricing information, including currency conversion to USD."
fields:
  - name: "client_mandt"
    description: "Client (Mandant), PK"
  - name: "airline_code_carrid"
    description: "Airline Carrier ID, PK"
  - name: "flight_connection_number_connid"
    description: "Flight Number, PK"
  - name: "flight_date_fldate"
    description: "Flight Date"
  - name: "price_usd"
    description: "Price in USD"
  - name: "price"
    description: "Price in local currency"
  - name: "currency"
    description: "Local currency"
  • La lógica empresarial del producto de datos se almacena en archivos js o sqlx.

En el ejemplo proporcionado, crea el archivo src/data_modules/custom_namespace_path/data_product/product_module_id/definitions/flights_usd.js con el siguiente contenido:

// ___MODULE_CONTEXT___
// ___TABLE_CONFIG___

const moduleConfig = config.product[moduleContext.moduleId];
const sapModuleConfigDatasetId = moduleConfig.sources.sapModule.datasetId;
const sapModuleCustNSConfigDatasetId = moduleConfig.sources.sapModuleCustNS.datasetId;

const materializationType = tableConfig.materializationType || "incremental";

const incremental = require("includes/cortex/incremental.js");
const publish_config = require("includes/cortex/publish_config.js");

const publishConfig = publish_config.getPublishConfig(
   materializationType,
   tableConfig,
   moduleConfig,
   [
       "client_mandt",
       "airline_code_carrid",
       "flight_connection_number_connid",
       "flight_date_fldate"
   ]
);

publish("flight_usd", publishConfig).query(
   (ctx) => `
WITH flight_base AS (
   SELECT
       mandt,
       carrid,
       connid,
       fldate,
       price,
       currency,
       -- Convert flight date string (YYYYMMDD) to an integer to calculate SAP's inverted date key
       CAST(99999999 - CAST(fldate AS INT64) AS STRING) AS inverted_fldate
   FROM   ${ctx.ref(sapModuleCustNSConfigDatasetId, 'sflight')} AS flight
),
ranked_exchange_rates AS (
   SELECT
       f.mandt,
       f.carrid,
       f.connid,
       f.fldate,
       f.price,
       f.currency,
       t.ukurs,
       -- Window function to grab the closest historical exchange rate
       ROW_NUMBER() OVER (
           PARTITION BY f.mandt, f.carrid, f.connid, f.fldate
           ORDER BY t.gdatu ASC
       ) AS latest_rate_rank
   FROM flight_base f
   LEFT JOIN ${ctx.ref(sapModuleConfigDatasetId, 'tcurr')} AS t
     ON f.mandt = t.mandt
    AND t.kurst = 'M'       -- 'M' is the standard SAP default for average exchange rates
    AND t.fcurr = f.currency
    AND t.tcurr = 'USD'
    -- Chronological (rate_date <= flight_date) translates to (t.gdatu >= inverted_fldate)
    AND t.gdatu >= f.inverted_fldate
)

SELECT
   client_mandt,
   airline_code_carrid,
   flight_connection_number_connid,
   flight_date_fldate,
   price,
   currency,
   price_usd,
   CURRENT_TIMESTAMP() AS bq_loaded_at
FROM (
  SELECT
    mandt              AS client_mandt,
    carrid             AS airline_code_carrid,
    connid             AS flight_connection_number_connid,
    PARSE_TIMESTAMP('%Y%m%d', fldate) AS flight_date_fldate,
    price              AS price,
    currency           AS currency,
    -- Currency Conversion Logic
    CASE
       WHEN currency = 'USD' THEN price
       WHEN ukurs IS NULL   THEN NULL -- Handles cases where no exchange rate is found
       -- If UKURS is negative, it's an indirect quotation (1 USD = X Local) -> Divide
       WHEN ukurs < 0       THEN ROUND(price / ABS(ukurs), 2)
       -- If UKURS is positive, it's a direct quotation (1 Local = X USD) -> Multiply
       ELSE ROUND(price * ukurs, 2)
     END AS price_usd
  FROM ranked_exchange_rates
  WHERE latest_rate_rank = 1
)
${incremental.getWhere(ctx, ["flight_date_fldate"])}
`
);

Verificación de la extensión del espacio de nombres personalizado

Para verificar que la extensión de Google Cloud Cortex Framework se haya realizado correctamente con espacios de nombres, módulos de productos de datos o de la base de datos, sigue estos pasos:

  1. Para implementar el módulo del producto de datos, ejecuta uv run targets build, deploy o build-and-deploy, como se describe en la página de implementación.

  2. Abre la IU de Dataform en la consola de BigQuery y navega al repositorio y al espacio de trabajo.

  3. En la IU de Dataform, asegúrate de que no se muestren errores de compilación en la consola.

  4. Verifica que las extensiones preparadas se hayan implementado en las rutas de acceso definitions/data_foundation/custom_namespace_path/ y definitions/data_product/product_module_id/.

  5. Sigue las instrucciones para ejecutar canalizaciones de Dataform.

  6. Verifica en BigQuery que el conjunto de datos del producto contenga la tabla de datos del producto y que esté completa.