Compartilhar via


O que são expressões de caminho jq na versão prévia do Processador de Dados do Azure IoT?

Importante

O recurso Pré-visualização de Operações do Azure IoT — habilitado pelo Azure Arc — está atualmente em VERSÃO PRÉVIA. Você não deve usar esse software em versão prévia em ambientes de produção.

Veja os Termos de Uso Complementares para Versões Prévias do Microsoft Azure para obter termos legais que se aplicam aos recursos do Azure que estão em versão beta, versão prévia ou que, de outra forma, ainda não foram lançados em disponibilidade geral.

Muitos estágios de pipeline na versão prévia do Processador de Dados IoT do Azure usam expressões de caminho jq. Sempre que você precisar recuperar informações de uma mensagem ou colocar algumas informações em uma mensagem, use um caminho. os caminhos jq permitem que você:

  • Localize uma informação em uma mensagem.
  • Identifique onde colocar uma informação em uma mensagem.

Ambos os casos usam a mesma sintaxe e especificam locais relativos à raiz da estrutura da mensagem.

Os caminhos jq compatíveis com o Processador de Dados estão sintaticamente corretos para jq, mas simplificaram a semântica para facilitar o uso deles e para ajudar a reduzir os erros na linha pipleline do Processador de Dados. Em particular, o Processador de Dados não usa a ? sintaxe para suprimir erros de estruturas de dados desalinhadas. Esses erros são suprimidos automaticamente para você ao trabalhar com caminhos.

Exemplos de acesso a dados em um pipeline de processador de dados incluem os inputPath nos estágios agregados e último valor conhecido. Use o padrão de acesso a dados sempre que precisar acessar alguns dados em uma mensagem de processador de dados.

A atualização de dados usa a mesma sintaxe que o acesso a dados, mas há alguns comportamentos especiais em cenários de atualização específicos. Exemplos de atualização de dados em um pipeline de processador de dados incluem o outputPath nos estágios de pipeline agregados e último valor conhecido. Use o padrão de atualização de dados sempre que precisar colocar o resultado de uma operação na mensagem do Processador de Dados.

Observação

Uma mensagem do processador de dados contém mais do que apenas o corpo da mensagem. Uma mensagem de processador de dados inclui todas as propriedades e metadados que você enviou e outras informações relevantes do sistema. O conteúdo primário que contém os dados enviados para o pipeline de processamento é colocado em um campo payload na raiz da mensagem. É por isso que muitos dos exemplos neste guia incluem caminhos que começam com .payload.

Sintaxe

Cada caminho jq consiste em uma sequência de um ou mais dos seguintes segmentos:

  • O caminho raiz: ..
  • Um campo em um mapa ou objeto que usa um dos seguintes:
    • .<identifier> para chaves de objeto alfanuméricas. Por exemplo, .temperature.
    • ."<identifier>" para chaves de objeto arbitrárias. Por exemplo, ."asset-id".
    • ["<identifier>"] ou chaves de objeto arbitrárias. Por exemplo, ["asset-id"].
  • Um índice de matriz: [<index>]. Por exemplo, [2].

Os caminhos sempre devem começar com um .. Mesmo que você tenha uma matriz ou uma chave de mapa complexa no início do caminho, deve haver uma . que a precede. Os caminhos .["complex-key"] e .[1].value são válidos. Os caminhos ["complex-key"] e [1].value são inválidos.

Mensagem de exemplo

Os seguintes exemplos de acesso a dados e atualização de dados usam a seguinte mensagem para ilustrar o uso de diferentes expressões de caminho:

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

Caminho raiz para acesso a dados

O caminho mais básico é o caminho raiz, que aponta para a raiz da mensagem e retorna a mensagem inteira. Dado o seguinte caminho jq:

.

O resultado é:

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

Identificador simples para acesso a dados

O próximo caminho mais simples envolve um único identificador, nesse caso, o campo payload. Dado o seguinte caminho jq:

.payload

Dica

."payload" e .["payload"] também são maneiras válidas de especificar esse caminho. No entanto, os identificadores que contêm apenas a-z, A-Z, 0-9 e _ não exigem a sintaxe mais complexa.

O resultado é:

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

Campos aninhados para acesso a dados

Você pode combinar segmentos de caminho para recuperar dados aninhados profundamente dentro da mensagem, como um único valor folha. Dado um dos dois caminhos jq a seguir:

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

O resultado é:

46

Elementos de matriz para acesso a dados

Os elementos de matriz funcionam da mesma maneira que as chaves de mapa, exceto pelo fato de você usar um número no lugar de uma cadeia de caracteres no []. Dado um dos dois caminhos jq a seguir:

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

O resultado é:

5

Caminhos inexistentes e inválidos no acesso a dados

Se um caminho jq identificar um local que não existe ou seja incompatível com a estrutura da mensagem, nenhum valor será retornado.

Importante

Alguns estágios de processamento exigem que algum valor esteja presente e podem falhar se nenhum valor for encontrado. Outros foram projetados para continuar o processamento normalmente e ignorar a operação solicitada ou executar uma ação diferente se nenhum valor for encontrado no caminho.

Dado o seguinte caminho jq:

.payload[1].temperature

O resultado é:

Sem valor

Caminho raiz para atualização de dados

O caminho mais básico é o caminho raiz, que aponta para a raiz da mensagem e substitui toda a mensagem. Dado o novo valor a seguir para inserir e caminho jq:

{ "update": "data" }
.

O resultado é:

{ "update": "data" }

As atualizações não são profundas mescladas com os dados anteriores, mas substituem os dados no nível em que a atualização acontece. Para evitar a substituição de dados, faça o escopo da atualização para o caminho mais refinado que você deseja alterar ou atualizar um campo separado para o lado dos dados primários.

Identificador simples para atualização de dados

O próximo caminho mais simples envolve um único identificador, nesse caso, o campo payload. Dado o novo valor a seguir para inserir e caminho jq:

{ "update": "data" }
.payload

Dica

."payload" e .["payload"] também são maneiras válidas de especificar esse caminho. No entanto, os identificadores que contêm apenas a-z, A-Z, 0-9 e _ não exigem a sintaxe mais complexa.

O resultado é:

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

Campos aninhados para atualização de dados

Você pode combinar segmentos de caminho para recuperar dados aninhados profundamente dentro da mensagem, como um único valor folha. Dado o novo valor a ser inserido e um dos dois caminhos jq a seguir:

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

O resultado é:

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

Elementos de matriz para atualização de dados

Os elementos de matriz funcionam da mesma maneira que as chaves de mapa, exceto pelo fato de você usar um número no lugar de uma cadeia de caracteres no []. Dado o novo valor a ser inserido e um dos dois caminhos jq a seguir:

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

O resultado é:

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

Caminhos inexistentes e incompatíveis de tipo na atualização de dados

Se um caminho jq identificar um local que não existe ou seja incompatível com a estrutura da mensagem, a seguinte semântica será aplicada:

  • Se qualquer segmento do caminho não existir, eles serão criados:
    • Para chaves de objeto, a chave é adicionada ao objeto.
    • Para índices de matriz, a matriz é alongada com valores null para torná-la longa o suficiente para ter o índice necessário e, em seguida, o índice é atualizado.
    • Para índices de matriz negativos, o mesmo procedimento de alongamento acontece, mas o primeiro elemento é substituído.
  • Se um segmento de caminho tiver um tipo diferente do necessário, a expressão alterará o tipo e descartará todos os dados existentes nesse local de caminho.

Os exemplos a seguir usam a mesma mensagem de entrada dos exemplos anteriores e inserem o seguinte novo valor:

{ "update": "data" }

Dado o seguinte caminho jq:

.payload[1].temperature

O resultado é:

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

Dado o seguinte caminho jq:

.payload.nested.additional.data

O resultado é:

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

Dado o seguinte caminho jq:

.systemProperties.partitionKey[-4]

O resultado é:

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