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 è oraDate of Birth
. - Campi ristrutturati: sia
Name
cheDate of Birth
sono raggruppati nella nuovaEmployee
categoria. - Campo eliminato: il
Place of birth
campo viene rimosso perché non è presente nell'output. - Campo aggiunto: il
Base Salary
campo è un nuovo campo nellaEmployment
categoria. - Valori dei campi modificati o uniti: il
Position
campo nell'output combina iPosition
campi eOffice
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 aEmployee.DateOfBirth
senza conversione. - Mapping molti-a-uno: combina
Position
eOffice
in un singolo campoEmployment.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 denominatoposition
.
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.10
e 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
eHe 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: "No
eIt'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 ilcaptured 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 apath2.path3
. - Al centro: -
path1.*.path3
In questa configurazione l'asterisco corrisponde a qualsiasi segmento trapath1
epath3
. - Alla fine: -
path1.path2.*
L'asterisco alla fine corrisponde a qualsiasi segmento che segue dopopath1.path2
.
- All'inizio: -
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 comeSaturation
. - 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 Mid
di , 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 ilCaptured Segments
èProperties.Opacity
, ilRank
è 2. Se è soloOpacity
, èRank
1. Un mapping senza caratteri jolly ha unRank
pari a 0. - Se il
Rank
della seconda regola è uguale o superiore alla regola precedente, un flusso di dati lo considera come unaSecond Rule
. - In caso contrario, il flusso di dati considera la configurazione come .
Specialization
- Un
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"
}
}