Création d'un module de produit de données

La création d'un module de produit de données personnalisé est nécessaire pour définir votre propre logique métier et vos propres modèles analytiques. Vous pouvez ainsi effectuer des calculs sur vos tables de base et les regrouper dans des ensembles de données déployables.

Prérequis

Lorsque vous créez un module de produit de données personnalisé, nous vous recommandons vivement d'utiliser un espace de noms personnalisé dédié pour l'empaqueter. Assurez-vous également que la table source que vous prévoyez d'utiliser existe dans l'ensemble de données de base.

Création d'un module de produit de données

La définition d'un module de produit de données nécessite les étapes suivantes :

  • Enregistrement du module de produit de données dans le fichier config/config.yaml, en étendant la liste data.modules.products avec l'entrée :
[...]
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.
[...]
  • Création du fichier tableSettings (par exemple, config/custom_namespace_path/data_product/product_module_id/table_settings.yaml). Ce fichier YAML contrôle les configurations de table, telles que les matérialisations et les détails d'optimisation 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"
  • Création d'un fichier d'annotation

Le fichier d'annotation <tablename>.yaml est créé pour chaque artefact de sortie du produit de données (table, vue) et décrit les colonnes et les champs au format YAML. Lors de la compilation, le compilateur recherche automatiquement les annotations dans le dossier annotations/ du produit (par exemple, annotations/custom_sales_summary.yaml), puis fusionne ces chaînes directement dans les définitions de schéma Dataform de sortie afin qu'elles soient conservées dans les métadonnées de la table BigQuery.

Un fichier d'annotation config/custom_namespace_path/data_product/product_module_id/annotations/'tablename'.yaml a le format suivant :

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"
  • Créez un fichier manifest.yaml dans le dossier de votre produit de données config/custom_namespace_path/data_product/product_module_id/, en conservant le type, les tables et les dépendances du module. Le fichier manifeste a le format suivant :

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

Exemple de module de produit de données

Pour l'exemple de vols, nous créons src/data_modules/custom_namespace_path/data_product/product_module_id/manifest.yaml avec le contenu

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
  • À l'étape suivante, étendez le fichier de paramètres de table référencé pour les tables de produits de données.

Dans l'exemple utilisé, créez : config/custom_namespace_path/data_product/product_module_id/table_settings.yaml avec le contenu :

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

  • Créez des annotations pour les tables de produits de données afin d'enrichir le schéma de stockage avec des descriptions.

Dans l'exemple utilisé, créez le fichier : src/data_modules/custom_namespace_path/data_product/product_module_id/annotations/flights_usd.yaml avec le contenu :

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 logique métier du produit de données est stockée dans des fichiers js ou sqlx.

Dans l'exemple donné, créez le fichier src/data_modules/custom_namespace_path/data_product/product_module_id/definitions/flights_usd.js avec le contenu :

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

Vérification de l'extension d'espace de noms personnalisé

Pour vérifier que l'extension de Google Cloud Cortex Framework avec des espaces de noms, des bases de données ou des modules de produits de données a réussi, procédez comme suit :

  1. Pour déployer le module de produit de données, exécutez uv run targets build, deploy ou build-and-deploy, comme décrit sur la page Déploiement.

  2. Ouvrez l'UI Dataform dans la console BigQuery, puis accédez au dépôt et à l'espace de travail.

  3. Dans l'UI Dataform, assurez-vous qu'aucune erreur de compilation n'est affichée dans la console.

  4. Vérifiez que les extensions préparées ont été déployées dans les chemins d'accès definitions/data_foundation/custom_namespace_path/ et definitions/data_product/product_module_id/.

  5. Suivez les instructions d'exécution des pipelines Dataform.

  6. Vérifiez dans BigQuery que l'ensemble de données du produit contient la table du produit de données et qu'il est rempli de données.