Condividi tramite


Che cosa sono le espressioni di percorso jq nell'anteprima del processore di dati IoT di Azure?

Importante

Anteprima delle operazioni di Azure IoT: abilitata da Azure Arc è attualmente disponibile in ANTEPRIMA. Non è consigliabile usare questo software di anteprima negli ambienti di produzione.

Vedere le condizioni per l'utilizzo supplementari per le anteprime di Microsoft Azure per termini legali aggiuntivi che si applicano a funzionalità di Azure in versione beta, in anteprima o in altro modo non ancora disponibili a livello generale.

Molte fasi della pipeline nell'anteprima del processore di dati IoT di Azure usano espressioni di percorso jq. Ogni volta che è necessario recuperare informazioni da un messaggio o inserire alcune informazioni in un messaggio, si usa un percorso. I percorsi jq consentono di:

  • Individuare una parte di informazioni in un messaggio.
  • Identificare dove inserire una parte di informazioni in un messaggio.

Entrambi i casi usano la stessa sintassi e specificano percorsi relativi alla radice della struttura dei messaggi.

I percorsi jq supportati dal responsabile del trattamento dei dati sono sintatticamente corretti per jq, ma hanno una semantica semplificata per semplificarne l'uso e contribuire a ridurre gli errori nella pipleline del responsabile del trattamento dei dati. In particolare, il responsabile del trattamento dei dati non usa la ? sintassi per eliminare gli errori per strutture di dati non allineate. Tali errori vengono eliminati automaticamente durante l'utilizzo dei percorsi.

Esempi di accesso ai dati all'interno di una pipeline del processore di dati includono nelle inputPath fasi aggregate e dell'ultimo valore noto. Usare il modello di accesso ai dati ogni volta che è necessario accedere ad alcuni dati all'interno di un messaggio del responsabile del trattamento dei dati.

L'aggiornamento dei dati usa la stessa sintassi dell'accesso ai dati, ma esistono alcuni comportamenti speciali in scenari di aggiornamento specifici. Esempi di aggiornamento dei dati all'interno di una pipeline del processore di dati includono le outputPath fasi della pipeline di valori noti e aggregate. Usare il modello di aggiornamento dei dati ogni volta che è necessario inserire il risultato di un'operazione nel messaggio del responsabile del trattamento dei dati.

Nota

Un messaggio del responsabile del trattamento dei dati contiene più del corpo del messaggio. Un messaggio del responsabile del trattamento dei dati include tutte le proprietà e i metadati inviati dall'utente e altre informazioni di sistema pertinenti. Il payload primario contenente i dati inviati nella pipeline di elaborazione viene inserito in un payload campo nella radice del messaggio. Ecco perché molti degli esempi di questa guida includono percorsi che iniziano con .payload.

Sintassi

Ogni percorso jq è costituito da una sequenza di uno o più dei segmenti seguenti:

  • Percorso radice: ..
  • Campo in una mappa o in un oggetto che usa uno di:
    • .<identifier> per le chiavi dell'oggetto alfanumerico. Ad esempio: .temperature.
    • ."<identifier>" per chiavi oggetto arbitrarie. Ad esempio: ."asset-id".
    • ["<identifier>"] o chiavi di oggetto arbitrarie. Ad esempio: ["asset-id"].
  • Indice di matrice: [<index>]. Ad esempio, [2].

I percorsi devono sempre iniziare con .. Anche se si dispone di una matrice o di una chiave della mappa complessa all'inizio del percorso, deve essere presente un oggetto . che lo precede. I percorsi .["complex-key"] e .[1].value sono validi. I percorsi ["complex-key"] e [1].value non sono validi.

Messaggio di esempio

Gli esempi di accesso ai dati e aggiornamento dei dati seguenti usano il messaggio seguente per illustrare l'uso di espressioni di percorso diverse:

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

Percorso radice per l'accesso ai dati

Il percorso più semplice è il percorso radice, che punta alla radice del messaggio e restituisce l'intero messaggio. Dato il percorso jq seguente:

.

Il risultato è:

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

Identificatore semplice per l'accesso ai dati

Il percorso più semplice successivo prevede un singolo identificatore, in questo caso il payload campo . Dato il percorso jq seguente:

.payload

Suggerimento

."payload" e .["payload"] sono anche modi validi per specificare questo percorso. Tuttavia, gli identificatori che contengono a-zsolo , 0-9A-Ze _ non richiedono la sintassi più complessa.

Il risultato è:

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

Campi annidati per l'accesso ai dati

È possibile combinare segmenti di percorso per recuperare i dati annidati in profondità all'interno del messaggio, ad esempio un singolo valore foglia. Dato uno dei due percorsi jq seguenti:

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

Il risultato è:

46

Elementi della matrice per l'accesso ai dati

Gli elementi della matrice funzionano allo stesso modo delle chiavi di mapping, ad eccezione del fatto che si usa un numero sul posto di una stringa in[] . Dato uno dei due percorsi jq seguenti:

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

Il risultato è:

5

Percorsi inesistenti e non validi nell'accesso ai dati

Se un percorso jq identifica una posizione che non esiste o non è compatibile con la struttura del messaggio, non viene restituito alcun valore.

Importante

Alcune fasi di elaborazione richiedono che sia presente un valore e potrebbero non riuscire se non viene trovato alcun valore. Altri sono progettati per continuare l'elaborazione normalmente e ignorare l'operazione richiesta o eseguire un'azione diversa se non viene trovato alcun valore nel percorso.

Dato il percorso jq seguente:

.payload[1].temperature

Il risultato è:

Nessun valore

Percorso radice per l'aggiornamento dei dati

Il percorso più semplice è il percorso radice, che punta alla radice del messaggio e sostituisce l'intero messaggio. Dato il nuovo valore seguente da inserire e percorso jq:

{ "update": "data" }
.

Il risultato è:

{ "update": "data" }

Aggiornamenti non vengono uniti in modo approfondito con i dati precedenti, ma sostituire invece i dati a livello in cui si verifica l'aggiornamento. Per evitare di sovrascrivere i dati, definire l'ambito dell'aggiornamento al percorso più dettagliato che si vuole modificare o aggiornare un campo separato sul lato dei dati primari.

Identificatore semplice per l'aggiornamento dei dati

Il percorso più semplice successivo prevede un singolo identificatore, in questo caso il payload campo . Dato il nuovo valore seguente da inserire e percorso jq:

{ "update": "data" }
.payload

Suggerimento

."payload" e .["payload"] sono anche modi validi per specificare questo percorso. Tuttavia, gli identificatori che contengono a-zsolo , 0-9A-Ze _ non richiedono la sintassi più complessa.

Il risultato è:

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

Campi annidati per l'aggiornamento dei dati

È possibile combinare segmenti di percorso per recuperare i dati annidati in profondità all'interno del messaggio, ad esempio un singolo valore foglia. Dato il nuovo valore seguente da inserire e uno dei due percorsi jq seguenti:

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

Il risultato è:

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

Elementi della matrice per l'aggiornamento dei dati

Gli elementi della matrice funzionano allo stesso modo delle chiavi di mapping, ad eccezione del fatto che si usa un numero sul posto di una stringa in[] . Dato il nuovo valore seguente da inserire e uno dei due percorsi jq seguenti:

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

Il risultato è:

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

Percorsi non esistenti e di tipo non corrispondenti nell'aggiornamento dei dati

Se un percorso jq identifica una posizione che non esiste o non è compatibile con la struttura del messaggio, si applica la semantica seguente:

  • Se i segmenti del percorso non esistono, vengono creati:
    • Per le chiavi oggetto, la chiave viene aggiunta all'oggetto .
    • Per gli indici di matrice, la matrice viene allungata con null valori per renderla sufficientemente lunga da avere l'indice richiesto, quindi l'indice viene aggiornato.
    • Per gli indici di matrice negativi, viene eseguita la stessa procedura di lunghezza, ma il primo elemento viene sostituito.
  • Se un segmento di percorso ha un tipo diverso da quello necessario, l'espressione modifica il tipo e rimuove tutti i dati esistenti in tale percorso.

Gli esempi seguenti usano lo stesso messaggio di input degli esempi precedenti e inseriscono il nuovo valore seguente:

{ "update": "data" }

Dato il percorso jq seguente:

.payload[1].temperature

Il risultato è:

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

Dato il percorso jq seguente:

.payload.nested.additional.data

Il risultato è:

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

Dato il percorso jq seguente:

.systemProperties.partitionKey[-4]

Il risultato è:

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