データ プロセッサの jq パス式とは

重要

Azure Arc によって有効にされる Azure IoT Operations Preview は、 現在プレビュー段階です。 運用環境ではこのプレビュー ソフトウェアを使わないでください。

一般公開リリースが利用可能になった場合は、新しい Azure IoT Operations インストールをデプロイする必要があります。プレビュー インストールをアップグレードすることはできません。

ベータ版、プレビュー版、または一般提供としてまだリリースされていない Azure の機能に適用される法律条項については、「Microsoft Azure プレビューの追加使用条件」を参照してください。

データ プロセッサのパイプライン ステージの多くは、 jq パス 式を使用します。 メッセージから情報を取得したり、メッセージに情報を配置したりする必要があるときは常に、パスを使います。 jq パスを使うと、次のことができます。

  • メッセージ内の情報を見つけます。
  • メッセージに情報を配置する場所を特定します。

どちらの場合も同じ構文を使い、メッセージ構造のルートを基準にして場所を指定します。

データ プロセッサでサポートされる jq パスは、 jq の構文的に正しいですが、セマンティクスが簡略化され、使いやすくなり、データ プロセッサ パイプラインのエラーを減らすことができます。 特に、データ プロセッサは、 ? 構文を使用して、データ構造がずれている場合のエラーを抑制しません。 これらのエラーは、パスを操作するときは自動的に抑制されます。

Data Processor パイプライン内のデータ アクセスの例には、集計最後の既知の値ステージの inputPath が含まれます。 Data Processor メッセージ内のデータにアクセスする必要がある場合は常に、データ アクセス パターンを使います。

データ更新ではデータ アクセスと同じ構文を使いますが、特定の更新シナリオではいくつかの特別な動作があります。 Data Processor パイプライン内のデータ更新の例には、集計最後の既知の値パイプライン ステージの outputPath が含まれます。 操作の結果をデータ プロセッサ メッセージに配置する必要がある場合は常に、データ更新パターンを使用します。

Note

Data Processor メッセージには、メッセージの本文以外のものが含まれます。 Data Processor メッセージには、送信したプロパティとメタデータ、およびその他の関連するシステム情報が含まれます。 処理パイプラインに送信されたデータを含むプライマリ ペイロードは、メッセージのルートにある payload フィールドに配置されます。 このため、このガイドの多くの例には、.payload で始まるパスが含まれます。

構文

すべての jq パスは、次のセグメントの 1 つ以上のシーケンスで構成されます。

  • ルート パス: .
  • 次のいずれかを使うマップまたはオブジェクト内のフィールド。
    • 英数字オブジェクト キーの .<identifier>。 たとえば、.temperature のようにします。
    • 任意のオブジェクト キーの ."<identifier>"。 たとえば、."asset-id" のようにします。
    • ["<identifier>"] または任意のオブジェクト キー。 たとえば、["asset-id"] のようにします。
  • 配列インデックス: [<index>]。 例: [2]

パスは常に . で始まる必要があります。 パスの先頭に配列または複合マップ キーがある場合でも、その前に . を付ける必要があります。 .["complex-key"].[1].value は有効なパスです。 ["complex-key"][1].value は無効なパスです。

メッセージ例:

以下のデータ アクセスとデータ更新の例では、次のメッセージを使って、さまざまなパス式の使用方法を示します。

{
  "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
  }
}

データ アクセスの場合のルート パス

最も基本的なパスはルート パスであり、メッセージのルートを指し、メッセージ全体を返します。 次のような jq パスを考えてください。

.

結果は次のとおりです。

{
  "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
  }
}

データ アクセスの場合の単純な識別子

次に最も簡単なパスには、1 つの識別子 (この場合は payload フィールド) が含まれます。 次のような jq パスを考えてください。

.payload

ヒント

."payload".["payload"] も、このパスを指定する有効な方法です。 ただし、a-zA-Z0-9_ のみを含む識別子では、さらに複雑な構文は必要ありません。

結果は次のとおりです。

{
  "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
}

データ アクセスの場合の入れ子になったフィールド

パス セグメントを組み合わせて、単一のリーフ値など、メッセージ内で深く入れ子になったデータを取得できます。 次の 2 つの jq パスのいずれかを考えてください。

.payload.Payload.["dtmi:com:prod1:slicer3345:temperature"].Value
.payload.Payload."dtmi:com:prod1:slicer3345:temperature".Value

結果は次のとおりです。

46

データ アクセスの場合の配列要素

配列要素はマップ キーと同じように動作しますが、[] で文字列の代わりに数値を使う点が異なります。 次の 2 つの jq パスのいずれかを考えてください。

.payload.Payload.["dtmi:com:prod1:slicer3345:lineStatus"].Value[1]
.payload.Payload."dtmi:com:prod1:slicer3345:lineStatus".Value[1]

結果は次のとおりです。

5

データ アクセスでの存在しないパスと無効なパス

jq パスで存在しない場所、またはメッセージの構造と互換性のない場所を指定した場合、値は返されません。

重要

一部の処理ステージでは、何らかの値が存在している必要があり、値が見つからない場合は失敗することがあります。 その他は通常どおり処理を続けるように設計されており、パスに値が見つからない場合は、要求された操作をスキップするか、別のアクションを実行します。

次のような jq パスを考えてください。

.payload[1].temperature

結果は次のとおりです。

値なし

データ更新の場合のルート パス

最も基本的なパスはルート パスであり、メッセージのルートを指し、メッセージ全体を置き換えます。 次のような新しく挿入する値と jq パスを考えてください。

{ "update": "data" }
.

結果は次のとおりです。

{ "update": "data" }

更新は前のデータと深くマージされるのではなく、更新が行われるレベルでデータを置き換えます。 データが上書きされないようにするには、更新のスコープを変更したい最も詳細なパスに設定するか、主要なデータとは異なるフィールドを更新します。

データ更新の場合の単純な識別子

次に最も簡単なパスには、1 つの識別子 (この場合は payload フィールド) が含まれます。 次のような新しく挿入する値と jq パスを考えてください。

{ "update": "data" }
.payload

ヒント

."payload".["payload"] も、このパスを指定する有効な方法です。 ただし、a-zA-Z0-9_ のみを含む識別子では、さらに複雑な構文は必要ありません。

結果は次のとおりです。

{
  "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": { "update": "data" }
}

データ更新の場合の入れ子になったフィールド

パス セグメントを組み合わせて、単一のリーフ値など、メッセージ内で深く入れ子になったデータを取得できます。 次のような新しく挿入する値と 2 つの jq パスのいずれかを考えてください。

{ "update": "data" }
.payload.Payload.["dtmi:com:prod1:slicer3345:temperature"].Value
.payload.Payload."dtmi:com:prod1:slicer3345:temperature".Value

結果は次のとおりです。

{
  "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": { "update": "data" }
      }
    },
    "DataSetWriterName": "slicer-3345",
    "SequenceNumber": 461092
  }
}

データ更新の場合の配列要素

配列要素はマップ キーと同じように動作しますが、[] で文字列の代わりに数値を使う点が異なります。 次のような新しく挿入する値と 2 つの jq パスのいずれかを考えてください。

{ "update": "data" }
.payload.Payload.["dtmi:com:prod1:slicer3345:lineStatus"].Value[1]
.payload.Payload."dtmi:com:prod1:slicer3345:lineStatus".Value[1]

結果は次のとおりです。

{
  "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, { "update": "data" }, 2]
      },
      "dtmi:com:prod1:slicer3345:speed": {
        "sourceTimestamp": 1681926048,
        "value": 85
      },
      "dtmi:com:prod1:slicer3345:temperature": {
        "sourceTimestamp": 1681926048,
        "value": 46
      }
    },
    "DataSetWriterName": "slicer-3345",
    "SequenceNumber": 461092
  }
}

データ更新での存在しないパスと型が一致しないパス

jq パスで存在しない場所、またはメッセージの構造と互換性のない場所を指定した場合、次のセマンティクスが適用されます。

  • パスのセグメントが存在しない場合は、作成されます。
    • オブジェクト キーの場合、キーがオブジェクトに追加されます。
    • 配列インデックスの場合、必要なインデックスを保持するのに十分な長さになるまで、配列が null 値で延長された後、インデックスが更新されます。
    • 負の配列インデックスの場合、同じ延長手順が実行されますが、最初の要素が置き換えられます。
  • パスのセグメントの型が必要な型と異なる場合は、式によって型が変更されて、そのパスの場所にある既存のデータは破棄されます。

次の例では、前の例と同じ入力メッセージを使って、次の新しい値を挿入します。

{ "update": "data" }

次のような jq パスを考えてください。

.payload[1].temperature

結果は次のとおりです。

{
  "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": [null, { "update": "data" }]
}

次のような jq パスを考えてください。

.payload.nested.additional.data

結果は次のとおりです。

{
  "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,
    "nested": {
      "additional": {
        "data": { "update": "data" }
      }
    }
  }
}

次のような jq パスを考えてください。

.systemProperties.partitionKey[-4]

結果は次のとおりです。

{
  "systemProperties": {
    "partitionKey": [{"update": "data"}, null, null, null],
    "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,
  }