Asignación de datos mediante flujos de datos
Importante
En esta página se incluyen instrucciones para administrar componentes de operaciones de IoT de Azure mediante manifiestos de implementación de Kubernetes, que se encuentra en versión preliminar. Esta característica se proporciona con varias limitacionesy no se debe usar para cargas de trabajo de producción.
Consulte Términos de uso complementarios para las versiones preliminares de Microsoft Azure para conocer los términos legales que se aplican a las características de Azure que se encuentran en la versión beta, en versión preliminar o que todavía no se han publicado para que estén disponibles con carácter general.
Use el lenguaje de asignación de flujos de datos para transformar datos en Operaciones de IoT de Azure. La sintaxis es una manera sencilla, pero eficaz, de definir asignaciones que transforman datos de un formato a otro. En este artículo se proporciona información general sobre el lenguaje de asignación de flujos de datos y los conceptos clave.
La asignación le permiten transformar datos de un formato a otro. Tenga en cuenta el registro de entrada siguiente:
{
"Name": "Grace Owens",
"Place of birth": "London, TX",
"Birth Date": "19840202",
"Start Date": "20180812",
"Position": "Analyst",
"Office": "Kent, WA"
}
Compárelo con el registro de salida:
{
"Employee": {
"Name": "Grace Owens",
"Date of Birth": "19840202"
},
"Employment": {
"Start Date": "20180812",
"Position": "Analyst, Kent, WA",
"Base Salary": 78000
}
}
En el registro de salida, se realizan los cambios siguientes en los datos del registro de entrada:
- Campos a los que se ha cambiado de nombre: el campo
Birth Date
ahora esDate of Birth
. - Campos reestructurados: tanto
Name
comoDate of Birth
se agrupan en la nueva categoría deEmployee
. - Campo eliminado: el campo
Place of birth
se quita porque no está presente en la salida. - Campo agregado: el campo
Base Salary
es un campo nuevo en la categoríaEmployment
. - Valores de campo modificados o combinados: el campo
Position
de la salida combina los camposPosition
yOffice
de la entrada.
Las transformaciones se logran mediante la asignación, que normalmente implica lo siguiente:
- Definición de entrada: identificación de los campos de los registros de entrada que se usan.
- Definición de salida: especificación de dónde y cómo se organizan los campos de entrada en los registros de salida.
- Conversión (opcional): modificación de los campos de entrada que caben en los campos de salida.
expression
es necesario cuando se combinan varios campos de entrada en un único campo de salida.
A continuación se muestra una asignación de ejemplo:
{
inputs: [
'BirthDate'
]
output: 'Employee.DateOfBirth'
}
{
inputs: [
'Position' // - - - - $1
'Office' // - - - - $2
]
output: 'Employment.Position'
expression: '$1 + ", " + $2'
}
{
inputs: [
'$context(position).BaseSalary'
]
output: 'Employment.BaseSalary'
}
En el ejemplo se asigna:
- Asignación uno a uno:
BirthDate
se asigna directamente aEmployee.DateOfBirth
sin conversión. - Asignación de varios a uno: se combina
Position
yOffice
en un único campo deEmployment.Position
. La fórmula de conversión ($1 + ", " + $2
) combina estos campos en una cadena con formato. - Datos contextuales:
BaseSalary
se agrega desde un conjunto de datos contextual denominadoposition
.
Referencias de campo
Las referencias de campo muestran cómo especificar rutas de acceso en la entrada y salida mediante la notación de puntos, como Employee.DateOfBirth
, o bien el acceso a datos desde un conjunto de datos contextual mediante $context(position)
.
Propiedades de metadatos MQTT y Kafka
Al usar MQTT o Kafka como origen o destino, puede acceder a las propiedades de varios metadatos en el lenguaje de asignación. Estas propiedades de usuario se pueden asignar en la entrada o salida.
Propiedades de metadatos
- Tema: funciona tanto para MQTT como para Kafka. Contiene la cadena donde se publicó el mensaje. Ejemplo:
$metadata.topic
. - Propiedad de usuario: en MQTT, hace referencia a los pares clave-valor de forma libre que puede llevar un mensaje MQTT. Por ejemplo, si el mensaje MQTT se publicó con una propiedad de usuario con la clave "priority" y el valor "high", la referencia de
$metadata.user_property.priority
contiene el valor "high". Las claves de propiedad de usuario pueden ser cadenas arbitrarias y pueden requerir escape:$metadata.user_property."weird key"
usa la clave "weird key" (con un espacio). - Propiedad del sistema: este término se usa para cada propiedad que no es una propiedad de usuario. Actualmente, solo se admite una sola propiedad del sistema:
$metadata.system_property.content_type
, que lee la propiedad de tipo de contenido del mensaje MQTT (si se establece). - Encabezado: es el equivalente de Kafka de la propiedad de usuario de MQTT. Kafka puede usar cualquier valor binario para una clave, pero el flujo de datos solo admite claves de cadena UTF-8. Ejemplo:
$metadata.header.priority
. Esta funcionalidad es similar a las propiedades del usuario.
Asignación de propiedades de metadatos
Asignación de datos de entrada
En el ejemplo siguiente, la propiedad topic
de MQTT se asigna al campo origin_topic
en la salida:
inputs: [
'$metadata.topic'
]
output: 'origin_topic'
Si la propiedad de usuario priority
está presente en el mensaje MQTT, en el ejemplo siguiente se muestra cómo asignarla a un campo de salida:
inputs: [
'$metadata.user_property.priority'
]
output: 'priority'
Asignación de salida
También puede asignar propiedades de metadatos a un encabezado de salida o a una propiedad de usuario. En el ejemplo siguiente, topic
del MQTT se asigna al campo origin_topic
en la propiedad de usuario de la salida:
inputs: [
'$metadata.topic'
]
output: '$metadata.user_property.origin_topic'
Si la carga entrante contiene un campo priority
, en el ejemplo siguiente se muestra cómo asignarla a una propiedad de usuario MQTT:
inputs: [
'priority'
]
output: '$metadata.user_property.priority'
El mismo ejemplo para Kafka:
inputs: [
'priority'
]
output: '$metadata.header.priority'
Selectores de conjuntos de datos de contextualización
Estos selectores permiten que las asignaciones integren datos adicionales de bases de datos externas, denominadas conjuntos de datos de contextualización.
Filtrado de registros
El filtrado de registros implica establecer condiciones para seleccionar qué registros se deben procesar o quitar.
Notación de puntos
La notación de puntos se usa ampliamente en informática para hacer referencia a campos, incluso de manera recursiva. En programación, los nombres de campo normalmente constan de letras y números. Un ejemplo de notación de puntos estándar podría ser como el de este ejemplo:
inputs: [
'Person.Address.Street.Number'
]
En un flujo de datos, una ruta de acceso descrita por la notación de puntos podría incluir cadenas y algunos caracteres especiales sin necesidad de escape:
inputs: [
'Person.Date of Birth'
]
En otros casos, es necesario el escape:
inputs: [
'Person."Tag.10".Value'
]
El ejemplo anterior, entre otros caracteres especiales, contiene puntos dentro del nombre del campo. Si no se escapa, el nombre del campo serviría como separador en la notación de puntos.
Mientras un flujo de datos analiza una ruta de acceso, trata solo dos caracteres como especiales:
- Los puntos (
.
) actúan como separadores de campo. - Las comillas simples, cuando se colocan al principio o al final de un segmento, inician una sección con escape donde los puntos no se tratan como separadores de campo.
Cualquier otro carácter se trata como parte del nombre del campo. Esta flexibilidad es útil en formatos como JSON, donde los nombres de campo pueden ser cadenas arbitrarias.
En Bicep, todas las cadenas deben estar entre comillas simples ('
). Los ejemplos sobre las comillas adecuadas en YAML para Kubernetes no se aplican.
Escape
La función principal del escape en una ruta de acceso con notación de puntos es dar cabida al uso de puntos que forman parte de nombres de campo en lugar de separadores:
inputs: [
'Payload."Tag.10".Value'
]
En este ejemplo, la ruta de acceso consta de tres segmentos: Payload
, Tag.10
y Value
.
Reglas de escape en la notación de puntos
Aplicar escape a cada segmento por separado: si varios segmentos contienen puntos, esos segmentos se deben incluir entre comillas dobles. Otros segmentos también se pueden incluir entre comillas, pero no afecta a la interpretación de la ruta de acceso:
inputs: [ 'Payload."Tag.10".Measurements."Vibration.$12".Value' ]
Uso adecuado de comillas dobles: las comillas dobles deben abrir y cerrar un segmento con escape. Las comillas del centro del segmento se consideran parte del nombre del campo:
inputs: [ 'Payload.He said: "Hello", and waved' ]
En este ejemplo se definen dos campos: Payload
y He said: "Hello", and waved
. Cuando aparece un punto en estas circunstancias, sigue funcionando como separador:
inputs: [
'Payload.He said: "No. It is done"'
]
En este caso, la ruta de acceso se divide en los segmentos Payload
, He said: "No
y It is done"
(empezando por un espacio).
Algoritmo de segmentación
- Si el primer carácter de un segmento es una comilla, el analizador busca la siguiente comilla. La cadena incluida entre estas comillas se considera un único segmento.
- Si el segmento no comienza con una comilla, el analizador identifica los segmentos buscando el siguiente punto o el final de la ruta de acceso.
Wildcard (Carácter comodín)
En muchos escenarios, el registro de salida se parece mucho al registro de entrada, y solo se necesitan pequeñas modificaciones. Cuando se trabaja con registros que contienen numerosos campos, la especificación manual de asignaciones para cada campo puede resultar tediosa. Los caracteres comodín simplifican este proceso al permitir asignaciones generalizadas que se pueden aplicar automáticamente a varios campos.
Imagine un escenario básico para comprender el uso de los asteriscos en las asignaciones:
inputs: [
'*'
]
output: '*'
Esta configuración muestra una asignación básica en la que cada campo de la entrada se asigna directamente al mismo campo de la salida sin cambios. El asterisco (*
) actúa como un carácter comodín que coincide con cualquier campo del registro de entrada.
Aquí se muestra cómo funciona el asterisco (*
) en este contexto:
- Coincidencia de patrones: el asterisco puede coincidir con uno o varios segmentos de una ruta de acceso. Actúa como marcador de posición para los segmentos de la ruta de acceso.
- Coincidencia de campos: durante el proceso de asignación, el algoritmo evalúa cada campo del registro de entrada con el patrón especificado en
inputs
. El asterisco del ejemplo anterior coincide con todas las rutas posibles, lo que ajusta de forma eficaz todos los campos individuales de la entrada. - Segmento capturado: la parte de la ruta de acceso con la que coincide el asterisco se conoce como
captured segment
. - Asignación de salida: en la configuración de salida,
captured segment
se coloca donde aparece el asterisco. Esto significa que la estructura de la entrada se conserva en la salida, ycaptured segment
rellena el marcador de posición proporcionado por el asterisco.
Otro ejemplo muestra cómo se pueden usar caracteres comodín para buscar coincidencias con subsecciones y moverlas de manera conjunta. En este ejemplo se aplanan eficazmente las estructuras anidadas dentro de un objeto JSON.
JSON original:
{
"ColorProperties": {
"Hue": "blue",
"Saturation": "90%",
"Brightness": "50%",
"Opacity": "0.8"
},
"TextureProperties": {
"type": "fabric",
"SurfaceFeel": "soft",
"SurfaceAppearance": "matte",
"Pattern": "knitted"
}
}
Configuración de asignación que usa caracteres comodín:
{
inputs: [
'ColorProperties.*'
]
output: '*'
}
{
inputs: [
'TextureProperties.*'
]
output: '*'
}
JSON resultante:
{
"Hue": "blue",
"Saturation": "90%",
"Brightness": "50%",
"Opacity": "0.8",
"type": "fabric",
"SurfaceFeel": "soft",
"SurfaceAppearance": "matte",
"Pattern": "knitted"
}
Colocación de los caracteres comodín
Al colocar un carácter comodín, debe seguir estas reglas:
- Un solo asterisco por referencia de datos: solo se permite un asterisco (
*
) dentro de una única referencia de datos. - Coincidencia de segmentos completa: el asterisco debe coincidir siempre con un segmento completo de la ruta de acceso. No se puede usar para que coincida solo con una parte de un segmento, como
path1.partial*.path3
. - Posicionamiento: el asterisco se puede colocar en varias partes de una referencia de datos:
- Al inicio:
*.path2.path3
: aquí, el asterisco coincide con cualquier segmento que lleva apath2.path3
. - En el centro:
path1.*.path3
: en esta configuración, el asterisco coincide con cualquier segmento entrepath1
ypath3
. - Al final:
path1.path2.*
: el asterisco al final coincide con cualquier segmento que aparezca después depath1.path2
.
- Al inicio:
- La ruta de acceso que contiene el asterisco debe incluirse entre comillas simples (
'
).
Caracteres comodín de entrada múltiple
JSON original:
{
"Saturation": {
"Max": 0.42,
"Min": 0.67,
},
"Brightness": {
"Max": 0.78,
"Min": 0.93,
},
"Opacity": {
"Max": 0.88,
"Min": 0.91,
}
}
Configuración de asignación que usa caracteres comodín:
inputs: [
'*.Max' // - $1
'*.Min' // - $2
]
output: 'ColorProperties.*'
expression: '($1 + $2) / 2'
JSON resultante:
{
"ColorProperties" : {
"Saturation": 0.54,
"Brightness": 0.85,
"Opacity": 0.89
}
}
Si hay caracteres comodín de entrada múltiple, el asterisco (*
) debe representar de forma coherente el mismo elemento Captured Segment
en cada entrada. Por ejemplo, cuando *
captura Saturation
en el patrón *.Max
, el algoritmo de asignación espera que el elemento Saturation.Min
correspondiente coincida con el patrón *.Min
. En este caso, *
se sustituye por el elemento Captured Segment
de la primera entrada, lo que guía la coincidencia para las entradas posteriores.
Considere este ejemplo detallado:
JSON original:
{
"Saturation": {
"Max": 0.42,
"Min": 0.67,
"Mid": {
"Avg": 0.51,
"Mean": 0.56
}
},
"Brightness": {
"Max": 0.78,
"Min": 0.93,
"Mid": {
"Avg": 0.81,
"Mean": 0.82
}
},
"Opacity": {
"Max": 0.88,
"Min": 0.91,
"Mid": {
"Avg": 0.89,
"Mean": 0.89
}
}
}
Configuración inicial de asignación que usa caracteres comodín:
inputs: [
'*.Max' // - $1
'*.Min' // - $2
'*.Avg' // - $3
'*.Mean' // - $4
]
Esta asignación inicial intenta crear una matriz (por ejemplo, para Opacity
: [0.88, 0.91, 0.89, 0.89]
). Se produce un error en esta configuración porque:
- El primer elemento
*.Max
de entrada captura un segmento comoSaturation
. - La asignación espera que las entradas posteriores estén presentes en el mismo nivel:
Saturation.Max
Saturation.Min
Saturation.Avg
Saturation.Mean
Como Avg
y Mean
se anidan dentro de Mid
, el asterisco de la asignación inicial no captura correctamente estas rutas de acceso.
Configuración de asignación corregida:
inputs: [
'*.Max' // - $1
'*.Min' // - $2
'*.Mid.Avg' // - $3
'*.Mid.Mean' // - $4
]
Esta asignación revisada captura con precisión los campos necesarios. Especifica correctamente las rutas de acceso para incluir el objeto Mid
anidado, lo que garantiza que los asteriscos funcionen eficazmente en distintos niveles de la estructura JSON.
Comparación de la segunda regla con la especialización
Al usar el ejemplo anterior de caracteres comodín de entrada múltiple, tenga en cuenta las siguientes asignaciones que generan dos valores derivados para cada propiedad:
{
inputs: [
'*.Max' // - $1
'*.Min' // - $2
]
output: 'ColorProperties.*.Avg'
expression: '($1 + $2) / 2'
}
{
inputs: [
'*.Max' // - $1
'*.Min' // - $2
]
output: 'ColorProperties.*.Diff'
expression: '$1 - $2'
}
Esta asignación está pensada para crear dos cálculos independientes (Avg
y Diff
) para cada propiedad en ColorProperties
. En este ejemplo se muestra el resultado:
{
"ColorProperties": {
"Saturation": {
"Avg": 0.54,
"Diff": 0.25
},
"Brightness": {
"Avg": 0.85,
"Diff": 0.15
},
"Opacity": {
"Avg": 0.89,
"Diff": 0.03
}
}
}
Aquí, la definición de la segunda asignación de las mismas entradas actúa como una segunda regla para la asignación.
Ahora, considere un escenario en el que un campo específico necesita un cálculo diferente:
{
inputs: [
'*.Max' // - $1
'*.Min' // - $2
]
output: 'ColorProperties.*'
expression: '($1 + $2) / 2'
}
{
inputs: [
'Opacity.Max' // - $1
'Opacity.Min' // - $2
]
output: 'ColorProperties.OpacityAdjusted'
expression: '($1 + $2 + 1.32) / 2'
}
En este caso, el campo Opacity
tiene un cálculo único. Hay dos opciones para controlar este escenario superpuesto:
- Incluya ambas asignaciones para
Opacity
. Como los campos de salida son diferentes en este ejemplo, no se invalidan entre ellos. - Use la regla más específica para
Opacity
y quite la más genérica.
Considere un caso especial para los mismos campos a fin de decidir la acción correcta:
{
inputs: [
'*.Max' // - $1
'*.Min' // - $2
]
output: 'ColorProperties.*'
expression: '($1 + $2) / 2'
}
{
inputs: [
'Opacity.Max' // - $1
'Opacity.Min' // - $2
]
output: ''
}
Un campo output
vacío en la segunda definición implica no escribir los campos en el registro de salida (lo que quita Opacity
). Esta configuración tiene más de Specialization
que de Second Rule
.
Resolución de asignaciones superpuestas por flujos de datos:
- La evaluación avanza desde la regla superior en la definición de asignación.
- Si una nueva asignación se resuelve en los mismos campos que una regla anterior, se aplican las siguientes condiciones:
- Se calcula un elemento
Rank
para cada entrada resuelta en función del número de segmentos que captura el carácter comodín. Por ejemplo, siCaptured Segments
sonProperties.Opacity
,Rank
es 2. Si solo esOpacity
,Rank
es 1. Una asignación sin caracteres comodín tiene un valorRank
de 0. - Si
Rank
en la última regla es igual o superior a la regla anterior, un flujo de datos lo trata como una instancia deSecond Rule
. - De lo contrario, el flujo de datos trata la configuración como
Specialization
.
- Se calcula un elemento
Por ejemplo, la asignación que dirige Opacity.Max
y Opacity.Min
a una salida vacía tiene un elemento Rank
de cero. Como la segunda regla tiene un elemento Rank
inferior a la anterior, se considera una especialización e invalida la regla anterior, que calcularía un valor para Opacity
.
Caracteres comodín en conjuntos de datos de contextualización
Ahora, veamos cómo se pueden usar los conjuntos de datos de contextualización con caracteres comodín a través de un ejemplo. Considere un conjunto de datos denominado position
que contiene el registro siguiente:
{
"Position": "Analyst",
"BaseSalary": 70000,
"WorkingHours": "Regular"
}
En un ejemplo anterior, se ha usado un campo específico de este conjunto de datos:
inputs: [
'$context(position).BaseSalary'
]
output: 'Employment.BaseSalary'
Esta asignación copia BaseSalary
del conjunto de datos de contexto directamente en la sección Employment
del registro de salida. Si quiere automatizar el proceso e incluir todos los campos del conjunto de datos position
en la sección Employment
, puede usar caracteres comodín:
inputs: [
'$context(position).*'
]
output: 'Employment.*'
Esta configuración permite una asignación dinámica en la que cada campo del conjunto de datos position
se copia en la sección Employment
del registro de salida:
{
"Employment": {
"Position": "Analyst",
"BaseSalary": 70000,
"WorkingHours": "Regular"
}
}
Último valor conocido
Puede realizar un seguimiento del último valor conocido de una propiedad. Sufijo el campo de entrada con ? $last
para capturar el último valor conocido del campo. Cuando falta un valor en una carga de entrada posterior, el último valor conocido se asigna a la carga de salida.
Por ejemplo, considere la siguiente asignación:
inputs: [
'Temperature ? $last'
]
output: 'Thermostat.Temperature'
En este ejemplo, se realiza el seguimiento del último valor conocido de Temperature
. Si una carga de entrada posterior no contiene un valor Temperature
, el último valor conocido se usa en la salida.