IoT Hubのクエリ言語 - デバイスツイン、モジュールツイン、ジョブ、メッセージルーティング

IoT Hub には SQL に似た強力な言語が備わっており、デバイス ツインモジュール ツインジョブ、およびメッセージ ルーティングに関する情報を取得できます。 この記事で取り扱う内容は次のとおりです。

具体的な例については、IoT Hub デバイス ツインとモジュール ツインのクエリ または IoT Hub ジョブのクエリ を参照してください。

注意

この記事で言及されている一部の機能 (cloud-to-device メッセージング、デバイス ツイン、デバイス管理など) は、IoT Hub の Standard レベルだけで使用することができます。 Basic および Standard/Free IoT Hub レベルの詳細については、「 ソリューションに適した IoT Hub レベルとサイズを選択する」を参照してください。

IoT Hub のクエリを実行する

Azure portal で直接 IoT ハブに対してクエリを実行できます。

  1. Azure Portal にサインインし、IoT Hub に移動します。
  2. ナビゲーション メニューの [デバイス管理] セクションから [クエリ] を選びます。
  3. テキスト ボックスにクエリを入力して、[クエリの実行] を選びます。

また、Azure IoT サービス SDK とサービス API を使って、アプリケーション内でクエリを実行することもできます。

IoT Hub のクエリを実装するコードの例については、「サービス SDK を使用するクエリの例」セクションをご覧ください。

SDK リファレンス ページとサンプルへのリンクについては、「Azure IoT Hub SDK」を参照してください。

IoT Hub クエリの基礎

IoT Hub のすべてのクエリは、SELECT 句と FROM 句、およびオプションの WHERE 句と GROUP BY で構成されます。

クエリは JSON ドキュメントのコレクション (デバイス ツインなど) で実行されます。 FROM 句は、反復対象のドキュメント コレクション (devicesdevices.modules、または devices.jobs) を示します。

次に、WHERE 句でフィルター処理が適用されます。 集計により、この手順の結果は GROUP BY 句の指定に従ってグループ化されます。 グループごとに、SELECT 句で指定された行が生成されます。

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

SELECT 句

SELECT <select_list> 句は、すべてのIoT Hubクエリで必要です。 クエリで取得する値を指定します。 これは、新しい 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 句

FROM <from_specification>句は、すべての ioT Hub クエリで必要です。 3 つの値のいずれかである必要があります。

  • デバイス ツインにクエリを実行するためのデバイス
  • devices.modules を使用してモジュール ツインをクエリする
  • デバイスごとのジョブの詳細のクエリを実行するための devices.jobs

次に例を示します。

  • すべてのデバイス ツインを取得する

    SELECT * FROM devices
    

WHERE 句

WHERE <filter_condition>句は省略可能です。 FROM コレクションの JSON ドキュメントが結果に含まれるために満たすべき1つ以上の条件を指定します。 すべての 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 を使用する場合は、次の例に示すように、エラーを回避するために二重角かっこで囲む 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 を複数回呼び出します。 クエリ オブジェクトは、クエリに必要な逆シリアル化オプションに応じて、複数の Next 値を公開します。 たとえば、クエリオブジェクトはデバイス ツインまたはジョブ オブジェクトを返すことができ、プロジェクションを使用する場合はプレーンな JSON を返すことができます。

式と条件

とは高いレベルで次のように言い表すことができます。

  • JSON 型 (ブール値、数値、文字列、配列、オブジェクトなど) のインスタンスに対して評価を行う
  • 組み込みの演算子と関数を使用するデバイス JSON ドキュメントと定数からくるデータの操作として定義される

条件はブール値に対して評価する式のことです。 ブール値の true 以外の定数は、false と見なされます。 このルールには、null未定義、任意のオブジェクトまたは配列インスタンス、任意の文字列、およびブール値の 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 ドキュメントのプロパティ。
二項演算子 演算子」セクションに記載されている 2 項演算子。
function_name 関数」セクションに記載されている関数。
10進数リテラル 浮動小数点数は10進表記で表現されます。
16進法リテラル 文字列 '0x' の後に 16 進数の文字列を続けて表された数値。
string_literal Unicode 文字列は、0個以上の Unicode 文字かエスケープ シーケンスによって表されます。 文字列リテラルは、単一引用符または二重引用符で囲みます。 使用できるエスケープ: 4 つの 16 進数字によって定義された Unicode 文字の \'\"\\\uXXXX

オペレーター

次の演算子がサポートされています。

ファミリ オペレーター
算術 +、-、*、/、%
論理 および、または、ではない
比較 =、!=、<、>、<=、>=、<>

Functions

ツインとジョブのクエリにおいて、サポートされている唯一の関数は次のとおりです。

機能 説明
IS_DEFINED(property) プロパティに値が割り当てられているかどうかを示すブール値 ( nullを含む) を返します。

ルートの条件では、次の計算関数がサポートされています。

機能 説明
ABS(x) 指定された数値式の絶対値 (正の値) を返します。
EXP(x) 指定された数値式の指数値を返します (e^x)。
POWER(x,y) 指定された式を指定されたべき乗に計算した値を返します (x^y)。
SQUARE(x) 指定された数値の 2 乗を返します。
CEILING(x) 指定された数値式以上の最小の整数値を返します。
FLOOR(x) 指定された数値式以下の最大の整数を返します。
SIGN(x) 指定された数値式の正 (+1)、ゼロ (0)、または負 (-1) の符号を返します。
SQRT(x) 指定された数値の平方根を返します。

ルート条件設定では、次の型チェックとキャスト関数がサポートされています。

機能 説明
AS番号 入力文字列を数値に変換します。 入力が数値の場合は noop、文字列が数値を表していない場合は Undefined
IS_ARRAY 指定した式の型が配列であるかどうかを示すブール値を返します。
IS_BOOL 指定した式の型がブール値であるかどうかを示すブール値を返します。
IS_DEFINED プロパティが値であるかどうかを示すブール値を返します。 この関数は、値がプリミティブ型である場合にのみサポートされます。 プリミティブ型には、文字列、ブール値、数値、または null が含まれます。 DateTime、オブジェクト型、配列はサポートされていません。
IS_NULL 指定した式の型が null であるかどうかを示すブール値を返します。
IS_NUMBER(数値かどうかを判定する関数) 指定した式の型が数値であるかどうかを示すブール値を返します。
オブジェクトかどうか 指定した式の型が JSON オブジェクトであるかどうかを示すブール値を返します。
IS_PRIMITIVE 指定した式の型がプリミティブ (文字列、ブール値、数値、または null) であるかどうかを示すブール値を返します。
IS_STRING 指定した式の型が文字列であるかどうかを示すブール値を返します。

ルートの条件では、次の文字列関数がサポートされています。

機能 説明
CONCAT(x, y, ...) 2 つ以上の文字列値を連結した結果である文字列を返します。
LENGTH(x) 指定された文字列式の文字数を返します。
LOWER(x) 文字列式の大文字データを小文字に変換して返します。
UPPER(x) 文字列式の小文字データを大文字に変換して返します。
SUBSTRING(string, start [, length]) 指定された文字のゼロベースの位置で始まる文字列式の一部を返し、指定された長さまたは文字列の末尾まで続きます。
INDEX_OF(string, fragment) 1 つ目に指定された文字列式内で、2 つ目の文字列式が最初に出現する箇所の開始位置を返します。文字列が見つからない場合は -1 を返します。
STARTS_WITH(x, y) 1 つ目の文字列式が 2 つ目の文字列で始まっているかどうかを示すブール値を返します。
ENDS_WITH(x, y) 1 つ目の文字列式が 2 つ目の文字列で終了しているかどうかを示すブール値を返します。
CONTAINS(x,y) 1 つ目の文字列式に 2 つ目の文字列式が含まれているかどうかを示すブール値を返します。

サービス SDK を使用したクエリの例

C# の例

クエリ機能は、 クラスの 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 の使用例

クエリ機能は、 オブジェクトの 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 メソッドを複数回呼び出すことによって、複数のページが取得されます。

IoT Hub デバイス ツインとモジュール ツインのクエリ

デバイス ツインモジュール ツインには、 タグとプロパティの両方として任意の JSON オブジェクトを含めることができます。 IoT Hubを使用すると、デバイス ツインとモジュール ツインに対して、すべてのツイン情報を含む単一の JSON ドキュメントとしてクエリを実行できます。

IoT Hub デバイス ツインのサンプルを次に示します (モジュール ツインは moduleId のパラメーターと同様です)。

{
    "deviceId": "myDeviceId",
    "etag": "AAAAAAAAAAc=",
    "status": "enabled",
    "statusUpdateTime": "0001-01-01T00:00:00",
    "connectionState": "Disconnected",
    "lastActivityTime": "0001-01-01T00:00:00",
    "cloudToDeviceMessageCount": 0,
    "authenticationType": "sas",
    "x509Thumbprint": {
        "primaryThumbprint": null,
        "secondaryThumbprint": null
    },
    "version": 2,
    "tags": {
        "location": {
            "region": "US",
            "plant": "Redmond43"
        }
    },
    "properties": {
        "desired": {
            "telemetryConfig": {
                "configId": "db00ebf5-eeeb-42be-86a1-458cccb69e57",
                "sendFrequencyInSecs": 300
            },
            "$metadata": {
            ...
            },
            "$version": 4
        },
        "reported": {
            "connectivity": {
                "type": "cellular"
            },
            "telemetryConfig": {
                "configId": "db00ebf5-eeeb-42be-86a1-458cccb69e57",
                "sendFrequencyInSecs": 300,
                "status": "Success"
            },
            "$metadata": {
            ...
            },
            "$version": 7
        }
    }
}

デバイス ツイン クエリ

IoT Hubは、デバイス ツインを devices というドキュメント コレクションとして公開します。 たとえば、最も基本的なクエリでは、デバイス ツインのセット全体が取得されます。

SELECT * FROM devices

注意

Azure IoT SDK では、大きな結果のページングがサポートされます。

SELECT 句を使用して、クエリの結果を集計できます。 たとえば、次のクエリでは、IoT ハブ内のデバイスの合計数を取得します。

SELECT COUNT() as totalNumberOfDevices FROM devices

WHERE 句を使用してクエリ結果をフィルター処理します。 たとえば、 location.region タグが US に設定されているデバイス ツインを受信するには、次のクエリを使用します。

SELECT * FROM devices
WHERE tags.location.region = 'US'

ブール演算子と算術比較を使用して、複雑な WHERE 句を作成します。 たとえば、次のクエリでは、米国にあるデバイス ツインを取得し、テレメトリを 1 分未満で送信するように構成します。

SELECT * FROM devices
  WHERE tags.location.region = 'US'
    AND properties.reported.telemetryConfig.sendFrequencyInSecs >= 60

配列定数は、 IN 演算子と NIN (含まれていない) 演算子と共に使用することもできます。 たとえば、次のクエリでは、WiFi または有線接続を報告するデバイス ツインを取得します。

SELECT * FROM devices
  WHERE properties.reported.connectivity IN ['wired', 'wifi']

多くの場合、特定のプロパティを含むすべてのデバイス ツインを識別する必要があります。 IoT Hubでは、この目的のために関数 is_defined() がサポートされています。 たとえば、次のクエリでは、 connectivity プロパティを定義するデバイス ツインを取得します。

SELECT * FROM devices
  WHERE is_defined(properties.reported.connectivity)

フィルター機能の完全なリファレンスについては、 WHERE 句 のセクションを参照してください。

グループ化もサポートされています。 たとえば、次のクエリは、各テレメトリ構成状態のデバイスの数を返します。

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

このグループ化クエリは、次の例のような結果を返します。

[
    {
        "numberOfDevices": 3,
        "status": "Success"
    },
    {
        "numberOfDevices": 2,
        "status": "Pending"
    },
    {
        "numberOfDevices": 1,
        "status": "Error"
    }
]

この例では、3 つのデバイスが正常な構成を報告し、2 つがまだ構成を適用しており、1 つはエラーを報告しています。

プロジェクション クエリを使用すると、開発者は関心があるプロパティのみを返すことができます。 たとえば、最後のアクティビティ時刻と、切断されているすべての有効なデバイスのデバイス ID を取得するには、次のクエリを使用します。

SELECT DeviceId, LastActivityTime FROM devices WHERE status = 'enabled' AND connectionState = 'Disconnected'

そのクエリの結果は、次の例のようになります。

[
  {
    "deviceId": "AZ3166Device",
    "lastActivityTime": "2021-05-07T00:50:38.0543092Z"
  }
]

モジュール ツイン クエリ

モジュール ツインに対するクエリは、デバイス ツインでのクエリに似ていますが、別のコレクション/名前空間を使用します。 devicesではなく、 devices.modulesからクエリを実行します。

SELECT * FROM devices.modules

devices と devices.modules コレクションの間の結合は許可されていません。 デバイス間でモジュール ツインのクエリを実行する場合は、タグに基づいて実行します。 次のクエリでは、スキャン状態のすべてのデバイスのすべてのモジュール ツインが返されます。

SELECT * FROM devices.modules WHERE properties.reported.status = 'scanning'

次のクエリは、スキャン状態を持つすべてのモジュール ツインを返しますが、指定されたデバイスのサブセットでのみ返されます。

SELECT * FROM devices.modules
  WHERE properties.reported.status = 'scanning'
  AND deviceId IN ['device1', 'device2']

ツイン クエリの制限事項

Important

クエリ結果は最終的に一貫した操作であり、最大 30 分の遅延を許容する必要があります。 ほとんどの場合、ツイン クエリは数秒の順序で結果を返します。 IoT Hub は、すべての操作の待機時間を短くするように努めています。 ただし、ネットワークの状態やその他の予測不能な要因のために、特定の待機時間を保証することはできません。

ツイン クエリの別のオプションは、 get twin REST API を使用して ID を使用して個々のデバイス ツインにクエリを実行することです。 この API は常に最新の値を返し、調整制限が高くなります。 REST API を直接発行することも、Azure IoT Hub Service SDK のいずれかで同等の機能を使用することもできます。

クエリ式の最大長は 8,192 文字です。

現在、比較はプリミティブ型 (オブジェクトなし) 間でのみサポートされています。たとえば、 ... WHERE properties.desired.config = properties.reported.config は、それらのプロパティにプリミティブ値がある場合にのみサポートされます。

どのシナリオでも、ツイン クエリのデバイス ID プロパティで見つかった lastActivityTime に依存しないことをお勧めします。 この項目では、デバイスの状態を正確に測定することは保証されません。 代わりに、IoT デバイス ライフサイクル イベントを使用して、デバイスの状態とアクティビティを管理します。 ソリューションでの IoT Hub のイベントへの対応のため、Event Grid を使用してアクションをトリガーする方法については、 IoT Hub イベントへの対応方法を参照してください。

注意

この操作の最大待機時間に関する想定は避けてください。 待機時間を考慮してソリューションを構築する方法の詳細については、「待機時間ソリューション」を参照してください。

IoT Hub ジョブのクエリ

ジョブ は、一組のデバイスに対して操作を実行する方法を提供します。 各デバイス ツインには、jobs と呼ばれるコレクションでそのデバイスを対象とするジョブの情報が含まれています。 IoT Hubを使用すると、すべてのツイン情報を含む単一の JSON ドキュメントとしてジョブにクエリを実行できます。

myJobId というジョブの一部である IoT Hub デバイス ツインのサンプルを次に示します。

{
    "deviceId": "myDeviceId",
    "etag": "AAAAAAAAAAc=",
    "tags": {
        ...
    },
    "properties": {
        ...
    },
    "jobs": [
        {
            "deviceId": "myDeviceId",
            "jobId": "myJobId",
            "jobType": "scheduleUpdateTwin",
            "status": "completed",
            "startTimeUtc": "2016-09-29T18:18:52.7418462",
            "endTimeUtc": "2016-09-29T18:20:52.7418462",
            "createdDateTimeUtc": "2016-09-29T18:18:56.7787107Z",
            "lastUpdatedDateTimeUtc": "2016-09-29T18:18:56.8894408Z",
            "outcome": {
                "deviceMethodResponse": null
            }
        },
        ...
    ]
}

現在、このコレクションは、IoT Hub クエリ言語で devices.jobs としてクエリ可能です。

Important

現時点では、デバイス ツインに対してクエリを実行する場合、jobs プロパティは返されません。 つまり、 FROM devicesを含むクエリです。 jobs プロパティには、 FROM devices.jobsを使用したクエリでのみ直接アクセスできます。

たとえば、次のクエリは、1 つのデバイスに影響を与えるすべてのジョブ (過去とスケジュール済み) を返します。

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

このクエリは、返される各ジョブのデバイス固有の状態 (および場合によってはダイレクト メソッドの応答) を提供する方法に注意してください。

また、 devices.jobs コレクション内のすべてのオブジェクト プロパティに対して任意のブール条件でフィルター処理することもできます。

たとえば、次のクエリは、特定のデバイスに対して 2016 年 9 月以降に作成されたすべての完了したデバイス ツイン更新ジョブを取得します。

SELECT * FROM devices.jobs
  WHERE devices.jobs.deviceId = 'myDeviceId'
    AND devices.jobs.jobType = 'scheduleUpdateTwin'
    AND devices.jobs.status = 'completed'
    AND devices.jobs.createdTimeUtc > '2016-09-01'

また、1 つのジョブのデバイスごとの結果を取得することもできます。

SELECT * FROM devices.jobs
  WHERE devices.jobs.jobId = 'myJobId'

ジョブ クエリの制限事項

クエリ式の最大長は 8,192 文字です。

現時点では、 devices.jobs に対するクエリでは次の機能はサポートされていません。

  • そのため、プロジェクションは SELECT * のみ可能です。
  • ジョブのプロパティに加えてデバイスツインを参照する条件が含まれています(前のセクションを参照)。
  • カウント平均グループ化による集計などの集計。