用于设备和模块孪生、作业和消息路由的 IoT 中心查询语言

IoT 中心提供类似于 SQL 的强大语言,用于检索有关设备孪生模块孪生作业消息路由的信息。 本文内容:

  • IoT 中心查询语言的主要功能简介,以及
  • 语言的详细说明。 有关用于消息路由的查询语言的详细信息,请参阅消息路由中的查询

有关特定示例,请参阅设备和模块孪生的查询作业查询

注意

本文中提到的某些功能(例如云到设备消息传递、设备孪生、设备管理)仅在 IoT 中心的标准层中提供。 有关 IoT 中心基本层和标准/免费层的详细信息,请参阅选择适合你的解决方案的 IoT 中心层

运行 IoT 中心查询

可以直接在 Azure 门户中针对 IoT 中心运行查询。

  1. 登录 Azure 门户,导航到 IoT 中心。
  2. 选择导航菜单的“设备管理”部分中的“查询”。
  3. 在文本框中输入查询,然后选择“运行查询”。

还可以使用 Azure IoT 服务 SDK 和服务 API 在应用程序内运行查询。

有关实现 IoT 中心查询的示例代码,请参阅具有服务 SDK 的查询示例部分。

有关指向 SDK 参考页和示例的链接,请参阅 Azure IoT SDK

IoT 中心查询基础知识

每个 IoT 中心查询都包括 SELECT 和 FROM 子句,以及可选的 WHERE 和 GROUP BY 子句。

查询在 JSON 文档的集合(例如设备孪生)上运行。 FROM 子句指示要迭代的文档集合(devices、devices.modules 或 devices.jobs)。

然后,应用 WHERE 子句中的筛选器。 使用聚合时,将按 GROUP BY 子句中的指定对此步骤的结果分组。 对于每组,将按照 SELECT 子句中的指定生成一行。

SELECT <select_list>
  FROM <from_specification>
  [WHERE <filter_condition>]
  [GROUP BY <group_specification>]

SELECT 子句

每个 IoT 中心查询都需要 SELECT <select_list> 子句。 它用于指定要通过查询检索的值。 它指定用于生成新 JSON 对象的 JSON 值。 对于 FROM 集合中已筛选子集(且可选择性分组)的每个元素,投影阶段将生成一个新 JSON 对象。 使用 SELECT 子句中指定的值构造此对象。

例如:

  • 返回所有值

    SELECT *
    
  • 返回特定属性

    SELECT DeviceID, LastActivityTime
    
  • 聚合查询结果以返回计数

    SELECT COUNT() as TotalNumber
    

目前,仅在设备孪生的聚合查询中才支持使用不同于 SELECT 的选择子句。

以下是 SELECT 子句的语法:

SELECT [TOP <max number>] <projection list>

<projection_list> ::=
    '*'
    | <projection_element> AS alias [, <projection_element> AS alias]+

<projection_element> :==
    attribute_name
    | <projection_element> '.' attribute_name
    | <aggregate>

<aggregate> :==
    count()
    | avg(<projection_element>)
    | sum(<projection_element>)
    | min(<projection_element>)
    | max(<projection_element>)

Attribute_name 引用 FROM 集合中 JSON 文档的任一属性。

FROM 子句

所有 IoT 中心查询都需要使用 FROM <from_specification> 子句。 它必须是以下三个值之一:

  • 用于查询设备孪生的 devices
  • 用于查询模块孪生的 devices.modules
  • 用于查询每个设备作业详细信息的 devices.jobs

例如:

  • 检索所有设备孪生

    SELECT * FROM devices
    

WHERE 子句

WHERE <filter_condition> 子句是可选的。 它指定要将 FROM 集合中的 JSON 文档内含在结果中时需满足的一项或多项条件。 任何 JSON 文档必须将指定的条件求值为“true”才能包含在结果中。

例如:

  • 检索以特定设备为目标的所有作业

    SELECT * FROM devices.jobs
      WHERE devices.jobs.deviceId = 'myDeviceId'
    

表达式和条件部分介绍了允许的条件。

GROUP BY 子句

GROUP BY <group_specification> 子句是可选的。 可在 WHERE 子句中指定的筛选器后、在 SELECT 中指定的投影前执行该子句。 它根据属性值来分组文档。 这些组用于生成 SELECT 子句中指定的聚合值。

例如:

  • 返回报告每个遥测配置状态的设备计数

    SELECT properties.reported.telemetryConfig.status AS status,
      COUNT() AS numberOfDevices
    FROM devices
    GROUP BY properties.reported.telemetryConfig.status
    

目前,仅在查询设备孪生时才支持使用 GROUP BY 子句。

注意

术语 group 目前被视为查询中的特殊关键字。 如果你使用 group 作为属性名,请考虑使用双括号将其括起来以避免错误,例如 SELECT * FROM devices WHERE tags.[[group]].name = 'some_value'

GROUP BY 的正式语法为:

GROUP BY <group_by_element>
<group_by_element> :==
    attribute_name
    | < group_by_element > '.' attribute_name

Attribute_name 引用 FROM 集合中 JSON 文档的任一属性。

查询结果分页

查询对象的最大页大小 小于等于 100 条记录进行实例化。 若要获取多个页面,请多次调用 Node.js SDK 上的 nextAsTwin 或 .Net SDK 上的 GetNextAsTwinAsync 方法。 一个查询对象可公开多个“下一步”值,具体取决于该查询所需的反序列化选项。 例如,一个查询对象可以返回设备孪生或作业对象,或使用投影时的普通 JSON。

表达式和条件

从较高层面讲,表达式可以:

  • 求值结果为 JSON 类型的实例(例如布尔值、数字、字符串、数组或对象)。
  • 由设备 JSON 文档中的操作数据以及使用内置运算符和函数的常量定义。

条件是求值为布尔值的表达式。 将任何不同于布尔值“true”的常数视为“false” 。 此规则包括“Null”、“undefined”、任何对象或数组实例、任何字符串和布尔值“false” 。

表达式的语法为:

<expression> ::=
    <constant> |
    attribute_name |
    <function_call> |
    <expression> binary_operator <expression> |
    <create_array_expression> |
    '(' <expression> ')'

<function_call> ::=
    <function_name> '(' expression ')'

<constant> ::=
    <undefined_constant>
    | <null_constant>
    | <number_constant>
    | <string_constant>
    | <array_constant>

<undefined_constant> ::= undefined
<null_constant> ::= null
<number_constant> ::= decimal_literal | hexadecimal_literal
<string_constant> ::= string_literal
<array_constant> ::= '[' <constant> [, <constant>]+ ']'

若要了解表达式语法中的每个符号表示什么,请参阅下表:

符号 定义
attribute_name FROM 集合中 JSON 文档的任一属性。
binary_operator 运算符部分列出的任意二进制运算符。
function_name 函数部分列出的任一函数。
decimal_literal 以十进制表示法表示的浮点数。
hexadecimal_literal 以字符串“0x”后接十六进制数字符串表示的数字。
string_literal 由零个或多个 Unicode 字符序列或转义符序列表示的 Unicode 字符串。 字符串文本括在单引号或双引号中。 允许的转义符:\'\"\\\uXXXX(由 4 个十六进制数字定义的 Unicode 字符)。

运算符

支持以下运算符:

系列 运算符
算术 +、-、*、/、%
逻辑 AND、OR、NOT
比较 =、!=、<、>、<=、>=、<>

函数

查询克隆和作业时唯一受支持的函数是:

函数 说明
IS_DEFINED(property) 返回一个布尔值,指示是否已向属性分配值(包括 null)。

在路由情况下,支持以下数学函数:

函数 说明
ABS(x) 返回指定数值表达式的绝对(正)值。
EXP(x) 返回指定数值表达式 (e^x) 的指数值。
POWER(x,y) 返回指定表达式相对指定幂 (x^y) 的值。
SQUARE(x) 返回指定数字值的平方。
CEILING(x) 返回大于或等于指定数值表达式的最小整数值。
FLOOR(x) 返回小于或等于指定数值表达式的最大整数。
SIGN(x) 返回指定数值表达式的正数 (+1)、零 (0) 或负数 (-1)。
SQRT(x) 返回指定数值的平方根。

在路由情况下,支持以下检查和强制转换类型的函数:

函数 说明
AS_NUMBER 将输入字符串转换为数字。 如果输入的是数字,则为 noop;如果字符串不表示数字,则为 Undefined
IS_ARRAY 返回一个布尔值,指示指定表达式类型是否为数组。
IS_BOOL 返回一个布尔值,指示指定表达式的类型是否为布尔表达式。
IS_DEFINED 返回一个布尔,它指示属性是否已经分配了值。 仅当该值是基元类型时才支持此函数。 基元类型包括字符串、布尔值、数字或 null。 不支持日期/时间、对象类型和数组。
IS_NULL 返回一个布尔值,指示指定表达式的类型是否为 null。
IS_NUMBER 返回一个布尔值,指示指定表达式的类型是否为数字。
IS_OBJECT 返回一个布尔值,指示指定表达式的类型是否为 JSON 对象。
IS_PRIMITIVE 返回一个布尔值,指示指定表达式的类型是否为基元(字符串、布尔值、数字或 null)。
IS_STRING 返回一个布尔值,指示指定表达式的类型是否为字符串。

在路由情况下,支持以下字符串函数:

函数 说明
CONCAT(x, y, …) 返回一个字符串,该字符串是连接两个或多个字符串值的结果。
LENGTH(x) 返回指定字符串表达式的字符数。
LOWER(x) 返回在将大写字符数据转换为小写后的字符串表达式。
UPPER(x) 返回在将小写字符数据转换为大写后的字符串表达式。
SUBSTRING(string, start [, length]) 返回字符串表达式的部分内容,该内容起于指定字符从零开始的位置,继续到指定长度或字符串结尾。
INDEX_OF(string, fragment) 返回第一个指定的字符串表达式中第一次出现第二个字符串表达式的起始位置,如果未找到字符串,则返回 -1。
STARTSWITH(x, y) 返回一个布尔值,指示第一个字符串表达式是否以第二个字符串表达式开头。
ENDSWITH(x, y) 返回一个布尔值,指示第一个字符串表达式是否以第二个字符串表达式结尾。
CONTAINS(x,y) 返回一个布尔值,该值指示第一个字符串表达式是否包含第二个字符串表达式。

使用服务 SDK 查询示例

C# 示例

查询功能由 C# 服务 SDK 在 RegistryManager 类中公开。

以下是一个简单的查询示例:

var query = registryManager.CreateQuery("SELECT * FROM devices", 100);
while (query.HasMoreResults)
{
    var page = await query.GetNextAsTwinAsync();
    foreach (var twin in page)
    {
        // do work on twin object
    }
}

查询对象使用 查询结果分页 部分中提到的参数进行实例化。 通过多次调用 GetNextAsTwinAsync 方法来检索多个页面。

Node.js 示例

查询功能由适用于 Node.js 的 Azure IoT 服务 SDK 在 Registry 对象中公开。

以下是一个简单的查询示例:

var query = registry.createQuery('SELECT * FROM devices', 100);
var onResults = function(err, results) {
    if (err) {
        console.error('Failed to fetch the results: ' + err.message);
    } else {
        // Do something with the results
        results.forEach(function(twin) {
            console.log(twin.deviceId);
        });

        if (query.hasMoreResults) {
            query.nextAsTwin(onResults);
        }
    }
};
query.nextAsTwin(onResults);

查询对象使用 查询结果分页 部分中提到的参数进行实例化。 通过多次调用 nextAsTwin 方法来检索多个页面。

后续步骤