Erstellung von Datenproduktmodulen

Die Erstellung eines benutzerdefinierten Datenprodukt-Moduls ist erforderlich, um Ihre eigene Geschäftslogik und Ihre eigenen Analysemodelle zu definieren. So können Sie Berechnungen für Ihre Basistabellen ausführen und sie in bereitstellbare Datasets verpacken.

Vorbereitung

Wenn Sie ein benutzerdefiniertes Datenproduktmodul erstellen, empfehlen wir dringend, es in einem eigenen benutzerdefinierten Namespace zu verpacken. Achten Sie außerdem darauf, dass die Quelltabelle, die Sie verwenden möchten, im Dataset der Datenbasis vorhanden ist.

Erstellung eines Datenproduktmoduls

Für die Definition eines Datenproduktmoduls sind folgende Schritte erforderlich:

  • Registrierung des Datenproduktmoduls in der Datei config/config.yaml durch Erweitern der Liste data.modules.products mit dem Eintrag:
[...]
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.
[...]
  • Erstellung der Datei tableSettings (z.B. config/custom_namespace_path/data_product/product_module_id/table_settings.yaml). Diese YAML-Datei steuert Tabellenkonfigurationen wie Materialisierungen und BigQuery-Optimierungsdetails:
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"
  • Erstellung einer Annotationsdatei

Die Annotationsdatei <tablename>.yaml wird für jedes Ausgabeartefakt des Datenprodukts (Tabelle, Ansicht) erstellt und beschreibt Spalten und Felder im YAML-Format. Während der Kompilierung sucht der Builder automatisch nach Annotationen im Ordner annotations/ des Produkts (z.B. annotations/custom_sales_summary.yaml) und führt diese Strings direkt in die Dataform-Schema-Definitionen der Ausgabe ein, damit sie in den BigQuery-Tabellenmetadaten erhalten bleiben.

Eine Annotationsdatei config/custom_namespace_path/data_product/product_module_id/annotations/'tablename'.yaml hat folgendes Format:

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"
  • Erstellen Sie im Ordner des Datenprodukts config/custom_namespace_path/data_product/product_module_id/ eine Datei manifest.yaml und behalten Sie dabei die Typ-, Tabellen- und Modulabhängigkeiten bei. Die Manifestdatei hat folgendes Format:

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

Beispiel für ein Datenproduktmodul

Für das Beispiel mit den Flügen erstellen wir src/data_modules/custom_namespace_path/data_product/product_module_id/manifest.yaml mit folgendem Inhalt:

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
  • Erweitern Sie im nächsten Schritt die referenzierte Datei mit den Tabelleneinstellungen für Datenprodukttabellen.

Erstellen Sie im verwendeten Beispiel config/custom_namespace_path/data_product/product_module_id/table_settings.yaml mit folgendem Inhalt:

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

  • Erstellen Sie Annotationen für Datenprodukttabellen, um das Speicherschema mit Beschreibungen zu ergänzen.

Erstellen Sie im verwendeten Beispiel die Datei src/data_modules/custom_namespace_path/data_product/product_module_id/annotations/flights_usd.yaml mit folgendem Inhalt:

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"
  • Die Geschäftslogik des Datenprodukts wird in js- oder sqlx-Dateien gespeichert.

Erstellen Sie im angegebenen Beispiel die Datei src/data_modules/custom_namespace_path/data_product/product_module_id/definitions/flights_usd.js mit folgendem Inhalt:

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

Überprüfung der benutzerdefinierten Namespace-Erweiterung

So überprüfen Sie, ob das Google Cloud Cortex Framework erfolgreich mit Namespaces, Datenbasis- oder Datenproduktmodulen erweitert wurde:

  1. Führen Sie zum Bereitstellen des Datenproduktmoduls uv run targets build, deploy oder build-and-deploy aus, wie auf der Seite Bereitstellung beschrieben.

  2. Öffnen Sie die Dataform-UI in der BigQuery Console und rufen Sie das Repository und den Arbeitsbereich auf.

  3. Achten Sie in der Dataform-UI darauf, dass in der Konsole keine Kompilierungsfehler angezeigt werden.

  4. Prüfen Sie, ob die vorbereiteten Erweiterungen in den Pfaden definitions/data_foundation/custom_namespace_path/ und definitions/data_product/product_module_id/ bereitgestellt wurden.

  5. Folgen Sie der Anleitung zum Ausführen von Dataform-Pipelines.

  6. Prüfen Sie in BigQuery, ob das Produkt-Dataset die Datenprodukttabelle enthält und ob sie mit Daten gefüllt ist.