Mapuj dane przy użyciu przepływów danych
Ważne
Ta strona zawiera instrukcje dotyczące zarządzania składnikami operacji usługi Azure IoT przy użyciu manifestów wdrażania platformy Kubernetes, które są w wersji zapoznawczej. Ta funkcja jest udostępniana z kilkoma ograniczeniami i nie powinna być używana w przypadku obciążeń produkcyjnych.
Zobacz Dodatkowe warunki użytkowania wersji zapoznawczych platformy Microsoft Azure, aby zapoznać się z postanowieniami prawnymi dotyczącymi funkcji platformy Azure, które są w wersji beta lub wersji zapoznawczej albo w inny sposób nie zostały jeszcze wydane jako ogólnie dostępne.
Użyj języka mapowania przepływu danych, aby przekształcić dane w operacjach usługi Azure IoT. Składnia jest prostym, ale zaawansowanym sposobem definiowania mapowań, które przekształcają dane z jednego formatu na inny. Ten artykuł zawiera omówienie języka mapowania przepływu danych i kluczowych pojęć.
Mapowanie umożliwia przekształcanie danych z jednego formatu na inny. Rozważmy następujący rekord wejściowy:
{
"Name": "Grace Owens",
"Place of birth": "London, TX",
"Birth Date": "19840202",
"Start Date": "20180812",
"Position": "Analyst",
"Office": "Kent, WA"
}
Porównaj go z rekordem wyjściowym:
{
"Employee": {
"Name": "Grace Owens",
"Date of Birth": "19840202"
},
"Employment": {
"Start Date": "20180812",
"Position": "Analyst, Kent, WA",
"Base Salary": 78000
}
}
W rekordzie wyjściowym zostaną wprowadzone następujące zmiany w danych rekordu wejściowego:
- Zmieniono nazwę pól:
Birth Date
pole ma teraz wartośćDate of Birth
. - Zrestrukturyzowane pola: zarówno pola, jak
Name
iDate of Birth
są grupowane w nowejEmployee
kategorii. - Usunięto pole:
Place of birth
pole zostało usunięte, ponieważ nie jest obecne w danych wyjściowych. - Dodano pole:
Base Salary
Pole jest nowym polemEmployment
w kategorii. - Wartości pól zostały zmienione lub scalone:
Position
pole w danych wyjściowych łączyPosition
pola iOffice
z danych wejściowych.
Przekształcenia są osiągane za pomocą mapowania, które zwykle obejmuje:
- Definicja danych wejściowych: identyfikowanie pól w używanych rekordach wejściowych.
- Definicja danych wyjściowych: określanie miejsca i sposobu organizowania pól wejściowych w rekordach wyjściowych.
- Konwersja (opcjonalnie): Modyfikowanie pól wejściowych w celu dopasowania ich do pól wyjściowych.
expression
jest wymagany, gdy wiele pól wejściowych jest połączonych w jedno pole wyjściowe.
Oto przykładowe mapowanie:
{
inputs: [
'BirthDate'
]
output: 'Employee.DateOfBirth'
}
{
inputs: [
'Position' // - - - - $1
'Office' // - - - - $2
]
output: 'Employment.Position'
expression: '$1 + ", " + $2'
}
{
inputs: [
'$context(position).BaseSalary'
]
output: 'Employment.BaseSalary'
}
Przykładowe mapy:
- Mapowanie jeden do jednego:
BirthDate
jest bezpośrednio mapowane naEmployee.DateOfBirth
bez konwersji. - Mapowanie wiele-do-jednego: łączy
Position
iOffice
w jednoEmployment.Position
pole. Formuła konwersji ($1 + ", " + $2
) scala te pola w sformatowany ciąg. - Dane kontekstowe:
BaseSalary
są dodawane z kontekstowego zestawu danych o nazwieposition
.
Odwołania do pól
Odwołania do pól pokazują, jak określić ścieżki w danych wejściowych i wyjściowych przy użyciu notacji kropkowej, takiej jak Employee.DateOfBirth
lub uzyskiwania dostępu do danych z kontekstowego zestawu danych za pośrednictwem .$context(position)
Właściwości metadanych MQTT i Kafka
Jeśli używasz MQTT lub Kafka jako źródła lub miejsca docelowego, możesz uzyskać dostęp do różnych właściwości metadanych w języku mapowania. Te właściwości można zamapować w danych wejściowych lub wyjściowych.
Właściwości metadanych
- Temat: działa zarówno w przypadku MQTT, jak i platformy Kafka. Zawiera on ciąg, w którym opublikowano komunikat. Przykład:
$metadata.topic
. - Właściwość użytkownika: W MQTT odnosi się to do par klucz/wartość dowolnej postaci, które może przenosić komunikat MQTT. Jeśli na przykład komunikat MQTT został opublikowany z właściwością użytkownika z kluczem "priority" i wartością "high",
$metadata.user_property.priority
odwołanie przechowuje wartość "high". Klucze właściwości użytkownika mogą być dowolnymi ciągami i mogą wymagać ucieczki:$metadata.user_property."weird key"
używa klucza "dziwnego klucza" (z spacją). - Właściwość systemowa: ten termin jest używany dla każdej właściwości, która nie jest właściwością użytkownika. Obecnie obsługiwana jest tylko jedna właściwość systemu:
$metadata.system_property.content_type
, która odczytuje właściwość typu zawartości komunikatu MQTT (jeśli ustawiono). - Nagłówek: jest to odpowiednik platformy Kafka właściwości użytkownika MQTT. Platforma Kafka może używać dowolnej wartości binarnej dla klucza, ale przepływ danych obsługuje tylko klucze ciągów UTF-8. Przykład:
$metadata.header.priority
. Ta funkcja jest podobna do właściwości użytkownika.
Właściwości metadanych mapowania
Mapowanie danych wejściowych
W poniższym przykładzie właściwość MQTT topic
jest mapowana na origin_topic
pole w danych wyjściowych:
inputs: [
'$metadata.topic'
]
output: 'origin_topic'
Jeśli właściwość priority
użytkownika znajduje się w komunikacie MQTT, w poniższym przykładzie pokazano, jak mapować ją na pole wyjściowe:
inputs: [
'$metadata.user_property.priority'
]
output: 'priority'
Mapowanie danych wyjściowych
Właściwości metadanych można również mapować na nagłówek danych wyjściowych lub właściwość użytkownika. W poniższym przykładzie MQTT topic
jest mapowany na origin_topic
pole we właściwości użytkownika danych wyjściowych:
inputs: [
'$metadata.topic'
]
output: '$metadata.user_property.origin_topic'
Jeśli przychodzący ładunek zawiera priority
pole, w poniższym przykładzie pokazano, jak mapować go na właściwość użytkownika MQTT:
inputs: [
'priority'
]
output: '$metadata.user_property.priority'
Ten sam przykład dla platformy Kafka:
inputs: [
'priority'
]
output: '$metadata.header.priority'
Selektory zestawów danych kontekstowych
Selektory te umożliwiają mapowania w celu zintegrowania dodatkowych danych z zewnętrznych baz danych, które są określane jako zestawy danych kontekstowych.
Filtrowanie rekordów
Filtrowanie rekordów obejmuje ustawienie warunków, aby wybrać, które rekordy mają być przetwarzane lub porzucane.
Notacja kropkowa
Notacja kropkowa jest powszechnie używana w nauce komputerowej do odwoływanie się do pól, nawet rekursywnie. W programowaniu nazwy pól zwykle składają się z liter i cyfr. Przykład standardowej notacji kropkowej może wyglądać następująco:
inputs: [
'Person.Address.Street.Number'
]
W przepływie danych ścieżka opisana przez notację kropkową może zawierać ciągi i niektóre znaki specjalne bez konieczności ucieczki:
inputs: [
'Person.Date of Birth'
]
W innych przypadkach konieczne jest ucieczka:
inputs: [
'Person."Tag.10".Value'
]
Poprzedni przykład, między innymi znakami specjalnymi, zawiera kropki w nazwie pola. Bez ucieczki nazwa pola będzie służyć jako separator w notacji kropkowej.
Podczas gdy przepływ danych analizuje ścieżkę, traktuje tylko dwa znaki jako specjalne:
- Kropki (
.
) działają jako separatory pól. - Pojedyncze cudzysłów, po umieszczeniu na początku lub na końcu segmentu, rozpocznij sekcję ucieczki, w której kropki nie są traktowane jako separatory pól.
Wszystkie inne znaki są traktowane jako część nazwy pola. Ta elastyczność jest przydatna w formatach, takich jak JSON, gdzie nazwy pól mogą być dowolnymi ciągami.
W Bicep wszystkie ciągi są ujęte w pojedynczy cudzysłów ('
). Przykłady prawidłowego cytowania w języku YAML dla platformy Kubernetes nie mają zastosowania.
Ucieczki
Podstawową funkcją ucieczki w ścieżce kropkowanej jest zastosowanie kropek, które są częścią nazw pól, a nie separatorów:
inputs: [
'Payload."Tag.10".Value'
]
W tym przykładzie ścieżka składa się z trzech segmentów: Payload
, Tag.10
i Value
.
Ucieczka reguł w notacji kropkowej
Ucieczka każdego segmentu oddzielnie: jeśli wiele segmentów zawiera kropki, te segmenty muszą być ujęte w podwójny cudzysłów. Można również cytować inne segmenty, ale nie ma to wpływu na interpretację ścieżki:
inputs: [ 'Payload."Tag.10".Measurements."Vibration.$12".Value' ]
Prawidłowe użycie podwójnych cudzysłowów: podwójny cudzysłów musi otwierać i zamykać segment ucieczki. Wszelkie cudzysłów w środku segmentu są traktowane jako część nazwy pola:
inputs: [ 'Payload.He said: "Hello", and waved' ]
W tym przykładzie zdefiniowano dwa pola: Payload
i He said: "Hello", and waved
. Gdy kropka pojawia się w tych okolicznościach, nadal pełni rolę separatora:
inputs: [
'Payload.He said: "No. It is done"'
]
W takim przypadku ścieżka jest podzielona na segmenty Payload
, He said: "No
i It is done"
(począwszy od spacji).
Algorytm segmentacji
- Jeśli pierwszy znak segmentu jest znakiem cudzysłowu, analizator wyszukuje następny cudzysłów. Ciąg ujęta między tymi cudzysłowami jest traktowana jako pojedynczy segment.
- Jeśli segment nie zaczyna się od cudzysłowu, analizator identyfikuje segmenty, wyszukując następną kropkę lub koniec ścieżki.
Symbole wieloznaczne
W wielu scenariuszach rekord wyjściowy jest ściśle podobny do rekordu wejściowego, z wymaganymi tylko drobnymi modyfikacjami. Jeśli zajmujesz się rekordami zawierającymi wiele pól, ręczne określanie mapowań dla każdego pola może stać się żmudne. Symbole wieloznaczne upraszczają ten proces, umożliwiając uogólnione mapowania, które mogą być automatycznie stosowane do wielu pól.
Rozważmy podstawowy scenariusz, aby zrozumieć użycie gwiazdki w mapowaniach:
inputs: [
'*'
]
output: '*'
Ta konfiguracja pokazuje podstawowe mapowanie, w którym każde pole w danych wejściowych jest bezpośrednio mapowane na to samo pole w danych wyjściowych bez żadnych zmian. Gwiazdka (*
) służy jako symbol wieloznaczny zgodny z dowolnym polem w rekordzie wejściowym.
Oto jak gwiazdka (*
) działa w tym kontekście:
- Dopasowywanie wzorca: gwiazdka może być zgodna z pojedynczym segmentem lub wieloma segmentami ścieżki. Służy jako symbol zastępczy dla wszystkich segmentów w ścieżce.
- Dopasowywanie pól: podczas procesu mapowania algorytm ocenia każde pole w rekordzie wejściowym względem wzorca określonego w elemecie
inputs
. Gwiazdka w poprzednim przykładzie jest zgodna ze wszystkimi możliwymi ścieżkami, co skutecznie pasuje do każdego pola w danych wejściowych. - Przechwycony segment: część ścieżki, do której pasuje gwiazdka, jest określana jako
captured segment
. - Mapowanie danych wyjściowych: w konfiguracji
captured segment
wyjściowej jest umieszczana gwiazdka. Oznacza to, że struktura danych wejściowych jest zachowywana w danych wyjściowych zcaptured segment
wypełnieniem symbolu zastępczego dostarczonego przez gwiazdkę.
Inny przykład ilustruje, jak można używać symboli wieloznacznych do dopasowywania podsekcji i przenoszenia ich razem. Ten przykład skutecznie spłaszcza struktury zagnieżdżone w obiekcie JSON.
Oryginalny kod JSON:
{
"ColorProperties": {
"Hue": "blue",
"Saturation": "90%",
"Brightness": "50%",
"Opacity": "0.8"
},
"TextureProperties": {
"type": "fabric",
"SurfaceFeel": "soft",
"SurfaceAppearance": "matte",
"Pattern": "knitted"
}
}
Konfiguracja mapowania używająca symboli wieloznacznych:
{
inputs: [
'ColorProperties.*'
]
output: '*'
}
{
inputs: [
'TextureProperties.*'
]
output: '*'
}
Wynikowy kod JSON:
{
"Hue": "blue",
"Saturation": "90%",
"Brightness": "50%",
"Opacity": "0.8",
"type": "fabric",
"SurfaceFeel": "soft",
"SurfaceAppearance": "matte",
"Pattern": "knitted"
}
Umieszczanie symboli wieloznacznych
W przypadku umieszczenia symbolu wieloznakowego należy postępować zgodnie z następującymi regułami:
- Pojedyncza gwiazdka na odwołanie do danych: tylko jedna gwiazdka (
*
) jest dozwolona w ramach pojedynczego odwołania do danych. - Dopasowanie pełnego segmentu: gwiazdka musi zawsze odpowiadać całej części ścieżki. Nie można jej użyć do dopasowania tylko do części segmentu, takiej jak
path1.partial*.path3
. - Pozycjonowanie: gwiazdka może być umieszczona w różnych częściach odwołania do danych:
- Na początku:
*.path2.path3
- Tutaj gwiazdka pasuje do każdego segmentu, który prowadzi dopath2.path3
. - W środku:
path1.*.path3
— W tej konfiguracji gwiazdka pasuje do dowolnego segmentu międzypath1
ipath3
. - Na końcu:
path1.path2.*
- Gwiazdka na końcu pasuje do każdego segmentu, który następuje popath1.path2
.
- Na początku:
- Ścieżka zawierająca gwiazdkę musi być ujęta w pojedynczy cudzysłów (
'
).
Symbole wieloznaczne z wieloma danymi wejściowymi
Oryginalny kod JSON:
{
"Saturation": {
"Max": 0.42,
"Min": 0.67,
},
"Brightness": {
"Max": 0.78,
"Min": 0.93,
},
"Opacity": {
"Max": 0.88,
"Min": 0.91,
}
}
Konfiguracja mapowania używająca symboli wieloznacznych:
inputs: [
'*.Max' // - $1
'*.Min' // - $2
]
output: 'ColorProperties.*'
expression: '($1 + $2) / 2'
Wynikowy kod JSON:
{
"ColorProperties" : {
"Saturation": 0.54,
"Brightness": 0.85,
"Opacity": 0.89
}
}
Jeśli używasz wielo wejściowych symboli wieloznacznych, gwiazdka (*
) musi spójnie reprezentować to samo Captured Segment
we wszystkich danych wejściowych. Na przykład w przypadku *
przechwytywania Saturation
we wzorcu *.Max
algorytm mapowania oczekuje dopasowania do Saturation.Min
wzorca *.Min
. *
W tym miejscu element jest zastępowany przez element Captured Segment
z pierwszego wejścia, prowadząc do dopasowywania dla kolejnych danych wejściowych.
Rozważmy ten szczegółowy przykład:
Oryginalny kod JSON:
{
"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
}
}
}
Początkowa konfiguracja mapowania używająca symboli wieloznacznych:
inputs: [
'*.Max' // - $1
'*.Min' // - $2
'*.Avg' // - $3
'*.Mean' // - $4
]
To początkowe mapowanie próbuje skompilować tablicę (na przykład dla Opacity
: [0.88, 0.91, 0.89, 0.89]
). Ta konfiguracja kończy się niepowodzeniem, ponieważ:
- Pierwsze dane wejściowe
*.Max
przechwytują segment, taki jakSaturation
. - Mapowanie oczekuje, że kolejne dane wejściowe będą obecne na tym samym poziomie:
Saturation.Max
Saturation.Min
Saturation.Avg
Saturation.Mean
Ponieważ Avg
i Mean
są zagnieżdżone w obiekcie Mid
, gwiazdka w początkowym mapowaniu nie przechwytuje poprawnie tych ścieżek.
Poprawiona konfiguracja mapowania:
inputs: [
'*.Max' // - $1
'*.Min' // - $2
'*.Mid.Avg' // - $3
'*.Mid.Mean' // - $4
]
To poprawione mapowanie dokładnie przechwytuje niezbędne pola. Poprawnie określa ścieżki do uwzględnienia zagnieżdżonego Mid
obiektu, co gwarantuje, że gwiazdki działają skutecznie na różnych poziomach struktury JSON.
Druga reguła a specjalizacja
W przypadku użycia poprzedniego przykładu z wielo wejściowych symboli wieloznacznych należy wziąć pod uwagę następujące mapowania, które generują dwie wartości pochodne dla każdej właściwości:
{
inputs: [
'*.Max' // - $1
'*.Min' // - $2
]
output: 'ColorProperties.*.Avg'
expression: '($1 + $2) / 2'
}
{
inputs: [
'*.Max' // - $1
'*.Min' // - $2
]
output: 'ColorProperties.*.Diff'
expression: '$1 - $2'
}
To mapowanie ma na celu utworzenie dwóch oddzielnych obliczeń (Avg
i Diff
) dla każdej właściwości w obszarze ColorProperties
. W tym przykładzie przedstawiono wynik:
{
"ColorProperties": {
"Saturation": {
"Avg": 0.54,
"Diff": 0.25
},
"Brightness": {
"Avg": 0.85,
"Diff": 0.15
},
"Opacity": {
"Avg": 0.89,
"Diff": 0.03
}
}
}
W tym miejscu druga definicja mapowania na tych samych danych wejściowych działa jako druga reguła mapowania.
Teraz rozważmy scenariusz, w którym określone pole wymaga innego obliczenia:
{
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'
}
W tym przypadku Opacity
pole ma unikatowe obliczenie. Istnieją dwie opcje obsługi tego nakładających się scenariuszy:
- Uwzględnij oba mapowania dla elementu
Opacity
. Ponieważ pola wyjściowe różnią się w tym przykładzie, nie przesłoniłyby się nawzajem. - Użyj bardziej konkretnej reguły dla
Opacity
i usuń więcej ogólnych.
Rozważmy specjalny przypadek dla tych samych pól, aby ułatwić podjęcie decyzji o odpowiedniej akcji:
{
inputs: [
'*.Max' // - $1
'*.Min' // - $2
]
output: 'ColorProperties.*'
expression: '($1 + $2) / 2'
}
{
inputs: [
'Opacity.Max' // - $1
'Opacity.Min' // - $2
]
output: ''
}
output
Puste pole w drugiej definicji oznacza, że nie zapisuje pól w rekordzie wyjściowym (skutecznie usuwając Opacity
). Ta konfiguracja jest bardziej niż Specialization
Second Rule
.
Rozwiązywanie nakładających się mapowań według przepływów danych:
- Ocena postępuje z góry reguły w definicji mapowania.
- Jeśli nowe mapowanie zostanie rozpoznane w tych samych polach co poprzednia reguła, obowiązują następujące warunki:
- Wartość A
Rank
jest obliczana dla każdego rozpoznanego danych wejściowych na podstawie liczby segmentów przechwyconych symboli wieloznacznych. Na przykład, jeśliCaptured Segments
parametr toProperties.Opacity
, parametrRank
ma wartość 2. Jeśli jest to tylkoOpacity
wartość , parametrRank
ma wartość 1. Mapowanie bez symboli wieloznacznych maRank
wartość 0. - Jeśli druga
Rank
reguła jest równa lub wyższa niż poprzednia reguła, przepływ danych traktuje go jakoSecond Rule
. - W przeciwnym razie przepływ danych traktuje konfigurację
Specialization
jako .
- Wartość A
Na przykład mapowanie, które kieruje element i Opacity.Min
do pustych danych wyjściowychOpacity.Max
, ma Rank
wartość 0. Ponieważ druga reguła ma wartość niższą Rank
niż poprzednia, jest uważana za specjalizację i zastępuje poprzednią regułę, która oblicza wartość dla Opacity
elementu .
Symbole wieloznaczne w zestawach danych kontekstowych
Teraz zobaczmy, jak zestawy danych kontekstowych mogą być używane z symbolami wieloznacznymi za pomocą przykładu. Rozważmy zestaw danych o nazwie position
zawierający następujący rekord:
{
"Position": "Analyst",
"BaseSalary": 70000,
"WorkingHours": "Regular"
}
We wcześniejszym przykładzie użyliśmy określonego pola z tego zestawu danych:
inputs: [
'$context(position).BaseSalary'
]
output: 'Employment.BaseSalary'
To mapowanie kopiuje BaseSalary
z zestawu danych kontekstowych bezpośrednio do Employment
sekcji rekordu wyjściowego. Jeśli chcesz zautomatyzować proces i dołączyć wszystkie pola z position
zestawu danych do Employment
sekcji, możesz użyć symboli wieloznacznych:
inputs: [
'$context(position).*'
]
output: 'Employment.*'
Ta konfiguracja umożliwia dynamiczne mapowanie, w którym każde pole w zestawie position
danych jest kopiowane do Employment
sekcji rekordu wyjściowego:
{
"Employment": {
"Position": "Analyst",
"BaseSalary": 70000,
"WorkingHours": "Regular"
}
}
Ostatnia znana wartość
Możesz śledzić ostatnią znaną wartość właściwości. Sufiks pola wejściowego z elementem ? $last
, aby przechwycić ostatnią znaną wartość pola. Jeśli w kolejnym ładunku wejściowym brakuje właściwości, ostatnia znana wartość jest mapowana na ładunek wyjściowy.
Rozważmy na przykład następujące mapowanie:
inputs: [
'Temperature ? $last'
]
output: 'Thermostat.Temperature'
W tym przykładzie jest śledzona ostatnia znana wartość Temperature
. Jeśli kolejny ładunek wejściowy nie zawiera Temperature
wartości, ostatnia znana wartość jest używana w danych wyjściowych.