Eseguire il mapping dei dati usando flussi di dati

Importante

Anteprima delle operazioni di Azure IoT: abilitata da Azure Arc è attualmente in anteprima. Non è consigliabile usare questo software di anteprima negli ambienti di produzione.

Sarà necessario distribuire una nuova installazione di Azure IoT Operations quando viene resa disponibile una versione disponibile a livello generale. Non sarà possibile aggiornare un'installazione di anteprima.

Vedere le condizioni per l'utilizzo supplementari per le anteprime di Microsoft Azure per termini legali aggiuntivi che si applicano a funzionalità di Azure in versione beta, in anteprima o in altro modo non ancora disponibili a livello generale.

Usare il linguaggio di mapping dei flussi di dati per trasformare i dati nelle operazioni IoT di Azure. La sintassi è un modo semplice, ma potente, per definire i mapping che trasformano i dati da un formato a un altro. Questo articolo offre una panoramica del linguaggio di mapping dei flussi di dati e dei concetti chiave.

Il mapping consente di trasformare i dati da un formato all'altro. Si consideri il record di input seguente:

{
  "Name": "Grace Owens",
  "Place of birth": "London, TX",
  "Birth Date": "19840202",
  "Start Date": "20180812",
  "Position": "Analyst",
  "Office": "Kent, WA"
}

Confrontarlo con il record di output:

{
  "Employee": {
    "Name": "Grace Owens",
    "Date of Birth": "19840202"
  },
  "Employment": {
    "Start Date": "20180812",
    "Position": "Analyst, Kent, WA",
    "Base Salary": 78000
  }
}

Nel record di output vengono apportate le modifiche seguenti ai dati del record di input:

  • Campi rinominati: il Birth Date campo è ora Date of Birth.
  • Campi ristrutturati: sia Name che Date of Birth sono raggruppati nella nuova Employee categoria.
  • Campo eliminato: il Place of birth campo viene rimosso perché non è presente nell'output.
  • Campo aggiunto: il Base Salary campo è un nuovo campo nella Employment categoria.
  • Valori dei campi modificati o uniti: il Position campo nell'output combina i Position campi e Office dall'input.

Le trasformazioni vengono ottenute tramite mapping, che in genere comporta:

  • Definizione di input: identificazione dei campi nei record di input usati.
  • Definizione di output: specificazione di dove e come sono organizzati i campi di input nei record di output.
  • Conversione (facoltativo): modifica dei campi di input in modo che si adattino ai campi di output. La conversione è necessaria quando più campi di input vengono combinati in un singolo campo di output.

Il mapping seguente è un esempio:

- inputs:
  - BirthDate
  output: Employee.DateOfBirth

- inputs:
  - Position # - - - $1
  - Office # - - - - $2
  output: Employment.Position
  expression: $1 + ", " + $2

- inputs:
  - $context(position).BaseSalary
  output: Employment.BaseSalary

L'esempio esegue il mapping:

  • Mapping uno-a-uno: BirthDate è mappato direttamente a Employee.DateOfBirth senza conversione.
  • Mapping molti-a-uno: combina Position e Office in un singolo campo Employment.Position. La formula di conversione ($1 + ", " + $2) unisce questi campi in una stringa formattata.
  • Dati contestuali: BaseSalary viene aggiunto da un set di dati contestuale denominato position.

Riferimenti ai campi

I riferimenti ai campi mostrano come specificare i percorsi nell'input e nell'output usando la notazione del punto come Employee.DateOfBirth o l'accesso ai dati da un set di dati contestuale tramite $context(position).

Selettori del set di dati di contestualizzazione

Questi selettori consentono ai mapping di integrare dati aggiuntivi da database esterni, detti set di dati di contestualizzazione.

Filtraggio dei record

Il filtraggio dei record comporta l'impostazione di condizioni per selezionare i record da elaborare o eliminare.

Notazione con il punto

La notazione dei punti è ampiamente usata in informatica per fare riferimento a campi, anche in modo ricorsivo. Nella programmazione, i nomi dei campi in genere sono costituiti da lettere e numeri. Un esempio standard di notazione con punti potrebbe essere simile a questo esempio:

- inputs:
  - Person.Address.Street.Number

In un flusso di dati, un percorso descritto dalla notazione punto può includere stringhe e alcuni caratteri speciali senza dover eseguire l'escape:

- inputs:
  - Person.Date of Birth

In altri casi, l'escape è necessario:

- inputs:
  - nsu=http://opcfoundation.org/UA/Plc/Applications;s=RandomSignedInt32

L'esempio precedente, tra gli altri caratteri speciali, contiene punti all'interno del nome del campo. Senza escape, il nome del campo fungerebbe da separatore nella notazione del punto stesso.

Mentre un flusso di dati analizza un percorso, tratta solo due caratteri come speciali:

  • I punti (.) fungono da separatori di campo.
  • Le virgolette singole, se posizionate all'inizio o alla fine di un segmento, iniziano una sezione di escape in cui i punti non vengono considerati come separatori di campo.

Tutti gli altri caratteri vengono considerati come parte del nome del campo. Questa flessibilità è utile in formati come JSON, in cui i nomi dei campi possono essere stringhe arbitrarie.

La definizione del percorso deve inoltre rispettare le regole di YAML. Quando un carattere con significato speciale viene incluso nel percorso, nella configurazione è necessaria una citazione corretta. Per informazioni precise sulle regole, vedere la documentazione YAML. Ecco alcuni esempi che illustrano la necessità di un'attenta formattazione:

- inputs:
  - ':Person:.:name:'   # ':' cannot be used as the first character without single quotation marks
  - '100 celsius.hot'   # numbers followed by text would not be interpreted as a string without single quotation marks

Escape

La funzione primaria di escape in un percorso con notazione con punti consiste nell'usare punti che fanno parte dei nomi di campo anziché separatori:

- inputs:
  - 'Payload."Tag.10".Value'

Nell'esempio precedente il percorso è costituito da tre segmenti: Payload, Tag.10e Value. Le virgolette singole esterne (') sono necessarie a causa delle regole di sintassi YAML, che consentono l'inclusione di virgolette doppie all'interno della stringa.

Regole di escape nella notazione con punti

  • Escape di ogni segmento separatamente: se più segmenti contengono punti, questi segmenti devono essere racchiusi tra virgolette doppie. Anche altri segmenti possono essere racchiusi tra virgolette, ma questo non influisce sull'interpretazione del percorso:

    - inputs:
      - 'Payload."Tag.10".Measurements."Vibration.$12".Value'
    
  • Uso corretto delle virgolette doppie: le virgolette doppie devono essere aperte e chiudere un segmento di escape. Le virgolette al centro del segmento sono considerate parte del nome del campo:

    - inputs:
      - 'Payload.He said: "Hello", and waved'
    

    In questo esempio vengono definiti due campi in dataDestination: Payload e He said: "Hello", and waved. Quando un punto viene visualizzato in queste circostanze, continua a fungere da separatore:

    - inputs:
      - 'Payload.He said: "No. It's done"'
    

    In questo caso, il percorso viene suddiviso nei segmenti Payload, He said: "Noe It's done" (a partire da uno spazio).

Algoritmo di segmentazione

  • Se il primo carattere di un segmento è una virgoletta, il parser cerca la virgoletta successiva. La stringa racchiusa tra queste virgolette è considerata un singolo segmento.
  • Se il segmento non inizia con virgolette, il parser identifica i segmenti cercando il punto successivo o la fine del percorso.

Wildcard (Carattere jolly)

In molti scenari, il record di output è simile al record di input, con solo modifiche minime necessarie. Quando si gestiscono record che contengono numerosi campi, la specifica manuale dei mapping per ogni campo può diventare noiosa. I caratteri jolly semplificano questo processo consentendo mapping generalizzati che possono essere applicati automaticamente a più campi.

Si consideri uno scenario di base per comprendere l'uso degli asterischi nei mapping:

- inputs:
  - *
  output: *

Ecco come funziona l'asterisco (*) in questo contesto:

  • Criteri di ricerca: l'asterisco può corrispondere a un singolo segmento o a più segmenti di un percorso. Funge da segnaposto per tutti i segmenti nel percorso.
  • Corrispondenza dei campi: durante il processo di mapping, l'algoritmo valuta ogni campo nel record di input rispetto al modello specificato in inputs. L'asterisco nell'esempio precedente corrisponde a tutti i percorsi possibili, adattando efficacemente ogni singolo campo nell'input.
  • Segmento acquisito: la parte del percorso corrispondente all'asterisco viene definita captured segment.
  • Mapping di output: nella configurazione di output, viene posizionato dove captured segment viene visualizzato l'asterisco. Ciò significa che la struttura dell'input viene mantenuta nell'output, con il captured segment riempimento del segnaposto fornito dall'asterisco.

Questa configurazione illustra la forma più generica di mapping, in cui ogni campo dell'input viene mappato direttamente a un campo corrispondente nell'output senza modifiche.

Un altro esempio illustra come usare i caratteri jolly per trovare le corrispondenze con le sottosezioni e spostarle insieme. In questo esempio vengono rese flat in modo efficace le strutture annidate all'interno di un oggetto JSON.

JSON originale:

{
  "ColorProperties": {
    "Hue": "blue",
    "Saturation": "90%",
    "Brightness": "50%",
    "Opacity": "0.8"
  },
  "TextureProperties": {
    "type": "fabric",
    "SurfaceFeel": "soft",
    "SurfaceAppearance": "matte",
    "Pattern": "knitted"
  }
}

Configurazione del mapping che usa caratteri jolly:

- inputs:
  - ColorProperties.*
  output: *

- inputs:
  - TextureProperties.*
  output: *

JSON risultante:

{
  "Hue": "blue",
  "Saturation": "90%",
  "Brightness": "50%",
  "Opacity": "0.8",
  "type": "fabric",
  "SurfaceFeel": "soft",
  "SurfaceAppearance": "matte",
  "Pattern": "knitted"
}

Posizionamento con caratteri jolly

Quando si inserisce un carattere jolly, è necessario seguire queste regole:

  • Singolo asterisco per dataDestination: all'interno di un singolo percorso è consentito un solo asterisco (*).
  • Corrispondenza segmento completo: l'asterisco deve corrispondere sempre a un intero segmento del percorso. Non può essere usato per trovare la corrispondenza solo con una parte di un segmento, ad esempio path1.partial*.path3.
  • Posizionamento: l'asterisco può essere posizionato in varie parti di dataDestination:
    • All'inizio: - *.path2.path3 Qui l'asterisco corrisponde a qualsiasi segmento che conduce a path2.path3.
    • Al centro: - path1.*.path3 In questa configurazione l'asterisco corrisponde a qualsiasi segmento tra path1 e path3.
    • Alla fine: - path1.path2.* L'asterisco alla fine corrisponde a qualsiasi segmento che segue dopo path1.path2.

Caratteri jolly multi-input

JSON originale:

{
  "Saturation": {
    "Max": 0.42,
    "Min": 0.67,
  },
  "Brightness": {
    "Max": 0.78,
    "Min": 0.93,
  },
  "Opacity": {
    "Max": 0.88,
    "Min": 0.91,
  }
}

Configurazione del mapping che usa caratteri jolly:

- inputs:
  - *.Max   # - $1
  - *.Min   # - $2
  output: ColorProperties.*
  conversion: ($1 + $2) / 2

JSON risultante:

{
  "ColorProperties" : {
    "Saturation": 0.54,
    "Brightness": 0.85,
    "Opacity": 0.89 
  }    
}

Se si usano caratteri jolly multi-input, l'asterisco (*) deve rappresentare in modo coerente lo stesso Captured Segment in ogni input. Ad esempio, quando * acquisisce Saturation nel modello *.Max, l'algoritmo di mapping prevede che il Saturation.Min corrispondente corrisponda al criterio *.Min. In questo caso, * viene sostituito dal Captured Segment dal primo input, guidando la corrispondenza per gli input successivi.

Si consideri questo esempio dettagliato:

JSON originale:

{
  "Saturation": {
    "Max": 0.42,
    "Min": 0.67,
    "Mid": {
      "Avg": 0.51,
      "Mean": 0.56
    }
  },
  "Brightness": {
    "Max": 0.78,
    "Min": 0.93,
    "Mid": {
      "Avg": 0.81,
      "Mean": 0.82
    }
  },
  "Opacity": {
    "Max": 0.88,
    "Min": 0.91,
    "Mid": {
      "Avg": 0.89,
      "Mean": 0.89
    }
  }
}

Configurazione di mapping iniziale che usa caratteri jolly:

- inputs:
  - *.Max    # - $1
  - *.Min    # - $2
  - *.Avg    # - $3
  - *.Mean   # - $4
  output: ColorProperties.*
  expression: ($1, $2, $3, $4)

Questo mapping iniziale tenta di compilare una matrice , ad esempio per Opacity: [0.88, 0.91, 0.89, 0.89]. Questa configurazione ha esito negativo perché:

  • Il primo input *.Max acquisisce un segmento come Saturation.
  • Il mapping prevede che gli input successivi siano presenti allo stesso livello:
    • Saturation.Max
    • Saturation.Min
    • Saturation.Avg
    • Saturation.Mean

Poiché Avg e Mean sono annidati all'interno Middi , l'asterisco nel mapping iniziale non acquisisce correttamente questi percorsi.

Configurazione del mapping corretta:

- inputs:
  - *.Max        # - $1
  - *.Min        # - $2
  - *.Mid.Avg    # - $3
  - *.Mid.Mean   # - $4
  output: ColorProperties.*
  expression: ($1, $2, $3, $4)

Questo mapping modificato acquisisce in modo accurato i campi necessari. Specifica correttamente i percorsi da includere nell'oggetto annidato Mid , che garantisce che gli asterischi funzionino in modo efficace in diversi livelli della struttura JSON.

Seconda regola e specializzazione

Quando si usa l'esempio precedente da caratteri jolly multi-input, considerare i mapping seguenti che generano due valori derivati per ogni proprietà:

- inputs:
  - *.Max   # - $1
  - *.Min   # - $2
  output: ColorProperties.*.Avg
  expression: ($1 + $2) / 2

- inputs:
  - *.Max   # - $1
  - *.Min   # - $2
  output: ColorProperties.*.Diff
  expression: abs($1 - $2)

Questo mapping è progettato per creare due calcoli separati (Avg e Diff) per ogni proprietà in ColorProperties. Questo esempio mostra il risultato:

{
  "ColorProperties": {
    "Saturation": {
      "Avg": 0.54,
      "Diff": 0.25
    },
    "Brightness": {
      "Avg": 0.85,
      "Diff": 0.15
    },
    "Opacity": {
      "Avg": 0.89,
      "Diff": 0.03
    }
  }
}

In questo caso, la seconda definizione di mapping sugli stessi input funge da seconda regola per il mapping.

Si consideri ora uno scenario in cui un campo specifico richiede un calcolo diverso:

- inputs:
  - *.Max   # - $1
  - *.Min   # - $2
  output: ColorProperties.*
  expression: ($1 + $2) / 2

- inputs:
  - Opacity.Max   # - $1
  - Opacity.Min   # - $2
  output: ColorProperties.OpacityAdjusted
  expression: ($1 + $2 + 1.32) / 2  

In questo caso, il campo Opacity ha un calcolo univoco. Due opzioni per gestire questo scenario sovrapposto sono:

  • Includere entrambi i mapping per Opacity. Poiché i campi di output sono diversi in questo esempio, non eseguirebbero l'override tra loro.
  • Usare la regola più specifica per Opacity e rimuovere quella più generica.

Si consideri un caso speciale per gli stessi campi per decidere l'azione corretta:

- inputs:
  - *.Max   # - $1
  - *.Min   # - $2
  output: ColorProperties.*
  expression: ($1 + $2) / 2

- inputs:
  - Opacity.Max
  - Opacity.Min
  output:   

Un campo output vuoto nella seconda definizione implica la mancata scrittura dei campi nel record di output (rimuovendo in modo efficace Opacity). Questa configurazione è più una Specialization che una Second Rule.

Risoluzione dei mapping sovrapposti in base ai flussi di dati:

  • La valutazione procede dalla regola principale nella definizione del mapping.
  • Se un nuovo mapping viene risolto negli stessi campi di una regola precedente, si applicano le condizioni seguenti:
    • Un Rank viene calcolato per ogni input risolto in base al numero di segmenti acquisiti dai caratteri jolly. Ad esempio, se il Captured Segments è Properties.Opacity, il Rank è 2. Se è solo Opacity, è Rank 1. Un mapping senza caratteri jolly ha un Rank pari a 0.
    • Se il Rank della seconda regola è uguale o superiore alla regola precedente, un flusso di dati lo considera come una Second Rule.
    • In caso contrario, il flusso di dati considera la configurazione come .Specialization

Ad esempio, il mapping diretto Opacity.Max e Opacity.Min a un output vuoto ha un Rank valore pari a 0. Poiché la seconda regola ha un valore inferiore Rank a quello precedente, viene considerata una specializzazione ed esegue l'override della regola precedente, che calcola un valore per Opacity.

Caratteri jolly nei set di dati di contestualizzazione

Si vedrà ora come usare i set di dati di contestualizzazione con caratteri jolly tramite un esempio. Si consideri un set di dati denominato position che contiene il record seguente:

{
  "Position": "Analyst",
  "BaseSalary": 70000,
  "WorkingHours": "Regular"
}

In un esempio precedente è stato usato un campo specifico di questo set di dati:

- inputs:
  - $context(position).BaseSalary
  output: Employment.BaseSalary

Questo mapping copia BaseSalary dal set di dati di contesto direttamente nella Employment sezione del record di output. Se si vuole automatizzare il processo e includere tutti i campi del position set di dati nella Employment sezione , è possibile usare i caratteri jolly:

- inputs:
  - $context(position).*
  output: Employment.*

Questa configurazione consente un mapping dinamico in cui ogni campo all'interno del set di dati position viene copiato nella sezione Employment del record di output:

{
    "Employment": {      
      "Position": "Analyst",
      "BaseSalary": 70000,
      "WorkingHours": "Regular"
    }
}