Creazione del modulo del prodotto di dati

Per definire la tua logica di business e i tuoi modelli analitici, crea un modulo del prodotto di dati personalizzato. In questo modo puoi eseguire calcoli sulle tabelle di base o sui prodotti di dati upstream e raggruppare i risultati in set di dati di cui è possibile eseguire il deployment.

Prerequisiti

Per una migliore gestione del ciclo di vita, ti consigliamo di creare moduli di prodotti di dati personalizzati in uno spazio dei nomi personalizzato dedicato. Inoltre, assicurati che la tabella di origine che intendi utilizzare esista nel set di dati di base.

Creazione di un modulo del prodotto di dati

La definizione del modulo del prodotto di dati richiede i seguenti passaggi:

  • Registrazione del modulo del prodotto di dati nel file config/config.yaml, estendendo l'elenco data.modules.products con la voce:
data:
  # Configuration for data foundation and product modules.
  modules:
    # List of data product modules.
    products:
        # Recommended naming for product_module_id:
        # custom_namespace_product_name
      - moduleId:  product_module_id
        # Path of the data product (namespaced).
        modulePath:  custom_namespace.system_type.products.product_name
        # Map of module dependencies.
        dependencyBindings:
          sapModule: erp
          sapModuleCustNS:  foundation_module_id
        # Reference to the target dataset ID.
        dataTargetId: product_target
        # Whether the module is enabled.
        # enabled: true
        # Whether the foundation is external (does not create target dataset).
        # external: false
        # Custom table settings file, relative to 'config/' file directory
        # Recommended path: '{custom_namespace}/{system_type}/products/{product_name}/table_settings.yaml'
        # If omitted, defaults to '../src/data_modules/{custom_namespace}/{system_type}/products/{product_name}/table_settings.default.yaml'
        # tableSettings: "{custom_namespace}/{system_type}/products/{product_name}/table_settings.yaml"
        
  • Creazione del file tableSettings predefinito (ad es. src/data_modules/custom_namespace/system_type/products/product_name/table_settings.default.yaml).

Questo file YAML controlla le configurazioni delle tabelle, come le materializzazioni e i dettagli di ottimizzazione di BigQuery:

common:
  custom_sales_summary:
    materializationType: "table"
    bigQueryLabels:
      - key: data_class
        value: transactional
    dataformTags: ["custom", "sales", "reporting"]
    partitionDetails:
      column: "created_date"
      partitionType: "date"
      timeGrain: "day"
    clusterDetails:
      columns:
        - "customer_id"
  • Creazione del file di annotazione

Il file di annotazione tablename.yaml viene creato per ogni artefatto di output del prodotto di dati (tabella, visualizzazione) e descrive colonne e campi in formato YAML. Durante la compilazione, il builder cerca automaticamente le annotazioni nella cartella annotations/ del prodotto (ad es. src/data_modules/custom_namespace/system_type/products/product_name/annotations/custom_sales_summary.yaml), unisce queste stringhe direttamente nelle definizioni dello schema Dataform di output in modo che vengano conservate nei metadati della tabella BigQuery.

Un file di annotazione src/data_modules/custom_namespace/system_type/products/product_name/annotations/tablename.yaml ha il seguente 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 file manifest.yaml nella cartella del prodotto di dati src/data_modules/custom_namespace/system_type/products/product_name/, mantenendo il tipo, la categoria, le tabelle e le dipendenze dei moduli. Il file manifest ha il seguente formato:
displayName: Sales Performance Summary
description: Sales performance analytical data product.
category: product
type: generic
builder: sap_product     # Automatically resolves to the global SapProductBuilder fallback
dependencies:
  sapModule:
    modulePath: cortex.sap.foundations.sap
    supportedVersions:
      - ecc
      - s4

Esempio di modulo del prodotto di dati

I passaggi per implementare il flights_usd prodotto di dati nello spazio dei nomi sap_bookingdatamodel dell'esempio dei voli sono:

  • Registrazione del modulo del prodotto di dati nel file config/config.yaml, estendendo l'elenco data.modules.products con la voce:
data:
  modules:
    products:
      - moduleId: sap_bookingdatamodel_flights_usd
        modulePath: sap_bookingdatamodel.sap.products.flights_usd
        dependencyBindings:
          sapModule: erp
          sapModuleCustNS: sap_bookingdatamodel
        dataTargetId: product_target
  • Successivamente, crea src/data_modules/sap_bookingdatamodel/sap/products/flights_usd/manifest.yaml con il seguente contenuto
displayName: Flights USD
description: Flight scheduling and pricing USD data product.
category: product
type: generic
dependencies:
  sapModule:
    modulePath: cortex.sap.foundations.sap
    supportedVersions:
      - ecc
      - s4
    tables:
      common:
        - tcurr
  sapModuleCustNS:
    # Type of the dependent Module.
    # use cortex.sap.foundations.sap if you followed "Configure multiple instances of a data foundation module"
    # https://docs.cloud.google.com/cortex/docs/deployment-configuration#multiple-data-foundation-instances
    modulePath: cortex.sap.foundations.sap
    # use sap_bookingdatamodel.sap.foundations.sap if you are connecting to custom-data foundation module:
    # https://docs.cloud.google.com/cortex/docs/extensibility-guide-data-foundation
    #modulePath: sap_bookingdatamodel.sap.foundations.sap
    supportedVersions:
      - ecc
      - s4
    tables:
      common:
        - sflight
builder: sap_product
  • Nel passaggio successivo, crea il file di impostazioni della tabella a cui viene fatto riferimento per configurare lo schema e i metadati delle tabelle o delle visualizzazioni di output in BigQuery.

Nell'esempio utilizzato, crea: src/data_modules/sap_bookingdatamodel/sap/products/flights_usd/table_settings.default.yaml con il seguente contenuto:

ecc:
  flights_usd:
    materializationType: incremental
    bigQueryLabels:
      - key: data_class
        value: transactional
    dataformTags: [sap, dataproduct, masterdata]
s4:
  flights_usd:
    materializationType: incremental
    bigQueryLabels:
      - key: data_class
        value: transactional
    dataformTags: [sap, dataproduct, masterdata]

  • Crea annotazioni per le tabelle dei prodotti di dati per arricchire lo schema di archiviazione con le descrizioni.

Nell'esempio utilizzato, crea il file: src/data_modules/sap_bookingdatamodel/sap/products/flights_usd/annotations/flights_usd.yaml con il seguente contenuto:

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 logica di business del prodotto di dati è archiviata nei file js o sqlx.

Nell'esempio fornito, crea il file src/data_modules/sap_bookingdatamodel/sap/products/flights_usd/definitions/flights_usd.js con il seguente contenuto:

// ___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"])}
`
);

Verifica dell'estensione dello spazio dei nomi personalizzato

Per verificare la creazione corretta dei moduli dei prodotti di dati di Google Cloud Cortex Framework: