Sdílet prostřednictvím


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 25vý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 temptemperature:

.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 a value jako hodnota klíče.
  • if .key == "temp" then .key = "temperature" else . end provádí podmíněnou logiku vůči klíči položky. Pokud je temp klíč, převede se na temperature 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 ne false , nebo null. 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 timestampjq extrahujte objekt , humiditytemperaturea 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 pro timestamp: .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 na data 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 extrahuje value 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 struktura from_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 a value. {timestamp, value} je zkratka pro {timestamp: .timestamp, value: .value}.
  • <expression> | from_entries převede maticovou hodnotu <expression> na objekt, namapuje key pole každé položky pole na klíč objektu a value 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 temperaturehumidity, 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 polem name, valuea timestamp 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 extrahuje name 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ěr value jednotlivých dílčích polí:
    • map(.value) převede dílčí pole na matici value 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 pole timestamp 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 .keyobjektu 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 .keyobjektu 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 pro to_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 a SequenceNumber jsou beze změny, takže můžete použít zkrácenou syntaxi namísto psaní DataSetWriterName: .DataSetWriterName a SequenceNumber: .SequenceNumber.
  • Tag: .Payload.key, extrahuje původní klíč objektu z vnitřní Payload a vyšší úrovně do nadřazeného objektu. Operace to_entries dříve v dotazu vytvořila key pole.
  • Value: .Payload.value.value a Timestamp: .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řevod temperatureF 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 extrahuje mass položku ze userProperties seznamu. Data jsou již nastavena jako objekty s key a value, takže from_entries je lze přímo převést na objekt. Výraz načte mass 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, ora not. Operátory and a or operátory jsou operátory infix. not je funkce, kterou vyvoláte jako filtr, <expression> | notnapří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 z true na false.

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 se has 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ýt null.
  • temperatureNotNull: (.temperature != null),tento a další podobné výrazy ukazují, jak != null kontrola provádí podobnou kontrolu .has Neexistující klíč v objektu je null v případě přístupu pomocí .<key> syntaxe nebo klíče existuje, ale má hodnotu null. Oba siteNotNull a missingNotNull 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 objektem has, 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šit null 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 hodnotu userProperties , aby zbytek <expression> byl méně podrobný.
  • any(.key == "mass").key == "mass" spustí výraz proti každému userProperties 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ému userProperties 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ého status 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 s end.

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í funkce to_entries. Funkce převede pole objektů se strukturou {"key": <key>, "value": <value>} na objekt s poli a keyvalue poli namapovanými na páry klíč/hodnota.
  • .key |= ltrimstr("rotor_") aktualizuje hodnotu .key v každé položce s výsledkem ltrimstr("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, ale reduce 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ě $itemmá 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. Pole totalChangea count pole previousslouží 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 tomto else případě. Tento výraz nastaví každé pole v objektu akumulátoru na novou hodnotu na základě výpočtu. Pro totalChange, 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í $itemhodnotu value pro další iteraci, která se má použít, a count 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ýstupem while smyčky je pole, které obsahuje hodnoty vytvořené každou iterací smyčky.
  • until like while použije operaci opakovaně vůči kontextu vstupních dat, aktualizuje hodnotu kontextu dat pro použití v další iteraci. Na rozdíl od whiletoho smyčka until 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ázvem stdev. Funkce vypočítá směrodatnou odchylku vzorku pole pomocí upravené verze odpovědi komunity na StackOverflow.
  • .payload |= <expression> první dva defjsou 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 jejich value 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, je orvý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) | lengthvalue 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 jako time 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ů na time.
  • time::tounix převede některý ze tří typů na unix.
  • time::toiso převede některý ze tří typů na iso.
  • time::istime vrátí hodnotu true, pokud jsou data ve time 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říklad time::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říklad time::offset("1m2s").
  • time::offset(value;unit) posune čas zadanou dobu trvání. Tato funkce používá číslo a řetězec jednotky. Například time::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"