Azure Stream Analytics は、JavaScript で記述されたユーザー定義関数をサポートします。 JavaScriptが提供する豊富な文字列、RegExp、数学、配列、日付メソッドを活用することで、Stream Analyticsのジョブで複雑なデータ変換を作成できます。 JavaScript ユーザー定義関数は、外部との接続を必要としない、ステートレスの計算のみのスカラー関数をサポートします。 関数の戻り値には、スカラー (単一) 値のみを指定できます。 ジョブに JavaScript ユーザー定義関数を追加した後は、組み込みのスカラー関数と同様に、クエリ内の任意の場所で関数を使用できます。
この記事では、JavaScriptのユーザー定義関数を使うタイミングと、Stream Analyticsのジョブでそれらを定義・呼び出す方法について説明します。
JavaScriptユーザー定義関数の使用時期
ここでは、JavaScript ユーザー定義関数が役立つ可能性があるシナリオをいくつか示します。
- 文字列の解析および操作は、 Regexp_Replace()やRegexp_Extract ()などの正則表現関数を用いて行われます 。
- データのデコードとエンコード (例: バイナリから 16 進数への変換)
- JavaScript Math 関数を用いた数学計算の実施
- 並べ替え、結合、検索、値の設定などの配列操作の実行
Stream AnalyticsでJavaScriptのユーザー定義関数を使うことでできないいくつかのことは以下の通りです。
- 例えば、外部のRESTエンドポイントを呼び出し、リバースIPルックアップを行ったり、外部ソースから参照データを取得したりします
- 入力または出力に対してカスタムイベントフォーマットのシリアライズまたはデシリアライズを実行します
- カスタム集計の作成
Date.GetDate()やMath.random()のような関数は関数定義でブロックされませんが、使うのは避けてください。 これらの関数では呼び出すたびに同じ結果が返らず、Azure Stream Analytics サービスは関数呼び出しや戻り値のジャーナルを保持しません。 同じイベントで関数が異なる結果を返す場合、あなたやStream Analyticsサービスがジョブを再起動しても再現性が保証されません。
AzureポータルでJavaScriptユーザー定義関数を定義する
クラウド上で動作するStream Analyticsジョブの場合は、ジョブトポロジーの「関数」ページからJavaScriptのユーザー定義関数を追加してください。+Add メニューにJavaScriptのUDFオプションが含まれています。
注
この経験はクラウド上で実行されるように設定されたStream Analyticsジョブにも当てはまります。 お使いの Stream Analytics ジョブが Azure IoT Edge 上で実行されるように構成されている場合は、代わりに Visual Studio を使用し、C# を使ってユーザー定義関数を記述します。
関数の定義は以下の性質から成り立つ。
| プロパティ | 説明 |
|---|---|
| 関数のエイリアス | クエリの関数を呼び出しる名前です。 |
| 出力の種類 | JavaScriptのユーザー定義関数がStream Analyticsのクエリに返す型です。 |
| 関数の定義 | UDFがクエリから呼び出されるたびに実行されるJavaScript関数の実装です。 |
JavaScript UDFロジックのテストとトラブルシューティング
Stream Analyticsポータルはこれらのユーザー定義関数のロジックのデバッグやテストをサポートしていないため、どのブラウザでもJavaScriptのUDFロジックをテスト・デバッグできます。 関数が期待通りに動作すれば、Stream Analyticsジョブに追加し、クエリから直接呼び出しる準備ができています。 また、Visual StudioのStream Analyticsツールを使ってJavaScript UDFでクエリロジックをテストすることもできます。
Stream AnalyticsはJavaScriptのランタイムエラーを致命的なものとみなし、アクティビティログを通じて表示します。 ログはあなたの仕事のアクティビティログページからAzureポータルで入手可能です。
クエリでの JavaScript ユーザー定義関数の呼び出し
クエリでJavaScript関数を呼び出すには、 udfで付けた関数のaliasを使いましょう。 以下の例は、Stream Analyticsクエリで16進数値を整数に変換するJavaScript UDFを示しています。
SELECT
time,
UDF.hex2Int(offset) AS IntOffset
INTO
output
FROM
InputStream
サポートされている JavaScript オブジェクト
Azure Stream AnalyticsのJavaScriptユーザー定義関数は、標準の組み込みJavaScriptオブジェクトをサポートします。 これらのオブジェクトは、追加の設定なしで関数に共通の文字列、数学、配列、日付操作へのアクセスを可能にします。 利用可能なオブジェクトの完全なリストについては、 グローバルオブジェクトを参照してください。 Stream Analyticsのクエリ言語とJavaScriptは同じ型システムを共有しないため、Stream Analyticsは両者間で値を変換します。
Stream Analytics と JavaScript の型変換
Stream Analyticsクエリ言語とJavaScriptは異なるタイプをサポートしています。 次の表は、2 つの型の変換マッピングの一覧です。
| Stream Analytics | JavaScript |
|---|---|
| bigint | Number (JavaScript では最大 2^53 の精度の整数しか表現できません) |
| 日時 | Date (JavaScript ではミリ秒のみサポートされています) |
| double | 番号 |
| nvarchar(MAX) | 文字列 |
| レコード | オブジェクト |
| Array | Array |
| NULL | Null |
JavaScript から Stream Analytics への変換を以下に示します。
| JavaScript | Stream Analytics |
|---|---|
| 番号 | Bigint(値が整数で、かつ long.MinValue 以上 long.MaxValue 以下の場合。それ以外は double) |
| 日付 | 日時 |
| 文字列 | nvarchar(MAX) |
| オブジェクト | レコード |
| Array | Array |
| ヌル、未定義 | NULL |
| 他のすべての種類 (関数やエラーなど) | サポート対象外 (ランタイム エラーが発生します) |
JavaScriptでは大文字と小文字が区別されるため、JavaScriptコード内のオブジェクトのフィールド名の大文字・小文字は、受信データ内のフィールド名の大文字・小文字と一致している必要があります。 互換性レベル1.0のジョブは、SQL SELECT文から小文字にフィールドを変換します。 互換性レベル1.1以上では、SELECT文のフィールドはSQLクエリで指定されたケースと同じものになります。
一般的な機能パターン
以下のパターンは、JavaScriptのユーザー定義関数を使ってStream Analyticsクエリのデータ変換に使う一般的な方法を示しています。 各パターンには関数定義とそれを呼び出すサンプルクエリが含まれています。
入れ子になった JSON を記述して出力する
Stream Analytics ジョブ出力を入力として使用し、その入力が JSON フォーマットを必要とするフォローアップ処理手順の場合は、JSON 文字列を記述して出力することができます。 次の関数定義は JSON.stringify() 関数を呼び出し、入力のすべての名前/値ペアをパックし、それを出力時に単一の文字列値として書きます。
function main(x) {
return JSON.stringify(x);
}
Stream Analyticsのクエリは、以下の例のようにこの関数を呼び出します。
SELECT
DataString,
DataValue,
HexValue,
UDF.jsonstringify(input) As InputEvent
INTO
output
FROM
input PARTITION BY PARTITIONID
文字列を処理できるように JSON オブジェクトにキャストする
もしJSONの文字列フィールドがあり、それをJavaScript UDFで処理するためのJSONオブジェクトに変換したい場合は、 JSON.parse() 関数を使ってJSONオブジェクトを作成し、それを利用できます。 次の関数定義は文字列を解析し、その結果のオブジェクトからプロパティを返します。
function main(x) {
var person = JSON.parse(x);
return person.name;
}
Stream Analyticsのクエリは、以下の例のようにこの関数を呼び出します。
SELECT
UDF.getName(input) AS Name
INTO
output
FROM
input
エラー処理に try/catch を使用する
Try/catchブロックは、JavaScript UDFに入力する入力データの歪んだ問題を特定するのに役立ちます。 以下の関数定義では、解析エラーを処理するためにtry/catchブロックを使用します。
function main(input, x) {
var obj = null;
try{
obj = JSON.parse(x);
}catch(error){
throw input;
}
return obj.Value;
}
次のサンプルクエリでは、エラーがあった場合に関数が返せるように、最初のパラメータとしてレコード全体を渡します。
SELECT
A.context.company AS Company,
udf.getValue(A, A.context.value) as Value
INTO
output
FROM
input A
toLocaleString()
JavaScriptの toLocaleString メソッドは、メソッドを呼び出した日付時のデータを表す言語に敏感な文字列を返します。 Azure Stream AnalyticsはシステムのタイムスタンプとしてUTCの日付時刻のみを受け入れますが、この方法でシステムのタイムスタンプを別の場所やタイムゾーンに変換できます。 この方法はInternet Explorerで利用可能なものと同じ実装動作に従っています。 以下の関数定義は、入力のdatetimeを de-DE ロケートに変換します。
function main(datetime){
const options = { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' };
return datetime.toLocaleDateString('de-DE', options);
}
以下のサンプルクエリでは、入力値としてdatetimeが渡されます。
SELECT
udf.toLocaleString(input.datetime) as localeString
INTO
output
FROM
input
このクエリの出力結果は、指定されたオプションを使用した de-DE 形式の入力日時です。
Samstag, 28. December 2019
ユーザーロギング
ログは、Azure Stream AnalyticsがJavaScriptのユーザー定義関数からカスタム情報を取得するためにジョブが実行されている間に使用する仕組みです。 実行中のジョブは不透明であるため、ログデータはカスタムコードの挙動や正確さをリアルタイムで確認できます。 すべてのログメッセージには、メッセージの重要度やジョブが実行を続けられるかどうかを示すイベントレベルが付与されます。
情報メッセージは console.info() メソッドからもらえます。例えば console.info('my info message');。 このレベルは実行中に一般的な情報を記録し、計算を中断しません。 警告メッセージは console.warn() メソッド(例えば console.warn('my warning message');)から送信されます。 このレベルでは、予期しない可能性はあるものの計算上は許容されるデータが記録されるため、ジョブの実行は継続されます。 エラーメッセージは、console.error('my error message');のようなconsole.error()およびconsole.log()メソッドから発生します。 これらの方法はコードが継続できない場合にのみ適用され、提供されたエラー情報を含む例外を投げてジョブを停止します。
ログ メッセージにアクセスするには、診断ログを使用します。
atob() と btoa()
Stream Analyticsは、バイナリデータをテキストとしてエンコードする一般的な方法であるBase64変換の2つの方法をサポートしています。
btoa()メソッドはASCII文字列をBase64に符号化し、atob()メソッドはBase64で符号化されたデータの文字列をASCII文字列に復号します。 次の例では、 btoa() ASCII文字列を符号化し、 atob() 結果を元の文字列に復号します。
var myAsciiString = 'ascii string';
var encodedString = btoa(myAsciiString);
var decodedString = atob(encodedString);