Co jsou výrazy jq v Azure IoT Data Processor Preview?
Důležité
Azure IoT Operations Preview – Služba Azure Arc je aktuálně ve verzi PREVIEW. Tento software ve verzi Preview byste neměli používat v produkčních prostředích.
Právní podmínky, které platí pro funkce Azure, které jsou ve verzi beta, verzi Preview nebo které zatím nejsou veřejně dostupné, najdete v Dodatečných podmínkách použití pro Microsoft Azure verze Preview.
Výrazy jq poskytují výkonný způsob, jak provádět výpočty a manipulace se zprávami datového kanálu. Tato příručka ukazuje jazykové vzory a přístupy pro běžné požadavky na výpočty a zpracování v datových kanálech.
Tip
Pokud si chcete vyzkoušet příklady v této příručce, můžete použít dětské hřiště jq a vložit ukázkové vstupy a výrazy do editoru.
Základy jazyka
Pokud jazyk jq neznáte, najdete v této části základy jazyka některé informace na pozadí.
Funkční programování
Jazyk jq je funkční programovací jazyk. Každá operace přebírá vstup a vytváří výstup. Několik operací se zkombinuje za účelem provádění komplexní logiky. Například s následujícím vstupem:
{
"payload": {
"temperature": 25
}
}
Tady je jednoduchý výraz jq, který určuje cestu k načtení:
.payload.temperature
Tato cesta je operace, která jako vstup přebírá hodnotu a vypíše jinou hodnotu. V tomto příkladu je 25
výstupní hodnota .
Při práci se složitými zřetězenými operacemi v jq je potřeba vzít v úvahu několik důležitých aspektů:
- Všechna data vrácená operací už nejsou ve zbytku výrazu k dispozici. Existuje několik způsobů, jak toto omezení obejít, ale obecně byste se měli zamyslet nad tím, jaká data později ve výrazu potřebujete, a zabránit tomu, aby se vyřazovat z předchozích operací.
- Výrazy se nejlépe považují za řadu transformací dat, nikoli jako sadu výpočtů, které se mají provést. Dokonce i operace, jako jsou přiřazení, představují pouze transformaci celkové hodnoty, ve které se změnilo jedno pole.
Všechno je výraz.
Ve většině nefunkčních jazyků existuje rozdíl mezi dvěma typy operací:
- Výrazy , které vytvářejí hodnotu, kterou lze použít v kontextu jiného výrazu.
- Příkazy , které místo přímé manipulace se vstupem a výstupem vytvářejí nějakou formu vedlejšího efektu.
S několika výjimkami je vše v jazyce jq výrazem. Smyčky, operace if/else a dokonce přiřazení jsou všechny výrazy, které vytvářejí novou hodnotu, místo vytváření vedlejšího efektu v systému. Například s následujícím vstupem:
{
"temperature": 21,
"humidity": 65
}
Pokud chci změnit humidity
pole na 63
, můžete použít výraz přiřazení:
.humidity = 63
I když se tento výraz jeví jako změna vstupního objektu, v jazyce jq vytváří nový objekt s novou hodnotou pro humidity
:
{
"temperature": 21,
"humidity": 63
}
Tento rozdíl vypadá jemně, ale znamená to, že výsledek této operace můžete zřetězovat pomocí dalších operací, |
jak je popsáno později.
Řetězení operací pomocí potrubí: |
Provádění výpočtů a manipulace s daty v jazyce jq často vyžaduje kombinování více operací dohromady. Operace řetězíte tak, že mezi |
ně umístíte. Pokud chcete například vypočítat délku pole dat ve zprávě:
{
"data": [5, 2, 4, 1]
}
Nejprve izolujte část zprávy, která obsahuje pole:
.data
Tento výraz poskytuje pouze pole:
[5, 2, 4, 1]
Potom pomocí length
operace vypočítáte délku tohoto pole:
length
Tento výraz vám poskytne odpověď:
4
|
Operátor použijte jako oddělovač mezi kroky, takže jako jediný výraz jq se výpočet stane:
.data | length
Pokud se pokoušíte provést složitou transformaci a tady nevidíte příklad, který přesně odpovídá vašemu problému, je pravděpodobné, že problém vyřešíte zřetězováním více řešení v této příručce se |
symbolem.
Vstupy a argumenty funkce
Jedna z primárních operací v jq volá funkci. Funkce v jq přicházejí v mnoha formách a mohou přijímat různý počet vstupů. Vstupy funkcí mají dvě formy:
- Kontext dat – data se automaticky předá do funkce pomocí jq. Obvykle jsou data vytvořená operací před posledním
|
symbolem. - Argumenty funkce – jiné výrazy a hodnoty, které zadáte ke konfiguraci chování funkce.
Mnoho funkcí má nulové argumenty a veškerou práci provádí pomocí kontextu dat, který jq poskytuje. Funkce length
je příkladem:
["a", "b", "c"] | length
V předchozím příkladu je vstupem length
pole vytvořené vlevo od symbolu |
. Funkce k výpočtu délky vstupního pole nepotřebuje žádné další vstupy. Funkce s nulovými argumenty voláte pouze pomocí jejich názvu. Jinými slovy, použijte length
, ne length()
.
Některé funkce kombinují kontext dat s jedním argumentem, který definuje jejich chování. Například map
funkce:
[1, 2, 3] | map(. * 2)
V předchozím příkladu je vstupem map
pole čísel vytvořených vlevo od symbolu |
. Funkce map
spustí výraz pro každý prvek vstupního pole. Výraz zadáte jako argument map
, v tomto případě . * 2
vynásobíte hodnotu každé položky v matici o 2 pro výstup pole [2, 4, 6]
. Pomocí funkce mapy můžete nakonfigurovat jakékoli interní chování, které chcete použít.
Některé funkce přebírají více než jeden argument. Tyto funkce fungují stejně jako funkce s jedním argumentem a pomocí symbolu ;
oddělují argumenty. Například sub
funkce:
"Hello World" | sub("World"; "jq!")
V předchozím příkladu sub
funkce obdrží jako vstupní datový kontext "Hello World" a pak přebírá dva argumenty:
- Regulární výraz, který se má vyhledat v řetězci.
- Řetězec, který nahradí všechny odpovídající podřetězce. Oddělte argumenty
;
symbolem. Stejný vzor platí pro funkce s více než dvěma argumenty.
Důležité
Nezapomeňte použít ;
jako oddělovač argumentů, nikoli ,
.
Práce s objekty
Existuje mnoho způsobů, jak extrahovat data z objektů jq, manipulovat s nimi a vytvářet je. Následující části popisují některé z nejběžnějších vzorů:
Extrahování hodnot z objektu
K načtení klíčů obvykle používáte výraz cesty. Tato operace se často kombinuje s jinými operacemi, aby se získaly složitější výsledky.
Načítání dat z objektů je snadné. Když potřebujete načíst mnoho dat z neobjektových struktur, je běžným vzorem převod neobjektových struktur na objekty. Na základě následujícího vstupu:
{
"payload": {
"values": {
"temperature": 45,
"humidity": 67
}
}
}
K načtení hodnoty vlhkosti použijte následující výraz:
.payload.values.humidity
Tento výraz vygeneruje následující výstup:
67
Změna klíčů v objektu
K přejmenování nebo úpravě klíčů objektů můžete použít with_entries
funkci. Tato funkce přebírá výraz, který pracuje s páry klíč/hodnota objektu a vrací nový objekt s výsledky výrazu.
Následující příklad ukazuje, jak přejmenovat temp
pole tak, aby temperature
odpovídalo podřízené schématu. Na základě následujícího vstupu:
{
"payload": {
"temp": 45,
"humidity": 67
}
}
Pomocí následujícího výrazu přejmenujte pole na temp
temperature
:
.payload |= with_entries(if .key == "temp" then .key = "temperature" else . end)
V předchozím výrazu jq:
.payload |= <expression>
používá|=
k aktualizaci hodnoty.payload
s výsledkem spuštění<expression>
. Použití|=
namísto=
nastavení kontextu<expression>
.payload
dat místo.
.with_entries(<expression>)
je zkratka pro spouštění několika operací společně. Provede následující operace:- Vezme objekt jako vstup a převede každý pár klíč/hodnota na položku s strukturou
{"key": <key>, "value": <value>}
. - Spustí
<expression>
se proti každé položce vygenerované z objektu a nahradí vstupní hodnotu této položky výsledkem spuštění<expression>
. - Převede transformovanou sadu položek zpět na objekt, který se používá
key
jako klíč v páru klíč/hodnota avalue
jako hodnota klíče.
- Vezme objekt jako vstup a převede každý pár klíč/hodnota na položku s strukturou
if .key == "temp" then .key = "temperature" else . end
provádí podmíněnou logiku vůči klíči položky. Pokud jetemp
klíč, převede se natemperature
ponechání hodnoty beze změny. Pokud klíč nenítemp
, položka zůstane beze změny vrácením.
z výrazu.
Následující JSON ukazuje výstup z předchozího výrazu:
{
"payload": {
"temperature": 45,
"humidity": 67
}
}
Převedení objektu na pole
I když jsou objekty užitečné pro přístup k datům, pole jsou často užitečnější, když chcete rozdělit zprávy nebo dynamicky kombinovat informace. Slouží to_entries
k převodu objektu na pole.
Následující příklad ukazuje, jak převést payload
pole na pole. Na základě následujícího vstupu:
{
"id": "abc",
"payload": {
"temperature": 45,
"humidity": 67
}
}
Pomocí následujícího výrazu převeďte pole datové části na pole:
.payload | to_entries
Následující JSON je výstup z předchozího výrazu jq:
[
{
"key": "temperature",
"value": 45
},
{
"key": "humidity",
"value": 67
}
]
Tip
Tento příklad jednoduše extrahuje pole a zahodí všechny ostatní informace ve zprávě. Pokud chcete zachovat celkovou zprávu, ale prohodit strukturu .payload
pole, použijte .payload |= to_entries
místo toho.
Vytváření objektů
Objekty vytváříte pomocí syntaxe podobné formátu JSON, kde můžete poskytnout kombinaci statických a dynamických informací.
Následující příklad ukazuje, jak zcela restrukturalizovat objekt vytvořením nového objektu s přejmenovanými poli a aktualizovanou strukturou. Na základě následujícího vstupu:
{
"payload": {
"Timestamp": 1681926048,
"Payload": {
"dtmi:com:prod1:slicer3345:humidity": {
"SourceTimestamp": 1681926048,
"Value": 10
},
"dtmi:com:prod1:slicer3345:lineStatus": {
"SourceTimestamp": 1681926048,
"Value": [1, 5, 2]
},
"dtmi:com:prod1:slicer3345:speed": {
"SourceTimestamp": 1681926048,
"Value": 85
},
"dtmi:com:prod1:slicer3345:temperature": {
"SourceTimestamp": 1681926048,
"Value": 46
}
},
"DataSetWriterName": "slicer-3345",
"SequenceNumber": 461092
}
}
Pomocí následujícího výrazu jq vytvořte objekt s novou strukturou:
{
payload: {
humidity: .payload.Payload["dtmi:com:prod1:slicer3345:humidity"].Value,
lineStatus: .payload.Payload["dtmi:com:prod1:slicer3345:lineStatus"].Value,
temperature: .payload.Payload["dtmi:com:prod1:slicer3345:temperature"].Value
},
(.payload.DataSetWriterName): "active"
}
V předchozím výrazu jq:
{payload: {<fields>}}
vytvoří objekt s literálovým polem,payload
které je samo o sobě literálovým objektem obsahujícím více polí. Tento přístup představuje nejzásadnější způsob vytváření objektů.humidity: .payload.Payload["dtmi:com:prod1:slicer3345:humidity"].Value,
vytvoří název statického klíče s dynamicky vypočítanou hodnotou. Kontext dat pro všechny výrazy v rámci konstrukce objektu je úplný vstup do výrazu konstrukce objektu, v tomto případě úplná zpráva.(.payload.DataSetWriterName): "active"
je příkladem dynamického klíče objektu. V tomto příkladu je hodnota.payload.DataSetWriterName
mapována na statickou hodnotu. Při vytváření objektu používejte statické a dynamické klíče a hodnoty v libovolné kombinaci.
Následující JSON ukazuje výstup z předchozího výrazu jq:
{
"payload": {
"humidity": 10,
"lineStatus": [1, 5, 2],
"temperature": 46
},
"slicer-3345": "active"
}
Přidání polí do objektu
Objekt můžete rozšířit přidáním polí a poskytnout tak další kontext dat. Použijte přiřazení k poli, které neexistuje.
Následující příklad ukazuje, jak přidat averageVelocity
pole do datové části. Na základě následujícího vstupu:
{
"payload": {
"totalDistance": 421,
"elapsedTime": 1598
}
}
K přidání averageVelocity
pole do datové části použijte následující výraz jq:
.payload.averageVelocity = (.payload.totalDistance / .payload.elapsedTime)
Na rozdíl od jiných příkladů, které používají |=
symbol, tento příklad používá standardní přiřazení, =
. Proto nenasadí výraz na pravé straně na pole vlevo. Tento přístup je nezbytný, abyste měli přístup k jiným polím datové části.
Následující JSON ukazuje výstup z předchozího výrazu jq:
{
"payload": {
"totalDistance": 421,
"elapsedTime": 1598,
"averageVelocity": 0.2634543178973717
}
}
Podmíněné přidání polí do objektu
Kombinace podmíněné logiky se syntaxí pro přidání polí do objektu umožňuje scénáře, jako je přidání výchozích hodnot pro pole, která nejsou k dispozici.
Následující příklad ukazuje, jak přidat jednotku k měření teploty, které ho nemají. Výchozí jednotka je celsia. Na základě následujícího vstupu:
{
"payload": [
{
"timestamp": 1689712296407,
"temperature": 59.2,
"unit": "fahrenheit"
},
{
"timestamp": 1689712399609,
"temperature": 52.2
},
{
"timestamp": 1689712400342,
"temperature": 50.8,
"unit": "celsius"
}
]
}
Pomocí následujícího výrazu jq přidejte jednotku k měření teploty, které nemají:
.payload |= map(.unit //= "celsius")
V předchozím výrazu jq:
.payload |= <expression>
používá|=
k aktualizaci hodnoty.payload
s výsledkem spuštění<expression>
. Použití|=
namísto=
nastavení kontextu<expression>
.payload
dat místo.
.map(<expression>)
<expression>
provede pro každou položku v matici a nahradí vstupní hodnotu tím, co<expression>
vytvoří..unit //= "celsius"
používá speciální//=
přiřazení. Toto přiřazení kombinuje (=
) s alternativním operátorem (//
) k přiřazení stávající hodnoty.unit
k sobě, pokud nefalse
, nebonull
. Pokud.unit
je false nebo null, výraz se"celsius"
přiřadí jako hodnota.unit
, vytvoření.unit
v případě potřeby.
Následující JSON ukazuje výstup z předchozího výrazu jq:
{
"payload": [
{
"timestamp": 1689712296407,
"temperature": 59.2,
"unit": "fahrenheit"
},
{
"timestamp": 1689712399609,
"temperature": 52.2,
"unit": "celsius"
},
{
"timestamp": 1689712400342,
"temperature": 50.8,
"unit": "celsius"
}
]
}
Odebrání polí z objektu
del
Funkce slouží k odebrání nepotřebných polí z objektu.
Následující příklad ukazuje, jak odebrat timestamp
pole, protože není relevantní pro zbytek výpočtu. Na základě následujícího vstupu:
{
"payload": {
"timestamp": "2023-07-18T20:57:23.340Z",
"temperature": 153,
"pressure": 923,
"humidity": 24
}
}
Pomocí následujícího výrazu jq odeberete timestamp
pole:
del(.payload.timestamp)
Následující JSON ukazuje výstup z předchozího výrazu jq:
{
"payload": {
"temperature": 153,
"pressure": 923,
"humidity": 24
}
}
Práce s poli
Pole jsou základním stavebním blokem pro iteraci a rozdělení zpráv v jq. Následující příklady ukazují, jak manipulovat s poli.
Extrahování hodnot z pole
Pole je obtížnější zkontrolovat než objekty, protože data mohou být umístěna v různých indexech pole v různých zprávách. Proto pokud chcete extrahovat hodnoty z pole, musíte často prohledávat pole pro potřebná data.
Následující příklad ukazuje, jak extrahovat několik hodnot z pole a vytvořit nový objekt, který obsahuje data, která vás zajímají. Na základě následujícího vstupu:
{
"payload": {
"data": [
{
"field": "dtmi:com:prod1:slicer3345:humidity",
"value": 10
},
{
"field": "dtmi:com:prod1:slicer3345:lineStatus",
"value": [1, 5, 2]
},
{
"field": "dtmi:com:prod1:slicer3345:speed",
"value": 85
},
{
"field": "dtmi:com:prod1:slicer3345:temperature",
"value": 46
}
],
"timestamp": "2023-07-18T20:57:23.340Z"
}
}
Pomocí následujícího výrazu timestamp
jq extrahujte objekt , humidity
temperature
a pressure
hodnoty z pole a vytvořte nový objekt:
.payload |= {
timestamp,
temperature: .data | map(select(.field == "dtmi:com:prod1:slicer3345:temperature"))[0]?.value,
humidity: .data | map(select(.field == "dtmi:com:prod1:slicer3345:humidity"))[0]?.value,
pressure: .data | map(select(.field == "dtmi:com:prod1:slicer3345:pressure"))[0]?.value,
}
V předchozím výrazu jq:
.payload |= <expression>
používá|=
k aktualizaci hodnoty.payload
s výsledkem spuštění<expression>
. Použití|=
namísto=
nastavení kontextu<expression>
.payload
dat místo.
.{timestamp, <other-fields>}
je zkratka protimestamp: .timestamp
, která přidá časové razítko jako pole k objektu pomocí pole se stejným názvem z původního objektu.<other-fields>
přidá do objektu další pole.temperature: <expression>, humidity: <expression>, pressure: <expression>
nastavte teplotu, vlhkost a tlak ve výsledném objektu na základě výsledků tří výrazů..data | <expression>
definuje výpočet hodnoty nadata
pole datové části a provede<expression>
se v poli.map(<expression>)[0]?.value
dělá několik věcí:map(<expression>)
<expression>
provádí proti každému prvku v poli, který vrací výsledek spuštění tohoto výrazu pro každý prvek.[0]
extrahuje první prvek výsledného pole.?
umožňuje další řetězení segmentu cesty, i když předchozí hodnota je null. Pokud je předchozí hodnota null, následující cesta také vrátí hodnotu null místo selhání..value
extrahujevalue
pole z výsledku.
select(.field == "dtmi:com:prod1:slicer3345:temperature")
spustí logický výraz uvnitřselect()
vstupu. Pokud je výsledek pravdivý, vstup se předá. Pokud je výsledek false, vstup se zahodí.map(select(<expression>))
je běžná kombinace používaná k filtrování prvků v poli.
Následující JSON ukazuje výstup z předchozího výrazu jq:
{
"payload": {
"timestamp": "2023-07-18T20:57:23.340Z",
"temperature": 46,
"humidity": 10,
"pressure": null
}
}
Změna položek pole
Upravte položky v poli pomocí výrazu map()
. Pomocí těchto výrazů můžete upravit každý prvek pole.
Následující příklad ukazuje, jak převést časové razítko každé položky v poli z unix milisekund času na RFC3339 řetězec. Na základě následujícího vstupu:
{
"payload": [
{
"field": "humidity",
"timestamp": 1689723806615,
"value": 10
},
{
"field": "lineStatus",
"timestamp": 1689723849747,
"value": [1, 5, 2]
},
{
"field": "speed",
"timestamp": 1689723868830,
"value": 85
},
{
"field": "temperature",
"timestamp": 1689723880530,
"value": 46
}
]
}
Pomocí následujícího výrazu jq převeďte časové razítko každé položky v poli z unixového milisekundového času na řetězec RFC3339:
.payload |= map(.timestamp |= (. / 1000 | strftime("%Y-%m-%dT%H:%M:%SZ")))
V předchozím výrazu jq:
.payload |= <expression>
používá|=
k aktualizaci hodnoty.payload
s výsledkem spuštění<expression>
. Použití|=
namísto=
nastavení kontextu<expression>
.payload
dat místo.
.map(<expression>)
<expression>
provádí proti každému prvku v poli, přičemž každý nahradí výstupem spuštění<expression>
..timestamp |= <expression>
nastaví časové razítko na novou hodnotu na základě spuštění<expression>
, kde kontext dat pro<expression>
hodnotu.timestamp
.(. / 1000 | strftime("%Y-%m-%dT%H:%M:%SZ"))
převede milisekundový čas na sekundy a používá formátovač časového řetězce k vytvoření časového razítka ISO 8601.
Následující JSON ukazuje výstup z předchozího výrazu jq:
{
"payload": [
{
"field": "humidity",
"timestamp": "2023-07-18T23:43:26Z",
"value": 10
},
{
"field": "lineStatus",
"timestamp": "2023-07-18T23:44:09Z",
"value": [1, 5, 2]
},
{
"field": "speed",
"timestamp": "2023-07-18T23:44:28Z",
"value": 85
},
{
"field": "temperature",
"timestamp": "2023-07-18T23:44:40Z",
"value": 46
}
]
}
Převedení pole na objekt
Chcete-li restrukturalizovat pole do objektu, aby bylo snazší získat přístup k požadovanému schématu nebo ho splňovat, použijte from_entries
. Na základě následujícího vstupu:
{
"payload": [
{
"field": "humidity",
"timestamp": 1689723806615,
"value": 10
},
{
"field": "lineStatus",
"timestamp": 1689723849747,
"value": [1, 5, 2]
},
{
"field": "speed",
"timestamp": 1689723868830,
"value": 85
},
{
"field": "temperature",
"timestamp": 1689723880530,
"value": 46
}
]
}
Pomocí následujícího výrazu jq převeďte pole na objekt:
.payload |= (
map({key: .field, value: {timestamp, value}})
| from_entries
)
V předchozím výrazu jq:
.payload |= <expression>
používá|=
k aktualizaci hodnoty.payload
s výsledkem spuštění<expression>
. Použití|=
namísto=
nastavení kontextu<expression>
.payload
dat místo.
.map({key: <expression>, value: <expression>})
převede každý prvek pole na objekt formuláře{"key": <data>, "value": <data>}
, což je strukturafrom_entries
potřeb.{key: .field, value: {timestamp, value}}
vytvoří objekt z položky pole, mapovánífield
na klíč a vytvoření hodnoty, která je objekt držítimestamp
avalue
.{timestamp, value}
je zkratka pro{timestamp: .timestamp, value: .value}
.<expression> | from_entries
převede maticovou hodnotu<expression>
na objekt, namapujekey
pole každé položky pole na klíč objektu avalue
pole každé položky pole na hodnotu tohoto klíče.
Následující JSON ukazuje výstup z předchozího výrazu jq:
{
"payload": {
"humidity": {
"timestamp": 1689723806615,
"value": 10
},
"lineStatus": {
"timestamp": 1689723849747,
"value": [1, 5, 2]
},
"speed": {
"timestamp": 1689723868830,
"value": 85
},
"temperature": {
"timestamp": 1689723880530,
"value": 46
}
}
}
Vytváření polí
Vytváření maticových literálů je podobné vytváření literálů objektů. Syntaxe jq pro literál pole je podobná json a JavaScript.
Následující příklad ukazuje, jak extrahovat některé hodnoty do jednoduchého pole pro pozdější zpracování.
Na základě následujícího vstupu:
{
"payload": {
"temperature": 14,
"humidity": 56,
"pressure": 910
}
}
Následující výraz jq vytvoří pole z hodnot temperature
humidity
, a pressure
polí:
.payload |= ([.temperature, .humidity, .pressure])
Následující JSON ukazuje výstup z předchozího výrazu jq:
{
"payload": [14, 56, 910]
}
Přidání položek do pole
Položky můžete přidat na začátek nebo konec pole pomocí +
operátoru s polem a jeho novými položkami. Operátor +=
tuto operaci zjednodušuje automatickou aktualizací pole novými položkami na konci. Na základě následujícího vstupu:
{
"payload": {
"Timestamp": 1681926048,
"Payload": {
"dtmi:com:prod1:slicer3345:humidity": {
"SourceTimestamp": 1681926048,
"Value": 10
},
"dtmi:com:prod1:slicer3345:lineStatus": {
"SourceTimestamp": 1681926048,
"Value": [1, 5, 2]
},
"dtmi:com:prod1:slicer3345:speed": {
"SourceTimestamp": 1681926048,
"Value": 85
},
"dtmi:com:prod1:slicer3345:temperature": {
"SourceTimestamp": 1681926048,
"Value": 46
}
},
"DataSetWriterName": "slicer-3345",
"SequenceNumber": 461092
}
}
Pomocí následujícího výrazu jq přidejte hodnoty 12
a 41
na konec lineStatus
pole hodnot:
.payload.Payload["dtmi:com:prod1:slicer3345:lineStatus"].Value += [12, 41]
Následující JSON ukazuje výstup z předchozího výrazu jq:
{
"payload": {
"Timestamp": 1681926048,
"Payload": {
"dtmi:com:prod1:slicer3345:humidity": {
"SourceTimestamp": 1681926048,
"Value": 10
},
"dtmi:com:prod1:slicer3345:lineStatus": {
"SourceTimestamp": 1681926048,
"Value": [1, 5, 2, 12, 41]
},
"dtmi:com:prod1:slicer3345:speed": {
"SourceTimestamp": 1681926048,
"Value": 85
},
"dtmi:com:prod1:slicer3345:temperature": {
"SourceTimestamp": 1681926048,
"Value": 46
}
},
"DataSetWriterName": "slicer-3345",
"SequenceNumber": 461092
}
}
Odebrání položek z pole
del
Funkce slouží k odebrání položek z pole stejným způsobem jako u objektu. Na základě následujícího vstupu:
{
"payload": {
"Timestamp": 1681926048,
"Payload": {
"dtmi:com:prod1:slicer3345:humidity": {
"SourceTimestamp": 1681926048,
"Value": 10
},
"dtmi:com:prod1:slicer3345:lineStatus": {
"SourceTimestamp": 1681926048,
"Value": [1, 5, 2]
},
"dtmi:com:prod1:slicer3345:speed": {
"SourceTimestamp": 1681926048,
"Value": 85
},
"dtmi:com:prod1:slicer3345:temperature": {
"SourceTimestamp": 1681926048,
"Value": 46
}
},
"DataSetWriterName": "slicer-3345",
"SequenceNumber": 461092
}
}
Pomocí následujícího výrazu jq odeberte druhou položku z lineStatus
pole hodnot:
del(.payload.Payload["dtmi:com:prod1:slicer3345:lineStatus"].Value[1])
Následující JSON ukazuje výstup z předchozího výrazu jq:
{
"payload": {
"Timestamp": 1681926048,
"Payload": {
"dtmi:com:prod1:slicer3345:humidity": {
"SourceTimestamp": 1681926048,
"Value": 10
},
"dtmi:com:prod1:slicer3345:lineStatus": {
"SourceTimestamp": 1681926048,
"Value": [1, 2]
},
"dtmi:com:prod1:slicer3345:speed": {
"SourceTimestamp": 1681926048,
"Value": 85
},
"dtmi:com:prod1:slicer3345:temperature": {
"SourceTimestamp": 1681926048,
"Value": 46
}
},
"DataSetWriterName": "slicer-3345",
"SequenceNumber": 461092
}
}
Odebrání duplicitních položek pole
Pokud se prvky pole překrývají, můžete duplicitní položky odebrat. Ve většině programovacích jazyků můžete duplicity odebrat pomocí vedlejších vyhledávacích proměnných. V jazyce jq je nejlepší uspořádat data do způsobu jejich zpracování a pak provést jakékoli operace před převodem zpět do požadovaného formátu.
Následující příklad ukazuje, jak vzít zprávu s některými hodnotami v ní a pak ji filtrovat, abyste měli jen nejnovější čtení pro každou hodnotu. Na základě následujícího vstupu:
{
"payload": [
{
"name": "temperature",
"value": 12,
"timestamp": 1689727870701
},
{
"name": "humidity",
"value": 51,
"timestamp": 1689727944440
},
{
"name": "temperature",
"value": 15,
"timestamp": 1689727994085
},
{
"name": "humidity",
"value": 25,
"timestamp": 1689727914558
},
{
"name": "temperature",
"value": 31,
"timestamp": 1689727987072
}
]
}
Pomocí následujícího výrazu jq vyfiltrujte vstup, abyste měli jenom nejnovější čtení pro každou hodnotu:
.payload |= (group_by(.name) | map(sort_by(.timestamp)[-1]))
Tip
Pokud vás nezajímá načtení nejnovější hodnoty pro každý název, můžete výraz zjednodušit. .payload |= unique_by(.name)
V předchozím výrazu jq:
.payload |= <expression>
používá|=
k aktualizaci hodnoty.payload
s výsledkem spuštění<expression>
. Použití|=
namísto=
nastavení kontextu<expression>
.payload
dat místo.
.group_by(.name)
zadanou matici jako vstup umístí prvky do dílčích.name
polí na základě hodnoty v každém prvku. Každé dílčí pole obsahuje všechny prvky z původního pole se stejnou hodnotou.name
.map(<expression>)
přebírá pole polí vytvořenágroup_by
a provádí<expression>
proti každému z dílčích polí.sort_by(.timestamp)[-1]
extrahuje prvek, který vás zajímá, z každého dílčího pole:sort_by(.timestamp)
seřídí prvky zvýšením hodnoty pole.timestamp
pro aktuální dílčí pole.[-1]
načte poslední prvek z seřazeného dílčího pole, což je položka s nejnovějším časem pro každý název.
Následující JSON ukazuje výstup z předchozího výrazu jq:
{
"payload": [
{
"name": "humidity",
"value": 51,
"timestamp": 1689727944440
},
{
"name": "temperature",
"value": 15,
"timestamp": 1689727994085
}
]
}
Výpočet hodnot napříč prvky pole
Hodnoty prvků pole můžete zkombinovat a vypočítat hodnoty, jako jsou průměry napříč prvky.
Tento příklad ukazuje, jak snížit pole načtením nejvyššího časového razítka a průměrné hodnoty pro položky, které sdílejí stejný název. Na základě následujícího vstupu:
{
"payload": [
{
"name": "temperature",
"value": 12,
"timestamp": 1689727870701
},
{
"name": "humidity",
"value": 51,
"timestamp": 1689727944440
},
{
"name": "temperature",
"value": 15,
"timestamp": 1689727994085
},
{
"name": "humidity",
"value": 25,
"timestamp": 1689727914558
},
{
"name": "temperature",
"value": 31,
"timestamp": 1689727987072
}
]
}
Pomocí následujícího výrazu jq načtěte nejvyšší časové razítko a průměrnou hodnotu pro položky, které sdílejí stejný název:
.payload |= (group_by(.name) | map(
{
name: .[0].name,
value: map(.value) | (add / length),
timestamp: map(.timestamp) | max
}
))
V předchozím výrazu jq:
.payload |= <expression>
používá|=
k aktualizaci hodnoty.payload
s výsledkem spuštění<expression>
. Použití|=
namísto=
nastavení kontextu<expression>
.payload
dat místo.
.group_by(.name)
přebírá pole jako vstup, umístí elementy do dílčích polí na základě hodnoty.name
v každém prvku. Každé dílčí pole obsahuje všechny prvky z původního pole se stejnou hodnotou.name
.map(<expression>)
přebírá pole polí vytvořenágroup_by
a provádí<expression>
proti každému z dílčích polí.{name: <expression>, value: <expression>, timestamp: <expression>}
vytvoří objekt ze vstupního dílčího pole s polemname
,value
atimestamp
pole. Každá<expression>
z nich vytvoří požadovanou hodnotu pro přidružený klíč..[0].name
načte první prvek z dílčího pole a extrahujename
z něj pole. Všechny prvky v podarray mají stejný název, takže stačí načíst pouze první.map(.value) | (add / length)
vypočítá průměrvalue
jednotlivých dílčích polí:map(.value)
převede dílčí pole na maticivalue
pole v každé položce, v tomto případě vrátí matici čísel.add
je integrovaná funkce jq, která vypočítá součet pole čísel.length
je integrovaná funkce jq, která vypočítá počet nebo délku pole.add / length
vydělí součet počtem k určení průměru.
map(.timestamp) | max
najde maximálnítimestamp
hodnotu každého dílčího pole:map(.timestamp)
převede dílčí pole na poletimestamp
polí v každé položce, v tomto případě vrátí matici čísel.max
je integrovaná funkce jq, která najde maximální hodnotu v poli.
Následující JSON ukazuje výstup z předchozího výrazu jq:
{
"payload": [
{
"name": "humidity",
"value": 38,
"timestamp": 1689727944440
},
{
"name": "temperature",
"value": 19.333333333333332,
"timestamp": 1689727994085
}
]
}
Práce s řetězci
jq poskytuje několik nástrojů pro manipulaci a vytváření řetězců. Následující příklady ukazují některé běžné případy použití.
Rozdělení řetězců
Pokud řetězec obsahuje více informací oddělených běžným znakem, můžete pomocí split()
funkce extrahovat jednotlivé části.
Následující příklad ukazuje, jak rozdělit řetězec tématu a vrátit konkrétní segment tématu. Tato technika je často užitečná při práci s výrazy klíče oddílu. Na základě následujícího vstupu:
{
"systemProperties": {
"timestamp": "2023-01-11T10:02:07Z"
},
"qos": 1,
"topic": "assets/slicer-3345/tags/rpm",
"properties": {
"contentType": "application/json"
},
"payload": {
"Timestamp": 1681926048,
"Value": 142
}
}
Pomocí následujícího výrazu jq rozdělte řetězec tématu, použijte /
jako oddělovač a vraťte konkrétní segment tématu:
.topic | split("/")[1]
V předchozím výrazu jq:
.topic | <expression>
vybere klíč z kořenového objektu a spustí<expression>
se s datytopic
, která obsahuje.split("/")
rozdělí řetězec tématu do pole tak, že řetězec rozdělí od sebe pokaždé, když najde/
znak v řetězci. V tomto případě produkuje["assets", "slicer-3345", "tags", "rpm"]
.[1]
načte prvek na indexu 1 pole z předchozího kroku v tomto případěslicer-3345
.
Následující JSON ukazuje výstup z předchozího výrazu jq:
"slicer-3345"
Dynamické vytváření řetězců
jq umožňuje vytvářet řetězce pomocí šablon řetězců se syntaxí \(<expression>)
v řetězci. Pomocí těchto šablon můžete dynamicky vytvářet řetězce.
Následující příklad ukazuje, jak přidat předponu ke každému klíči v objektu pomocí šablon řetězců. Na základě následujícího vstupu:
{
"temperature": 123,
"humidity": 24,
"pressure": 1021
}
Pomocí následujícího výrazu jq přidejte předponu ke každému klíči v objektu:
with_entries(.key |= "current-\(.)")
V předchozím výrazu jq:
with_entries(<expression>)
převede objekt na pole párů klíč/hodnota se strukturou{key: <key>, value: <value>}
, provede<expression>
se s každou dvojicí klíč/hodnota a převede dvojice zpět na objekt..key |= <expression>
aktualizuje hodnotu v objektu páru.key
klíč/hodnota na výsledek .<expression>
=
Místo|=
nastavení datového kontextu<expression>
na hodnotu.key
objektu páru klíč/hodnota použijte místo celého objektu páru klíč/hodnota."current-\(.)"
vytvoří řetězec, který začíná na "current-" a potom vloží hodnotu aktuálního kontextu.
dat , v tomto případě hodnotu klíče. Syntaxe\(<expression>)
v řetězci označuje, že chcete nahradit tuto část řetězce výsledkem spuštění<expression>
.
Následující JSON ukazuje výstup z předchozího výrazu jq:
{
"current-temperature": 123,
"current-humidity": 24,
"current-pressure": 1021
}
Práce s regulárními výrazy
jq podporuje standardní regulární výrazy. Regulární výrazy můžete použít k extrakci, nahrazení a kontrole vzorů v řetězcích. Běžné funkce regulárního výrazu pro jq zahrnují test()
, match()
, split()
, capture()
, , sub()
a gsub()
.
Extrahování hodnot pomocí regulárních výrazů
Pokud nemůžete k extrakci hodnoty z řetězce použít dělení řetězců, zkuste k extrahování hodnot, které potřebujete, použít regulární výrazy.
Následující příklad ukazuje, jak normalizovat klíče objektů testováním regulárního výrazu a následným nahrazením jiným formátem. Na základě následujícího vstupu:
{
"payload": {
"Timestamp": 1681926048,
"Payload": {
"dtmi:com:prod1:slicer3345:humidity": {
"SourceTimestamp": 1681926048,
"Value": 10
},
"dtmi:com:prod1:slicer3345:speed": {
"SourceTimestamp": 1681926048,
"Value": 85
},
"temperature": {
"SourceTimestamp": 1681926048,
"Value": 46
}
},
"DataSetWriterName": "slicer-3345",
"SequenceNumber": 461092
}
}
K normalizaci klíčů objektů použijte následující výraz jq:
.payload.Payload |= with_entries(
.key |= if test("^dtmi:.*:(?<tag>[^:]+)$") then
capture("^dtmi:.*:(?<tag>[^:]+)$").tag
else
.
end
)
V předchozím výrazu jq:
.payload |= <expression>
používá|=
k aktualizaci hodnoty.payload
s výsledkem spuštění<expression>
. Použití|=
namísto=
nastavení kontextu<expression>
.payload
dat místo.
.with_entries(<expression>)
převede objekt na pole párů klíč/hodnota se strukturou{key: <key>, value: <value>}
, provede<expression>
se s každou dvojicí klíč/hodnota a převede dvojice zpět na objekt..key |= <expression>
aktualizuje hodnotu v objektu páru.key
klíč/hodnota na výsledek .<expression>
=
namísto|=
nastavení datového kontextu<expression>
na hodnotu.key
objektu páru klíč/hodnota.if test("^dtmi:.*:(?<tag>[^:]+)$") then capture("^dtmi:.*:(?<tag>[^:]+)$").tag else . end
kontroluje a aktualizuje klíč na základě regulárního výrazu:test("^dtmi:.*:(?<tag>[^:]+)$")
zkontroluje kontext vstupních dat, klíč v tomto případě, proti regulárnímu výrazu^dtmi:.*:(?<tag>[^:]+)$
. Pokud regulární výraz odpovídá, vrátí hodnotu true. Pokud ne, vrátí hodnotu false.capture("^dtmi:.*:(?<tag>[^:]+)$").tag
spustí regulární výraz^dtmi:.*:(?<tag>[^:]+)$
proti kontextu vstupních dat, klíč v tomto případě a umístí všechny skupiny zachycení z regulárního výrazu označeného objektem(?<tag>...)
jako výstup. Výraz pak extrahuje.tag
z daného objektu, aby vrátil informace extrahované regulárním výrazem..
else
ve větvi výraz předává data beze změny.
Následující JSON ukazuje výstup z předchozího výrazu jq:
{
"payload": {
"Timestamp": 1681926048,
"Payload": {
"humidity": {
"SourceTimestamp": 1681926048,
"Value": 10
},
"speed": {
"SourceTimestamp": 1681926048,
"Value": 85
},
"temperature": {
"SourceTimestamp": 1681926048,
"Value": 46
}
},
"DataSetWriterName": "slicer-3345",
"SequenceNumber": 461092
}
}
Rozdělení zpráv mezi
Užitečnou funkcí jazyka jq je schopnost vytvářet více výstupů z jednoho vstupu. Tato funkce umožňuje rozdělit zprávy do několika samostatných zpráv, aby kanál zpracovával. Klíčem k této technice je .[]
, který rozdělí pole na samostatné hodnoty. Následující příklady ukazují několik scénářů, které používají tuto syntaxi.
Dynamický počet výstupů
Obvykle platí, že když chcete zprávu rozdělit na více výstupů, počet požadovaných výstupů závisí na struktuře zprávy. Syntaxe []
umožňuje tento typ rozdělení.
Máte například zprávu se seznamem značek, které chcete umístit do samostatných zpráv. Na základě následujícího vstupu:
{
"systemProperties": {
"partitionKey": "slicer-3345",
"partitionId": 5,
"timestamp": "2023-01-11T10:02:07Z"
},
"qos": 1,
"topic": "assets/slicer-3345",
"properties": {
"responseTopic": "assets/slicer-3345/output",
"contentType": "application/json"
},
"payload": {
"Timestamp": 1681926048,
"Payload": {
"dtmi:com:prod1:slicer3345:humidity": {
"sourceTimestamp": 1681926048,
"value": 10
},
"dtmi:com:prod1:slicer3345:lineStatus": {
"sourceTimestamp": 1681926048,
"value": [1, 5, 2]
},
"dtmi:com:prod1:slicer3345:speed": {
"sourceTimestamp": 1681926048,
"value": 85
},
"dtmi:com:prod1:slicer3345:temperature": {
"sourceTimestamp": 1681926048,
"value": 46
}
},
"DataSetWriterName": "slicer-3345",
"SequenceNumber": 461092
}
}
K rozdělení zprávy na více zpráv použijte následující výraz jq:
.payload.Payload = (.payload.Payload | to_entries[])
| .payload |= {
DataSetWriterName,
SequenceNumber,
Tag: .Payload.key,
Value: .Payload.value.value,
Timestamp: .Payload.value.sourceTimestamp
}
V předchozím výrazu jq:
.payload.Payload = (.payload.Payload | to_entries[])
rozdělí zprávu na několik zpráv:.payload.Payload = <expression>
přiřadí výsledek spuštění<expression>
..payload.Payload
V tomto případě se obvykle používá|=
k určení rozsahu<expression>
kontextu dolů na.payload.Payload
, ale|=
nepodporuje rozdělení zprávy od sebe, takže místo toho použijte=
.(.payload.Payload | <expression>)
definuje pravou stranu výrazu přiřazení tak.payload.Payload
, aby<expression>
fungovala proti správné části zprávy.to_entries[]
je dvě operace a je zkratka proto_entries | .[]
:to_entries
převede objekt na pole párů klíč/hodnota se schématem{"key": <key>, "value": <value>}
. Tyto informace jsou to, co chcete rozdělit do různých zpráv.[]
provede rozdělení zprávy. Každá položka v poli se stane samostatnou hodnotou v jq. Když dojde k.payload.Payload
přiřazení, každá samostatná hodnota má za následek kopii celkové zprávy, která.payload.Payload
je nastavena na odpovídající hodnotu vytvořenou pravou stranou přiřazení.
.payload |= <expression>
nahradí hodnotu.payload
výsledkem spuštění<expression>
. V tomto okamžiku dotaz pracuje s datovým proudem hodnot, nikoli s jednou hodnotou v důsledku rozdělení v předchozí operaci. Proto se přiřazení provede jednou pro každou zprávu, že předchozí operace vytvoří místo pouhého provádění jednou celkově.{DataSetWriterName, SequenceNumber, ...}
vytvoří nový objekt, který je hodnotou.payload
.DataSetWriterName
aSequenceNumber
jsou beze změny, takže můžete použít zkrácenou syntaxi namísto psaníDataSetWriterName: .DataSetWriterName
aSequenceNumber: .SequenceNumber
.Tag: .Payload.key,
extrahuje původní klíč objektu z vnitřníPayload
a vyšší úrovně do nadřazeného objektu. Operaceto_entries
dříve v dotazu vytvořilakey
pole.Value: .Payload.value.value
aTimestamp: .Payload.value.sourceTimestamp
proveďte podobnou extrakci dat z vnitřní datové části. Tentokrát z hodnoty původní dvojice klíč/hodnota. Výsledkem je objekt ploché datové části, který můžete použít při dalším zpracování.
Následující JSON ukazuje výstupy z předchozího výrazu jq. Každý výstup se stane samostatnou zprávou pro pozdější fáze zpracování v kanálu:
{
"systemProperties": {
"partitionKey": "slicer-3345",
"partitionId": 5,
"timestamp": "2023-01-11T10:02:07Z"
},
"qos": 1,
"topic": "assets/slicer-3345",
"properties": {
"responseTopic": "assets/slicer-3345/output",
"contentType": "application/json"
},
"payload": {
"DataSetWriterName": "slicer-3345",
"SequenceNumber": 461092,
"Tag": "dtmi:com:prod1:slicer3345:humidity",
"Value": 10,
"Timestamp": 1681926048
}
}
{
"systemProperties": {
"partitionKey": "slicer-3345",
"partitionId": 5,
"timestamp": "2023-01-11T10:02:07Z"
},
"qos": 1,
"topic": "assets/slicer-3345",
"properties": {
"responseTopic": "assets/slicer-3345/output",
"contentType": "application/json"
},
"payload": {
"DataSetWriterName": "slicer-3345",
"SequenceNumber": 461092,
"Tag": "dtmi:com:prod1:slicer3345:lineStatus",
"Value": [1, 5, 2],
"Timestamp": 1681926048
}
}
{
"systemProperties": {
"partitionKey": "slicer-3345",
"partitionId": 5,
"timestamp": "2023-01-11T10:02:07Z"
},
"qos": 1,
"topic": "assets/slicer-3345",
"properties": {
"responseTopic": "assets/slicer-3345/output",
"contentType": "application/json"
},
"payload": {
"DataSetWriterName": "slicer-3345",
"SequenceNumber": 461092,
"Tag": "dtmi:com:prod1:slicer3345:speed",
"Value": 85,
"Timestamp": 1681926048
}
}
{
"systemProperties": {
"partitionKey": "slicer-3345",
"partitionId": 5,
"timestamp": "2023-01-11T10:02:07Z"
},
"qos": 1,
"topic": "assets/slicer-3345",
"properties": {
"responseTopic": "assets/slicer-3345/output",
"contentType": "application/json"
},
"payload": {
"DataSetWriterName": "slicer-3345",
"SequenceNumber": 461092,
"Tag": "dtmi:com:prod1:slicer3345:temperature",
"Value": 46,
"Timestamp": 1681926048
}
}
Pevný počet výstupů
Chcete-li rozdělit zprávu na pevný počet výstupů místo dynamického počtu výstupů na základě struktury zprávy, použijte ,
operátor místo []
.
Následující příklad ukazuje, jak rozdělit data do dvou zpráv na základě existujících názvů polí. Na základě následujícího vstupu:
{
"topic": "test/topic",
"payload": {
"minTemperature": 12,
"maxTemperature": 23,
"minHumidity": 52,
"maxHumidity": 92
}
}
Pomocí následujícího výrazu jq rozdělte zprávu na dvě zprávy:
.payload = (
{
field: "temperature",
minimum: .payload.minTemperature,
maximum: .payload.maxTemperature
},
{
field: "humidity",
minimum: .payload.minHumidity,
maximum: .payload.maxHumidity
}
)
V předchozím výrazu jq:
.payload = ({<fields>},{<fields>})
přiřadí do zprávy dva literály.payload
objektu. Objekty oddělené čárkami vytvářejí dvě samostatné hodnoty a přiřazují se do.payload
, což způsobí rozdělení celé zprávy do dvou zpráv. Každá nová zpráva je nastavená.payload
na jednu z hodnot.{field: "temperature", minimum: .payload.minTemperature, maximum: .payload.maxTemperature}
je literálový konstruktor objektu, který naplní pole objektu literálovým řetězcem a dalšími daty načtenými z objektu.
Následující JSON ukazuje výstupy z předchozího výrazu jq. Každý výstup se stane samostatnou zprávou pro další fáze zpracování:
{
"topic": "test/topic",
"payload": {
"field": "temperature",
"minimum": 12,
"maximum": 23
}
}
{
"topic": "test/topic",
"payload": {
"field": "humidity",
"minimum": 52,
"maximum": 92
}
}
Matematické operace
jq podporuje běžné matematické operace. Některé operace jsou operátory, jako +
jsou a -
. Další operace jsou funkce jako sin
a exp
.
Aritmetické
jq podporuje pět běžných aritmetických operací: sčítání (+
), odčítání (-
), násobení (*
), dělení (/
) a modulo (%
). Na rozdíl od mnoha funkcí jazyka jq jsou tyto operace operace infix, které umožňují psát úplný matematický výraz v jednom výrazu bez |
oddělovačů.
Následující příklad ukazuje, jak převést teplotu z fahrenheita na stupně celsia a extrahovat aktuální sekundy čtení z časového razítka unix milisekund. Na základě následujícího vstupu:
{
"payload": {
"temperatureF": 94.2,
"timestamp": 1689766750628
}
}
Pomocí následujícího výrazu jq převeďte teplotu z fahrenheita na stupně Celsia a extrahujte aktuální sekundy čtení z časového razítka unixu milisekund:
.payload.temperatureC = (5/9) * (.payload.temperatureF - 32)
| .payload.seconds = (.payload.timestamp / 1000) % 60
V předchozím výrazu jq:
.payload.temperatureC = (5/9) * (.payload.temperatureF - 32)
vytvoří novétemperatureC
pole v datové části nastavené na převodtemperatureF
z fahrenheita na stupně celsia..payload.seconds = (.payload.timestamp / 1000) % 60
vezme unixový milisekundový čas a převede ho na sekundy a pak pomocí výpočtu moduluo extrahuje počet sekund v aktuální minutě.
Následující JSON ukazuje výstup z předchozího výrazu jq:
{
"payload": {
"temperatureF": 94.2,
"timestamp": 1689766750628,
"temperatureC": 34.55555555555556,
"seconds": 10
}
}
Matematické funkce
jq obsahuje několik funkcí, které provádějí matematické operace. Úplný seznam najdete v příručce jq.
Následující příklad ukazuje, jak vypočítat kinetickou energii z pole hmotnosti a rychlosti. Na základě následujícího vstupu:
{
"userProperties": [
{ "key": "mass", "value": 512.1 },
{ "key": "productType", "value": "projectile" }
],
"payload": {
"velocity": 97.2
}
}
K výpočtu kinetické energie z hromadných a rychlostních polí použijte následující výraz jq:
.payload.energy = (0.5 * (.userProperties | from_entries).mass * pow(.payload.velocity; 2) | round)
V předchozím výrazu jq:
.payload.energy = <expression>
vytvoří v datové části novéenergy
pole, které je výsledkem provádění<expression>
.(0.5 * (.userProperties | from_entries).mass * pow(.payload.velocity; 2) | round)
je vzorec pro energii:(.userProperties | from_entries).mass
extrahujemass
položku zeuserProperties
seznamu. Data jsou již nastavena jako objekty skey
avalue
, takžefrom_entries
je lze přímo převést na objekt. Výraz načtemass
klíč z výsledného objektu a vrátí jeho hodnotu.pow(.payload.velocity; 2)
extrahuje rychlost z datové části a čtverců tím, že ji zvýší na výkon 2.<expression> | round
zaokrouhlí výsledek na nejbližší celé číslo, aby se zabránilo zavádějící vysoké přesnosti ve výsledku.
Následující JSON ukazuje výstup z předchozího výrazu jq:
{
"userProperties": [
{ "key": "mass", "value": 512.1 },
{ "key": "productType", "value": "projectile" }
],
"payload": {
"velocity": 97.2,
"energy": 2419119
}
}
Logická logika
Kanály zpracování dat často používají jq k filtrování zpráv. Filtrování obvykle používá logické výrazy a operátory. Kromě toho je logická logika užitečná k provádění toků řízení v transformacích a pokročilejších případech použití filtrování.
Následující příklady ukazují některé z nejběžnějších funkcí používaných v logických výrazech v jq:
Základní logické a podmíněné operátory
jq poskytuje základní logické logické operátory and
, or
a not
. Operátory and
a or
operátory jsou operátory infix. not
je funkce, kterou vyvoláte jako filtr, <expression> | not
například .
jq má podmíněné operátory >
, , ==
<
, !=
, , >=
, a <=
. Tyto operátory jsou infixní operátory.
Následující příklad ukazuje, jak provést základní logickou logiku pomocí podmíněných podmínek. Na základě následujícího vstupu:
{
"payload": {
"temperature": 50,
"humidity": 92,
"site": "Redmond"
}
}
Pomocí následujícího výrazu jq zkontrolujte, jestli:
- Teplota je v horní hranici mezi 30 a 60 stupňů včetně.
- Vlhkost vzduchu je menší než 80 a lokalita je Redmond.
.payload
| ((.temperature > 30 and .temperature <= 60) or .humidity < 80) and .site == "Redmond"
| not
V předchozím výrazu jq:
.payload | <expression>
rozsahy<expression>
na obsah ..payload
Díky této syntaxi je zbytek výrazu méně podrobný.((.temperature > 30 and .temperature <= 60) or .humidity < 80) and .site == "Redmond"
vrátí hodnotu true, pokud je teplota mezi 30 a 60 stupňů (včetně na horní hranici) nebo vlhkost je menší než 80, vrátí hodnotu true pouze v případě, že lokalita je také Redmond.<expression> | not
vezme výsledek předchozího výrazu a použije na něj logickou HODNOTU NOT, v tomto příkladu vrátí výsledek ztrue
nafalse
.
Následující JSON ukazuje výstup z předchozího výrazu jq:
false
Kontrola existence klíče objektu
Můžete vytvořit filtr, který místo jeho obsahu kontroluje strukturu zprávy. Můžete například zkontrolovat, jestli v objektu existuje konkrétní klíč. Chcete-li tuto kontrolu provést, použijte has
funkci nebo kontrolu proti hodnotě null. Následující příklad ukazuje oba tyto přístupy. Na základě následujícího vstupu:
{
"payload": {
"temperature": 51,
"humidity": 41,
"site": null
}
}
Pomocí následujícího výrazu jq zkontrolujte, jestli datová část obsahuje temperature
pole, pokud site
pole nemá hodnotu null a další kontroly:
.payload | {
hasTemperature: has("temperature"),
temperatureNotNull: (.temperature != null),
hasSite: has("site"),
siteNotNull: (.site != null),
hasMissing: has("missing"),
missingNotNull: (.missing != null),
hasNested: (has("nested") and (.nested | has("inner"))),
nestedNotNull: (.nested?.inner != null)
}
V předchozím výrazu jq:
.payload | <expression>
definuje kontext<expression>
dat na hodnotu.payload
, aby bylo méně<expression>
podrobné.hasTemperature: has("temperature"),
tento a další podobné výrazy ukazují, jak sehas
funkce chová se vstupním objektem. Funkce vrátí hodnotu true pouze v případě, že je klíč k dispozici.hasSite
je true navzdory hodnotěsite
býtnull
.temperatureNotNull: (.temperature != null),
tento a další podobné výrazy ukazují, jak!= null
kontrola provádí podobnou kontrolu .has
Neexistující klíč v objektu jenull
v případě přístupu pomocí.<key>
syntaxe nebo klíče existuje, ale má hodnotunull
. ObasiteNotNull
amissingNotNull
jsou false, i když jeden klíč je přítomný a druhý chybí.hasNested: (has("nested") and (.nested | has("inner")))
provede kontrolu vnořeného objektu s objektemhas
, kde nadřazený objekt nemusí existovat. Výsledkem je kaskádová kontrola na každé úrovni, aby se zabránilo chybě.nestedNotNull: (.nested?.inner != null)
provádí stejnou kontrolu u vnořeného objektu pomocí!= null
a?
povolení řetězení cest u polí, která nemusí existovat. Tento přístup vytváří čistější syntaxi pro hluboko vnořené řetězy, které můžou nebo nemusí existovat, ale nedokáže odlišitnull
klíčové hodnoty od těch, které neexistují.
Následující JSON ukazuje výstup z předchozího výrazu jq:
{
"hasTemperature": true,
"temperatureNotNull": true,
"hasSite": true,
"siteNotNull": false,
"hasMissing": false,
"missingNotNull": false,
"hasNested": false,
"nestedNotNull": false
}
Kontrola existence položky pole
any
Pomocí funkce zkontrolujte existenci položky v poli. Na základě následujícího vstupu:
{
"userProperties": [
{ "key": "mass", "value": 512.1 },
{ "key": "productType", "value": "projectile" }
],
"payload": {
"velocity": 97.2,
"energy": 2419119
}
}
Pomocí následujícího výrazu jq zkontrolujte, jestli userProperties
pole obsahuje položku s klíčem mass
a bez položky s klíčem missing
:
.userProperties | any(.key == "mass") and (any(.key == "missing") | not)
V předchozím výrazu jq:
.userProperties | <expression>
definuje kontext<expression>
dat na hodnotuuserProperties
, aby zbytek<expression>
byl méně podrobný.any(.key == "mass")
.key == "mass"
spustí výraz proti každémuuserProperties
prvku pole a vrátí hodnotu true, pokud se výraz vyhodnotí jako true pro alespoň jeden prvek pole.(any(.key == "missing") | not)
.key == "missing"
provede proti každémuuserProperties
prvku pole, který vrátí hodnotu true, pokud některý prvek vyhodnotí hodnotu true, pak neguje celkový výsledek s| not
.
Následující JSON ukazuje výstup z předchozího výrazu jq:
true
Tok řízení
Tok řízení v jq se liší od většiny jazyků, protože většina forem toku řízení je přímo řízená daty. Stále existuje podpora výrazů if/else s tradiční funkční sémantika programování, ale většinu struktur smyček můžete dosáhnout pomocí kombinací funkcí map
a reduce
funkcí.
Následující příklady ukazují některé běžné scénáře toku řízení v jq.
Příkazy If-else
jq podporuje podmínky pomocí .if <test-expression> then <true-expression> else <false-expression> end
Další případy můžete vložit přidáním elif <test-expression> then <true-expression>
doprostřed. Klíčovým rozdílem mezi jazykem jq a mnoha dalšími jazyky je to, že každý then
výraz else
vytvoří výsledek použitý v následných operacích v celkovém výrazu jq.
Následující příklad ukazuje použití if
příkazů k vytvoření podmíněných informací. Na základě následujícího vstupu:
{
"payload": {
"temperature": 25,
"humidity": 52
}
}
Pomocí následujícího výrazu jq zkontrolujte, jestli je teplota vysoká, nízká nebo normální:
.payload.status = if .payload.temperature > 80 then
"high"
elif .payload.temperature < 30 then
"low"
else
"normal"
end
V předchozím výrazu jq:
.payload.status = <expression>
přiřadí výsledek spuštění<expression>
do novéhostatus
pole v datové části.if ... end
je základníif/elif/else
výraz:if .payload.temperature > 80 then "high"
zkontroluje teplotu proti vysoké hodnotě, pokud"high"
je true, jinak bude pokračovat.elif .payload.temperature < 30 then "low"
provede druhou kontrolu teploty nízké hodnoty a nastaví výsledek na"low"
hodnotu true, jinak bude pokračovat.else "normal" end
vrátí"normal"
, pokud žádná z předchozích kontrol nebyla pravdivá a zavře výraz send
.
Následující JSON ukazuje výstup z předchozího výrazu jq:
{
"payload": {
"temperature": 25,
"humidity": 52,
"status": "low"
}
}
Mapovat
V funkčních jazycích, jako je jq, je nejběžnějším způsobem, jak provést iterativní logiku, vytvořit pole a pak namapovat hodnoty tohoto pole na nové. Tato technika se dosahuje v jazyce jq pomocí map
funkce, která se zobrazuje v mnoha příkladech v této příručce. Pokud chcete provést nějakou operaci s více hodnotami, map
pravděpodobně je odpovědí.
Následující příklad ukazuje, jak odebrat map
předponu z klíčů objektu. Toto řešení lze stručněji napsat pomocí with_entries
, ale podrobnější verze zobrazená zde ukazuje skutečné mapování probíhající pod kapotou v krátkém přístupu. Na základě následujícího vstupu:
{
"payload": {
"rotor_rpm": 150,
"rotor_temperature": 51,
"rotor_cycles": 1354
}
}
Pomocí následujícího výrazu jq odeberte předponu rotor_
z klíčů datové části:
.payload |= (to_entries | map(.key |= ltrimstr("rotor_")) | from_entries)
V předchozím výrazu jq:
.payload |= <expression>
používá|=
k aktualizaci hodnoty.payload
s výsledkem spuštění<expression>
. Použití|=
namísto=
nastavení kontextu<expression>
.payload
dat místo.
.(to_entries | map(<expression) | from_entries)
provádí převod pole objektů a mapuje každou položku na novou hodnotu pomocí<expression>
. Tento přístup je sémanticky ekvivalentníwith_entries(<expression>)
:to_entries
převede objekt na pole, přičemž každý pár klíč/hodnota se stane samostatným objektem se strukturou{"key": <key>, "value": <value>}
.map(<expression>)
<expression>
provede pro každý prvek v poli a vytvoří výstupní pole s výsledky každého výrazu.from_entries
je inverzní funkceto_entries
. Funkce převede pole objektů se strukturou{"key": <key>, "value": <value>}
na objekt s poli akey
value
poli namapovanými na páry klíč/hodnota.
.key |= ltrimstr("rotor_")
aktualizuje hodnotu.key
v každé položce s výsledkemltrimstr("rotor_")
. Syntaxe|=
definuje kontext dat na pravé straně na hodnotu.key
.ltrimstr
odebere danou předponu z řetězce, pokud je k dispozici.
Následující JSON ukazuje výstup z předchozího výrazu jq:
{
"payload": {
"rpm": 150,
"temperature": 51,
"cycles": 1354
}
}
Snížení
Redukce je primárním způsobem provádění operací smyčky nebo iterativních operací napříč prvky pole. Operace redukce se skládá z akumulátoru a operace, která používá akumulátor a aktuální prvek pole jako vstupy. Každá iterace smyčky vrátí další hodnotu akumulátoru a konečný výstup operace redukce je poslední hodnota akumulátoru. Redukce se označuje jako přeložení v některých dalších funkčních programovacích jazycích.
reduce
Použijte operaci v jazyce jq ke snížení. Většina případů použití nepotřebuje tuto manipulaci s nízkou úrovní a může místo toho používat funkce vyšší úrovně, ale reduce
je užitečným obecným nástrojem.
Následující příklad ukazuje, jak vypočítat průměrnou změnu hodnoty metriky nad datovými body, které máte. Na základě následujícího vstupu:
{
"payload": [
{
"value": 65,
"timestamp": 1689796743559
},
{
"value": 55,
"timestamp": 1689796771131
},
{
"value": 59,
"timestamp": 1689796827766
},
{
"value": 62,
"timestamp": 1689796844883
},
{
"value": 58,
"timestamp": 1689796864853
}
]
}
K výpočtu průměrné změny hodnoty v datových bodech použijte následující výraz jq:
.payload |= (
reduce .[] as $item (
null;
if . == null then
{totalChange: 0, previous: $item.value, count: 0}
else
.totalChange += (($item.value - .previous) | length)
| .previous = $item.value
| .count += 1
end
)
| .totalChange / .count
)
V předchozím výrazu jq:
.payload |= <expression>
používá|=
k aktualizaci hodnoty.payload
s výsledkem spuštění<expression>
. Použití|=
namísto=
nastavení kontextu<expression>
.payload
dat místo.
.reduce .[] as $item (<init>; <expression>)
je generování typické operace redukce s následujícími částmi:.[] as $item
musí být<expression> as <variable>
vždy a je nejčastěji.[] as $item
. Vytvoří<expression>
datový proud hodnot, z nichž každý se uloží do<variable>
iterace operace redukce. Pokud máte pole, které chcete iterovat,.[]
rozdělí ho na datový proud. Tato syntaxe je stejná jako syntaxe použitá k rozdělení zpráv, alereduce
operace nepoužívá datový proud k vygenerování více výstupů.reduce
nerozdělí vaši zprávu.<init>
v tomto případěnull
je počáteční hodnota akumulátoru použitého v operaci redukce. Tato hodnota je často nastavená na prázdnou nebo nulu. Tato hodnota se stane kontextem dat v.
této smyčce<expression>
pro první iteraci.<expression>
je operace prováděná pro každou iteraci operace redukce. V tomto případě$item
má přístup k aktuální hodnotě akumulátoru prostřednictvím.
a aktuální hodnoty v toku prostřednictvím<variable>
dříve deklarované hodnoty .
if . == null then {totalChange: 0, previous: $item.value, count: 0}
je podmíněná pro zpracování první iterace redukce. Nastaví strukturu akumulátoru pro další iteraci. Vzhledem k tomu, že výraz vypočítá rozdíly mezi položkami, nastaví první položka data použitá k výpočtu rozdílu při druhé iteraci redukce. PoletotalChange
acount
poleprevious
slouží jako proměnné smyčky a aktualizují se při každé iteraci..totalChange += (($item.value - .previous) | length) | .previous = $item.value | .count += 1
je výraz v tomtoelse
případě. Tento výraz nastaví každé pole v objektu akumulátoru na novou hodnotu na základě výpočtu. PrototalChange
, najde rozdíl mezi aktuální a předchozí hodnoty a získá absolutní hodnotu. Neintuitivně používálength
funkci k získání absolutní hodnoty.previous
je nastavena na aktuální$item
hodnotuvalue
pro další iteraci, která se má použít, acount
je přírůstková..totalChange / .count
vypočítá průměrnou změnu v datových bodech po dokončení operace redukce a vy máte konečnou hodnotu akumulátoru.
Následující JSON ukazuje výstup z předchozího výrazu jq:
{
"payload": 5.25
}
Smyčky
Smyčky v jq jsou obvykle vyhrazeny pro pokročilé případy použití. Vzhledem k tomu, že každá operace v jazyce jq je výraz, který vytváří hodnotu, nejsou sémantika sémantiky řízených příkazy ve většině jazyků přirozeným fitem v jazyce jq. Zvažte použití map
nebo reduce
řešení vašich potřeb.
V jq existují dva primární typy tradiční smyčky. Existují i jiné typy smyček, ale jsou určené pro specializovanější případy použití:
while
použije operaci opakovaně vůči kontextu vstupních dat, aktualizuje hodnotu kontextu dat pro použití v další iteraci a vytvoří tuto hodnotu jako výstup. Výstupemwhile
smyčky je pole, které obsahuje hodnoty vytvořené každou iterací smyčky.until
likewhile
použije operaci opakovaně vůči kontextu vstupních dat, aktualizuje hodnotu kontextu dat pro použití v další iteraci. Na rozdíl odwhile
toho smyčkauntil
vypíše hodnotu vytvořenou poslední iterací smyčky.
Následující příklad ukazuje, jak pomocí until
smyčky postupně eliminovat odlehlé datové body ze seznamu čtení, dokud směrodatná odchylka klesne pod předdefinovanou hodnotu. Na základě následujícího vstupu:
{
"payload": [
{
"value": 65,
"timestamp": 1689796743559
},
{
"value": 55,
"timestamp": 1689796771131
},
{
"value": 59,
"timestamp": 1689796827766
},
{
"value": 62,
"timestamp": 1689796844883
},
{
"value": 58,
"timestamp": 1689796864853
}
]
}
Pomocí následujícího výrazu jq postupně eliminujte odlehlé datové body ze seznamu čtení, dokud směrodatná odchylka klesne pod 2:
def avg: add / length;
def stdev: avg as $mean | (map(. - $mean | . * .) | add) / (length - 1) | sqrt;
.payload |= (
sort_by(.value)
| until(
(map(.value) | stdev) < 2 or length == 0;
(map(.value) | avg) as $avg
| if ((.[0].value - $avg) | length) > ((.[-1].value - $avg) | length) then
del(.[0])
else
del(.[-1])
end
)
)
V předchozím výrazu jq:
def avg: add / length;
definuje novou funkci, která se používáavg
k výpočtu průměrů později ve výrazu. Výraz na pravé straně:
je logický výraz použitý při každém použitíavg
. Výraz<expression> | avg
je ekvivalentní<expression> | add / length
def stdev: avg as $mean | (map(. - $mean | . * .) | add) / (length - 1) | sqrt;
definuje novou funkci s názvemstdev
. Funkce vypočítá směrodatnou odchylku vzorku pole pomocí upravené verze odpovědi komunity na StackOverflow..payload |= <expression>
první dvadef
jsou pouze deklarace a zahajují skutečný výraz. Výraz se<expression>
spustí se vstupním datovým objektem.payload
a přiřadí výsledek zpět ..payload
sort_by(.value)
seřadí pole položek pole podle jejichvalue
pole. Toto řešení vyžaduje, abyste identifikovali nejvyšší a nejnižší hodnoty v poli a manipuloval s nimi, takže řazení dat předem snižuje výpočet a zjednodušuje kód.until(<condition>; <expression>)
<expression>
provede proti vstupu, dokud<condition>
nevrátí hodnotu true. Vstup pro každé spuštění<expression>
a<condition>
je výstupem předchozího spuštění .<expression>
Výsledek posledního spuštění<expression>
je vrácen ze smyčky.(map(.value) | stdev) < 2 or length == 0
je podmínka smyčky:map(.value)
převede matici na seznam čistých čísel pro použití v následných výpočtech.(<expression> | stdev) < 2
vypočítá směrodatnou odchylku matice a vrátí hodnotu true, pokud je směrodatná odchylka menší než 2.length == 0
získá délku vstupního pole a vrátí hodnotu true, pokud je 0. Chcete-li chránit před případem, kdy jsou všechny položky vyloučeny, jeor
výsledek -ed s celkovým výrazem.
(map(.value) | avg) as $avg
převede matici na matici čísel a vypočítá jejich průměr a výsledek pak uloží do$avg
proměnné. Tento přístup šetří náklady na výpočty, protože průměr opakovaně používáte v iteraci smyčky. Výrazy přiřazení proměnných nemění kontext dat pro další výraz za|
, takže zbytek výpočtu má stále přístup k úplnému poli.if <condition> then <expression> else <expression> end
je základní logika iterace smyčky. Používá<condition>
k určení<expression>
spuštění a vrácení.((.[0].value - $avg) | length) > ((.[-1].value - $avg) | length)
if
je podmínka, která porovnává nejvyšší a nejnižší hodnoty s průměrnou hodnotou a pak porovnává tyto rozdíly:(.[0].value - $avg) | length
value
načte pole první položky pole a získá rozdíl mezi ní a celkovým průměrem. První položka pole je nejnižší z důvodu předchozího řazení. Tato hodnota může být záporná, takže výsledek je předanýlength
, který vrátí absolutní hodnotu při zadání čísla jako vstupu.(.[-1].value - $avg) | length
provede stejnou operaci s poslední položkou pole a vypočítá absolutní hodnotu i pro bezpečnost. Poslední položka pole je nejvyšší z důvodu předchozího řazení. Absolutní hodnoty se pak porovnávají v celkové podmínce pomocí .>
del(.[0])
then
je výraz, který se spustí, když byla první položka pole největší odlehlé hodnoty. Výraz odebere prvek z.[0]
pole. Výraz vrátí data uložená v poli po operaci.del(.[-1])
else
je výraz, který se spustí, když poslední položka pole byla největší odlehlé hodnoty. Výraz odebere prvek na.[-1]
, což je poslední položka, z pole. Výraz vrátí data uložená v poli po operaci.
Následující JSON ukazuje výstup z předchozího výrazu jq:
{
"payload": [
{
"value": 58,
"timestamp": 1689796864853
},
{
"value": 59,
"timestamp": 1689796827766
},
{
"value": 60,
"timestamp": 1689796844883
}
]
}
Zahoďte zprávy
Když napíšete výraz filtru, můžete systému dát pokyn, aby vyhodil všechny zprávy, které nechcete, vrácením nepravda. Toto chování je základním chováním podmíněných výrazů v jazyce jq. Někdy ale dochází k tomu, že transformujete zprávy nebo provádíte pokročilejší filtry, pokud chcete, aby systém explicitně nebo implicitně zahodil zprávy za vás. Následující příklady ukazují, jak toto chování implementovat.
Explicitní přetažení
Pokud chcete explicitně vypustit zprávu ve výrazu filtru, vraťte false
se z výrazu.
Zprávu můžete také přetáhnout z transformace pomocí předdefinované empty
funkce v jq.
Následující příklad ukazuje, jak vypočítat průměr datových bodů ve zprávě a odstranit všechny zprávy s průměrem pod pevnou hodnotou. Je možné i platné toto chování dosáhnout kombinací fáze transformace a fáze filtru. Použijte přístup, který nejlépe vyhovuje vaší situaci. S ohledem na následující vstupy:
Zpráva 1
{
"payload": {
"temperature": [23, 42, 63, 61],
"humidity": [64, 36, 78, 33]
}
}
Zpráva 2
{
"payload": {
"temperature": [42, 12, 32, 21],
"humidity": [92, 63, 57, 88]
}
}
Pomocí následujícího výrazu jq vypočítáte průměr datových bodů a zahoďte všechny zprávy s průměrnou teplotou menší než 30 nebo průměrnou vlhkostí větší než 90:
.payload |= map_values(add / length)
| if .payload.temperature > 30 and .payload.humidity < 90 then . else empty end
V předchozím výrazu jq:
.payload |= <expression>
používá|=
k aktualizaci hodnoty.payload
s výsledkem spuštění<expression>
. Použití|=
namísto=
nastavení kontextu<expression>
.payload
dat místo.
.map_values(add / length)
add / length
provede pro každou hodnotu v podobjektu.payload
. Výraz sečte prvky v matici hodnot a potom je vydělí délkou pole, aby se vypočítal průměr.if .payload.temperature > 30 and .payload.humidity < 90 then . else empty end
kontroluje dvě podmínky pro výslednou zprávu. Pokud se filtr vyhodnotí jako true, jako v prvním vstupu, vytvoří se celá zpráva jako výstup. Pokud se filtr vyhodnotí jako nepravda, jako ve druhém vstupu, vrátíempty
, což vede k prázdnému datovému proudu s nulovými hodnotami. Tento výsledek způsobí, že výraz zahodí odpovídající zprávu.
Výstup 1
{
"payload": {
"temperature": 47.25,
"humidity": 52.75
}
}
Výstup 2
(žádný výstup)
Implicitní vyřazení pomocí chyb
Výrazy filtru i transformace můžou implicitně vyřadit zprávy tím, že jq způsobí chybu. I když tento přístup není osvědčeným postupem, protože kanál nedokáže rozlišovat mezi chybou, kterou jste úmyslně způsobili, a to způsobené neočekávaným vstupem do výrazu. Systém v současné době zpracovává chybu za běhu ve filtru nebo transformaci přetažením zprávy a záznamem chyby.
Běžným scénářem, který tento přístup používá, je situace, kdy vstup do kanálu může obsahovat zprávy, které jsou strukturálně oddělené. Následující příklad ukazuje, jak přijímat dva typy zpráv, z nichž jeden se úspěšně vyhodnotí vůči filtru a druhý, který je strukturálně nekompatibilní s výrazem. S ohledem na následující vstupy:
Zpráva 1
{
"payload": {
"sensorData": {
"temperature": 15,
"humidity": 62
}
}
}
Zpráva 2
{
"payload": [
{
"rpm": 12,
"timestamp": 1689816609514
},
{
"rpm": 52,
"timestamp": 1689816628580
}
]
}
Pomocí následujícího výrazu jq vyfiltrujte zprávy s teplotou menší než 10 a vlhkostí větší než 80:
.payload.sensorData.temperature > 10 and .payload.sensorData.humidity < 80
V předchozím příkladu je samotný výraz jednoduchým složeným logickým výrazem. Výraz je navržený tak, aby fungoval se strukturou první ze vstupních zpráv zobrazených dříve. Když výraz obdrží druhou zprávu, je maticová struktura .payload
nekompatibilní s přístupem k objektu ve výrazu a výsledkem je chyba. Pokud chcete filtrovat na základě hodnot teploty a vlhkosti a odebrat zprávy s nekompatibilní strukturou, tento výraz funguje. Dalším přístupem, který způsobí, že nedojde k chybě, je přidat (.payload | type) == "object" and
na začátek výrazu.
Výstup 1
true
Výstup 2
(chyba)
Nástroje pro čas
jq nepodporuje čas jako nativní typ. Některé formáty přijaté a generované zpracovatelem dat však podporují čas jako nativní typ. Tyto typy jsou obvykle reprezentovány pomocí typu Jazyka time.Time
Go.
Procesor dat poskytuje modul se sadou funkcí, které vám umožní pracovat s těmito hodnotami z jq:
- Převod mezi nativním časem, řetězci ISO 8601 a číselnými časovými razítky unixu
- Pro všechny tyto typy proveďte různé časově specifické operace.
Modul time
Všechny speciální funkce specifické pro čas jsou zadané v time
modulu, který můžete importovat do dotazu.
Modul na začátku dotazu naimportujte jedním ze dvou způsobů:
import" "time" as time;
include "time"
První metoda umístí všechny funkce v modulu pod obor názvů, například time::totime
. Druhá metoda jednoduše umístí všechny funkce času na nejvyšší úrovni, například totime
. Obě syntaxe jsou platné a funkčně ekvivalentní.
Formáty a převod
Časový modul funguje se třemi časovými formáty:
time
je nativní časová hodnota. Můžete ho použít jenom s funkcemi v modulu času. Rozpoznán jakotime
datový typ při serializaci.unix
je číselné časové razítko unixu, které představuje čas jako sekundy od epochy Unixu. Může to být celé číslo nebo číslo s plovoucí desetinou čárkou. Rozpoznán jako odpovídající číselný typ při serializaci.iso
je formát řetězce ISO 8601 reprezentace času. Rozpoznán jako řetězec při serializaci.
Časový modul poskytuje následující funkce pro kontrolu a manipulaci s těmito typy:
time::totime
převede některý ze tří typů natime
.time::tounix
převede některý ze tří typů naunix
.time::toiso
převede některý ze tří typů naiso
.time::istime
vrátí hodnotu true, pokud jsou data vetime
formátu.
Časové operace
Časový modul poskytuje různé operace specifické pro čas, které pracují se všemi typy. Následující funkce můžou jako vstup přijmout libovolný z podporovaných typů a vrátit stejný typ jako jejich výstup. Celá čísla časového razítka mohou být převedena na časová razítka s plovoucí desetinnou čárkou, pokud je potřeba větší přesnost.
time::utc
převede čas na UTC.time::zone(zone)
převede čas na zadanou zónu.zone
je řetězec zóny ISO 8601. Napříkladtime::zone("-07")
.time::local
převede čas na místní čas.time::offset(duration)
posune čas zadanou dobu trvání.duration
používá syntaxi řetězce doby trvání jazyka Go. Napříkladtime::offset("1m2s")
.time::offset(value;unit)
posune čas zadanou dobu trvání. Tato funkce používá číslo a řetězec jednotky. Napříkladtime::offset(2;"s")
. Tato funkce je užitečná, když doba trvání pochází z jiné vlastnosti.
Poznámka:
Tři funkce časového pásma nemají žádný smysluplný vliv na časová razítka unixu.
Různé nástroje
Modul util
je kolekce nástrojů, které rozšiřují možnosti modulu runtime jq.
Modul util
Všechny různé nástroje jsou zadané v util
modulu, který můžete importovat do dotazu.
Modul na začátku dotazu naimportujte jedním ze dvou způsobů:
import" "util" as util;
include "util"
První metoda umístí všechny funkce v modulu pod obor názvů, například util::uuid
. Druhá metoda jednoduše umístí všechny různé funkce na nejvyšší úrovni, například uuid
. Obě syntaxe jsou platné a funkčně ekvivalentní.
Modul util
aktuálně obsahuje uuid
funkci, která vrací nové náhodné UUID ve standardním řetězcovém formátu.
Binární manipulace
jq je navržený tak, aby fungoval s daty, která se dají reprezentovat jako JSON. Kanály Azure IoT Data Processor Preview ale podporují také nezpracovaný formát dat, který obsahuje neparsovaná binární data. Pro práci s binárními daty obsahuje verze jq, která se dodává se zpracovatelem dat, balíček navržený tak, aby vám pomohl zpracovat binární data. Umožňuje:
- Převeďte dopředu a zpět mezi binárními a jinými formáty, jako jsou pole base64 a celočíselná pole.
- Pomocí předdefinovaných funkcí můžete číst číselné a řetězcové hodnoty z binární zprávy.
- Provádění úprav binárních dat v bodech a zachování jeho formátu.
Důležité
Nemůžete použít žádné předdefinované funkce ani operátory jq, které upravují binární hodnotu. To znamená, že bez zřetězení bez +
map
provozu s bajty a žádná smíšená přiřazení s binárními hodnotami, jako |=
je , +=
. //=
Můžete použít standardní přiřazení (==
). Pokud se pokusíte použít binární data s nepodporovanou operací, systém vyvolá jqImproperBinaryUsage
chybu. Pokud potřebujete pracovat s binárními daty vlastními způsoby, zvažte použití jedné z následujících funkcí k převodu na pole base64 nebo celočíselného pole pro výpočet a následné převodu zpět na binární.
Následující části popisují binární podporu v modulu jq Zpracovatel dat.
Modul binary
Veškerá binární podpora v modulu jq Zpracovatel dat je určena v binary
modulu, který můžete importovat.
Modul na začátku dotazu naimportujte jedním ze dvou způsobů:
import "binary" as binary;
include "binary"
První metoda umístí všechny funkce v modulu pod obor názvů, například binary::tobase64
. Druhá metoda jednoduše umístí všechny binární funkce na nejvyšší úrovni, například tobase64
. Obě syntaxe jsou platné a funkčně ekvivalentní.
Formáty a převod
Binární modul funguje se třemi typy:
- binary – binární hodnota, pouze přímo použitelná s funkcemi v binárním modulu. Kanál rozpozná jako binární datový typ při serializaci. Tento typ použijte pro nezpracované serializace.
- array – formát, který převede binární soubor na matici čísel, abyste mohli provádět vlastní zpracování. Kanál rozpozná jako pole celých čísel při serializaci.
- base64 – řetězcová reprezentace formátu binárního souboru. Většinou užitečné, pokud chcete převést mezi binárními a řetězci. Kanál rozpozná jako řetězec při serializaci.
V závislosti na vašich potřebách můžete převést mezi všemi třemi typy dotazů jq. Můžete například převést z binárního souboru na pole, provést určitou vlastní manipulaci a pak převést zpět na binární na konci, aby se zachovaly informace o typu.
Functions
Pro kontrolu a manipulaci mezi těmito typy jsou k dispozici následující funkce:
binary::tobinary
převede některý ze tří typů na binární.binary::toarray
převede některý ze tří typů na pole.binary::tobase64
převede některý ze tří typů na base64.binary::isbinary
vrátí hodnotu true, pokud jsou data v binárním formátu.binary::isarray
vrátí hodnotu true, pokud jsou data ve formátu pole.binary::isbase64
vrátí hodnotu true, pokud jsou data ve formátu base64.
Modul také poskytuje binary::edit(f)
funkci pro rychlé úpravy binárních dat. Funkce převede vstup do formátu pole, použije na ni funkci a pak převede výsledek zpět na binární.
Extrakce dat z binárního souboru
Binární modul umožňuje extrahovat hodnoty z binárních dat, které se použijí při rozbalení vlastních binárních datových částí. Obecně platí, že tato funkce se řídí dalšími binárními rozbalovacími knihovnami a řídí se podobným pojmenováváním. Můžete rozbalit následující typy:
- Celá čísla (int8, int16, int32, int64, uint8, uint16, uint32, uint64)
- Plováky (float, double)
- Řetězce (utf8)
Modul také umožňuje určit posuny a endianitu, pokud je to možné.
Funkce pro čtení binárních dat
Binární modul poskytuje následující funkce pro extrakci dat z binárních hodnot. Můžete použít všechny funkce s libovolným ze tří typů, mezi kterými může balíček převést.
Všechny parametry funkce jsou volitelné, offset
výchozí hodnoty 0
a length
výchozí hodnoty pro zbytek dat.
binary::read_int8(offset)
přečte hodnotu int8 z binární hodnoty.binary::read_int16_be(offset)
přečte hodnotu int16 z binární hodnoty v big-endian pořadí.binary::read_int16_le(offset)
čte hodnotu int16 z binární hodnoty v malém endianském pořadí.binary::read_int32_be(offset)
přečte hodnotu int32 z binární hodnoty v pořadí big-endian.binary::read_int32_le(offset)
čte hodnotu int32 z binární hodnoty v malém endiánském pořadí.binary::read_int64_be(offset)
přečte hodnotu int64 z binární hodnoty v big-endian pořadí.binary::read_int64_le(offset)
čte hodnotu int64 z binární hodnoty v malém endiánském pořadí.binary::read_uint8(offset)
čte uint8 z binární hodnoty.binary::read_uint16_be(offset)
čte uint16 z binární hodnoty v pořadí big-endian.binary::read_uint16_le(offset)
čte uint16 z binární hodnoty v malém endiánském pořadí.binary::read_uint32_be(offset)
čte uint32 z binární hodnoty v big-endian pořadí.binary::read_uint32_le(offset)
čte uint32 z binární hodnoty v malém endiánském pořadí.binary::read_uint64_be(offset)
čte uint64 z binární hodnoty v big-endian pořadí.binary::read_uint64_le(offset)
čte uint64 z binární hodnoty v malém endian pořadí.binary::read_float_be(offset)
čte hodnotu float z binární hodnoty v pořadí big-endian.binary::read_float_le(offset)
čte hodnotu float z binární hodnoty v malém endian pořadí.binary::read_double_be(offset)
čte double z binární hodnoty v big-endian pořadí.binary::read_double_le(offset)
čte dvojitou hodnotu z binární hodnoty v malém endian pořadí.binary::read_bool(offset; bit)
přečte logickou hodnotu z binární hodnoty a zkontroluje danou bitovou hodnotu.binary::read_bit(offset; bit)
čte bit z binární hodnoty pomocí daného bitového indexu.binary::read_utf8(offset; length)
přečte řetězec UTF-8 z binární hodnoty.
Zápis binárních dat
Binární modul umožňuje kódovat a zapisovat binární hodnoty. Tato funkce umožňuje vytvářet nebo upravovat binární datové části přímo v jq. Zápis dat podporuje stejnou sadu datových typů jako extrakce dat a umožňuje také určit koncovost, která se má použít.
Zápis dat má dvě formy:
write_*
funkce aktualizují data na místě v binární hodnotě, která slouží k aktualizaci nebo manipulaci s existujícími hodnotami.append_*
funkce přidávají data na konec binární hodnoty, která slouží k přidání nebo vytvoření nových binárních hodnot.
Funkce pro zápis binárních dat
Binární modul poskytuje následující funkce pro zápis dat do binárních hodnot. Všechny funkce je možné spouštět s libovolným ze tří platných typů, mezi které může tento balíček převést.
Parametr value
je povinný pro všechny funkce, ale offset
je volitelný, pokud je platný a výchozí hodnota 0
.
Funkce zápisu:
binary::write_int8(value; offset)
zapíše hodnotu int8 do binární hodnoty.binary::write_int16_be(value; offset)
zapíše hodnotu int16 do binární hodnoty v pořadí big-endian.binary::write_int16_le(value; offset)
zapíše hodnotu int16 do binární hodnoty v malém endian pořadí.binary::write_int32_be(value; offset)
zapíše hodnotu int32 do binární hodnoty v pořadí big-endian.binary::write_int32_le(value; offset)
zapíše hodnotu int32 do binární hodnoty v malém endovém pořadí.binary::write_int64_be(value; offset)
zapíše hodnotu int64 do binární hodnoty v pořadí big-endian.binary::write_int64_le(value; offset)
zapíše hodnotu int64 do binární hodnoty v malém endian pořadí.binary::write_uint8(value; offset)
zapíše uint8 do binární hodnoty.binary::write_uint16_be(value; offset)
zapíše uint16 do binární hodnoty v big-endian pořadí.binary::write_uint16_le(value; offset)
zapíše uint16 do binární hodnoty v malém endian pořadí.binary::write_uint32_be(value; offset)
zapíše uint32 do binární hodnoty v big-endian pořadí.binary::write_uint32_le(value; offset)
zapíše uint32 do binární hodnoty v malém endian pořadí.binary::write_uint64_be(value; offset)
zapíše uint64 na binární hodnotu v big-endian pořadí.binary::write_uint64_le(value; offset)
zapíše uint64 do binární hodnoty v malém endian pořadí.binary::write_float_be(value; offset)
zapíše hodnotu float do binární hodnoty v pořadí big-endian.binary::write_float_le(value; offset)
zapíše hodnotu float do binární hodnoty v malém endian pořadí.binary::write_double_be(value; offset)
zapíše dvojitou hodnotu do binární hodnoty v pořadí big-endian.binary::write_double_le(value; offset)
zapíše dvojitou hodnotu do binární hodnoty v malém endian pořadí.binary::write_bool(value; offset; bit)
zapíše logickou hodnotu do jednoho bajtu v binární hodnotě a nastaví daný bit na logickou hodnotu.binary::write_bit(value; offset; bit)
zapíše jeden bit do binární hodnoty a ostatní bity v bajtu ponechá tak, jak je.binary::write_utf8(value; offset)
zapíše řetězec UTF-8 do binární hodnoty.
Funkce připojení:
binary::append_int8(value)
připojí int8 k binární hodnotě.binary::append_int16_be(value)
připojí int16 k binární hodnotě v big-endian pořadí.binary::append_int16_le(value)
připojí int16 k binární hodnotě v malém endian pořadí.binary::append_int32_be(value)
připojí hodnotu int32 k binární hodnotě v pořadí big-endian.binary::append_int32_le(value)
připojí hodnotu int32 k binární hodnotě v malém endovém pořadí.binary::append_int64_be(value)
připojí int64 k binární hodnotě v big-endian pořadí.binary::append_int64_le(value)
připojí int64 k binární hodnotě v malém endian pořadí.binary::append_uint8(value)
připojí uint8 k binární hodnotě.binary::append_uint16_be(value)
připojí uint16 k binární hodnotě v pořadí big-endian.binary::append_uint16_le(value)
připojí uint16 k binární hodnotě v malém endian pořadí.binary::append_uint32_be(value)
připojí uint32 k binární hodnotě v pořadí big-endian.binary::append_uint32_le(value)
připojí uint32 k binární hodnotě v malém endian pořadí.binary::append_uint64_be(value)
připojí uint64 k binární hodnotě v pořadí big-endian.binary::append_uint64_le(value)
připojí uint64 k binární hodnotě v malém endian pořadí.binary::append_float_be(value)
připojí hodnotu float k binární hodnotě v pořadí big-endian.binary::append_float_le(value)
připojí hodnotu float k binární hodnotě v malém endian pořadí.binary::append_double_be(value)
připojí dvojitou hodnotu k binární hodnotě v pořadí big-endian.binary::append_double_le(value)
připojí dvojitou hodnotu k binární hodnotě v malém endian pořadí.binary::append_bool(value; bit)
připojí logickou hodnotu k jednomu bajtu v binární hodnotě a nastaví daný bit na logickou hodnotu.binary::append_utf8(value)
připojí řetězec UTF-8 k binární hodnotě.
Binární příklady
Tato část ukazuje některé běžné případy použití pro práci s binárními daty. V příkladech se používá běžná vstupní zpráva.
Předpokládejme, že máte zprávu s datovou částí, která je vlastním binárním formátem, který obsahuje více oddílů. Každá část obsahuje následující data v pořadí velkých bajtů:
- Uint32, který obsahuje délku názvu pole v bajtech.
- Řetězec utf-8, který obsahuje název pole, jehož délka předchozí uint32 určuje.
- Dvojitá hodnota, která obsahuje hodnotu pole.
V tomto příkladu máte tři z těchto částí, které obsahují:
(uint32) 11
(utf-8) teplota
(double) 86,0
(uint32) 8
(utf-8) vlhkost
(double) 51,290
(uint32) 8
(utf-8) tlak
(double) 346,23
Tato data vypadají při tisku v payload
části zprávy takto:
{
"payload": "base64::AAAAC3RlbXBlcmF0dXJlQFWAAAAAAAAAAAAIaHVtaWRpdHlASaUeuFHrhQAAAAhwcmVzc3VyZUB1o64UeuFI"
}
Poznámka:
Reprezentace base64::<string>
binárních dat je určená jen pro snadné odlišování od jiných typů a není reprezentativní pro fyzický formát dat během zpracování.
Přímé extrahování hodnot
Pokud znáte přesnou strukturu zprávy, můžete z ní načíst hodnoty pomocí odpovídajících posunů.
K extrahování hodnot použijte následující výraz jq:
import "binary" as binary;
.payload | {
temperature: binary::read_double_be(15),
humidity: binary::read_double_be(35),
pressure: binary::read_double_be(55)
}
Následující JSON ukazuje výstup z předchozího výrazu jq:
{
"humidity": 51.29,
"pressure": 346.23,
"temperature": 86
}
Dynamické extrahování hodnot
Pokud by zpráva mohla obsahovat všechna pole v libovolném pořadí, můžete dynamicky extrahovat celou zprávu:
K extrahování hodnot použijte následující výraz jq:
import "binary" as binary;
.payload
| {
parts: {},
rest: binary::toarray
}
|
until(
(.rest | length) == 0;
(.rest | binary::read_uint32_be) as $length
| {
parts: (
.parts +
{
(.rest | binary::read_utf8(4; $length)): (.rest | binary::read_double_be(4 + $length))
}
),
rest: .rest[(12 + $length):]
}
)
| .parts
Následující JSON ukazuje výstup z předchozího výrazu jq:
{
"humidity": 51.29,
"pressure": 346.23,
"temperature": 86
}
Přímé úpravy hodnot
Tento příklad ukazuje, jak upravit jednu z hodnot. Stejně jako v případě extrakce je jednodušší, pokud víte, kde je hodnota, kterou chcete upravit, v binárních datech. Tento příklad ukazuje, jak převést teplotu z fahrenheita na stupně celsia.
Následující výraz jq převede teplotu z fahrenheita na stupně celsia v binární zprávě:
import "binary" as binary;
15 as $index
| .payload
| binary::write_double_be(
((5 / 9) * (binary::read_double_be($index) - 32));
$index
)
Následující JSON ukazuje výstup z předchozího výrazu jq:
"base64::AAAAC3RlbXBlcmF0dXJlQD4AAAAAAAAAAAAIaHVtaWRpdHlASaUeuFHrhQAAAAhwcmVzc3VyZUB1o64UeuFI"
Pokud použijete dříve zobrazenou logiku extrakce, získáte následující výstup:
{
"humidity": 51.29,
"pressure": 346.23,
"temperature": 30
}
Dynamické úpravy hodnot
Tento příklad ukazuje, jak dosáhnout stejného výsledku jako v předchozím příkladu dynamickým vyhledáním požadované hodnoty v dotazu.
Pomocí následujícího výrazu jq převeďte teplotu z fahrenheita na stupně celsia v binární zprávě a dynamicky nalokujte data, která chcete upravit:
import "binary" as binary;
.payload
| binary::edit(
{
index: 0,
data: .
}
| until(
(.data | length) <= .index;
.index as $index
| (.data | binary::read_uint32_be($index)) as $length
| if (.data | binary::read_utf8($index + 4; $length)) == "temperature" then
(
(.index + 4 + $length) as $index
| .data |= binary::write_double_be(((5 / 9) * (binary::read_double_be($index) - 32)); $index)
)
end
| .index += $length + 12
)
| .data
)
Následující JSON ukazuje výstup z předchozího výrazu jq:
"base64::AAAAC3RlbXBlcmF0dXJlQD4AAAAAAAAAAAAIaHVtaWRpdHlASaUeuFHrhQAAAAhwcmVzc3VyZUB1o64UeuFI"
Vložení nových hodnot
Přidejte nové hodnoty pomocí přidávacích funkcí balíčku. Pokud chcete například přidat windSpeed
pole s hodnotou 31.678
vstupu při zachování příchozího binárního formátu, použijte následující výraz jq:
import "binary" as binary;
"windSpeed" as $key
| 31.678 as $value
| .payload
| binary::append_uint32_be($key | length)
| binary::append_utf8($key)
| binary::append_double_be($value)
Následující JSON ukazuje výstup z předchozího výrazu jq:
"base64:AAAAC3RlbXBlcmF0dXJlQFWAAAAAAAAAAAAIaHVtaWRpdHlASaUeuFHrhQAAAAhwcmVzc3VyZUB1o64UeuFIAAAACXdpbmRTcGVlZEA/rZFocrAh"
Související obsah
Váš názor
https://aka.ms/ContentUserFeedback.
Připravujeme: V průběhu roku 2024 budeme postupně vyřazovat problémy z GitHub coby mechanismus zpětné vazby pro obsah a nahrazovat ho novým systémem zpětné vazby. Další informace naleznete v tématu:Odeslat a zobrazit názory pro