コンテンツにスキップ

SCRIPT()

TQLは、SRCMAP のコンテキストでJavaScriptを使用できる SCRIPT() 関数を提供します Since v8.0.36
使い慣れたプログラミング言語でロジックを記述し、より柔軟で強力なスクリプトを作成できます。

構文: SCRIPT({main_code})

構文: SCRIPT({init_code}, {main_code})

構文: SCRIPT({init_code}, {main_code}, {deinit_code})

  • init_code:初期化コード(省略可。ただし deinit_code がある場合は必須)
  • main_code:必須の実行コード
  • deinit_code:終了時に実行するコード(省略可)

init_code は最初に1回だけ実行されます。main_code は必須です。

注意事項

現在の SCRIPT() はGojaベースのJavaScript実行環境を使用します。 Typed Array、テンプレートリテラル(バッククォート文字列)、const、アロー関数を使用できます。 "use strict" による厳格モードも有効です。 正規表現の先読み((?=...)(?!...))と後方参照(\1 など)も使用できます。 対応する言語機能は、利用するMachbase Neoに組み込まれた実行環境のバージョンに従います。

JSHモジュール

Since v8.0.52

require() を使って、SCRIPT() 内にJSHモジュールをインポートできます。

SCRIPT({
    const { arrange } = require("mathx")
    arrange(0, 6, 3).forEach((i) =>$.yield(i))
})
CSV()

コンテキストオブジェクト

Machbase Neoは、コンテキストオブジェクトとして $ 変数を提供します。JavaScriptは、このオブジェクトを通じてレコードやデータベースにアクセスし、新しいレコードを出力できます。

  • $.payload:リクエスト本文のデータ。SCRIPT() がSRCノードの場合にのみ存在し、それ以外は undefined です。
  • $.params:リクエストのクエリパラメーター。
  • $.resultSCRIPT() 関数が出力する結果のカラム名と型を指定します。
  • $.key$.values:現在のレコードのキーと値にアクセスします。MAPコンテキストでのみ使用できます。
  • $.yield():値だけを渡して新しいレコードを出力します。
  • $.yieldKey():キーと値を渡して新しいレコードを出力します。
  • $.yieldArray():配列1つを引数として受け取り、レコードを出力します。
  • $.db():新しいデータベース接続を返します。
  • $.db().query():SQLクエリを実行します。
  • $.db().exec():SELECT以外のSQLを実行します。
  • $.request().do():リモートサーバーにHTTPリクエストを送信します。

$.payload

$.payload からリクエスト本文のデータを読み取れます。入力データがない場合は undefined です。
SRCノードとして使用する場合にのみアクセスできます。

1
2
3
4
5
6
7
8
9
SCRIPT({
    var data = $.payload;
    if (data === undefined) {
        data = '{ "prefix": "name", "offset": 0, "limit": 10}';
    }
    var obj = JSON.parse(data);
    $.yield(obj.prefix, obj.offset, obj.limit);
})
CSV()

リクエスト本文なしで呼び出すと、$.payloadundefined になります。

curl -o - -X POST http://127.0.0.1:5654/db/tql/test.tql

結果は既定値の name,0,10 です。

リクエスト本文を渡すと、次のように動作します。

curl -o - -X POST http://127.0.0.1:5654/db/tql/test.tql \
-d '{"prefix":"testing", "offset":10, "limit":10}'

結果:testing,10,10

$.params

$.params は、リクエストのクエリパラメーターを提供します。
ドット表記($.params.name)と角括弧表記($.params["name"])の両方を使用できます。

1
2
3
4
5
6
7
SCRIPT({
    var prefix = $.params.prefix ? $.params.prefix : "name";
    var offset = $.params.offset ? $.params.offset : 0;
    var limit = $.params.limit ? $.params.limit: 10;
    $.yield(prefix, offset, limit);
})
CSV()

パラメーターなしで呼び出す場合:

curl -o - -X POST http://127.0.0.1:5654/db/tql/test.tql

結果:name,0,10

パラメーターを渡す場合:

curl -o - -X POST "http://127.0.0.1:5654/db/tql/test.tql?prefix=testing&offset=12&limit=20"

結果:testing,12,20

$.result

SCRIPT が出力する結果のカラムと型を定義します。
次の例のように、初期化コード内で設定します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
SCRIPT({
    $.result = {
        columns: ["val", "sig"],
        types: ["double", "double"] 
    }
},{
    for (i = 1.0; i <= 5.0; i+=0.03) {
        val = Math.round(i*100)/100;
        sig = Math.sin( 1.2*2*Math.PI*val );
        $.yield( val, sig );
    }
})
JSON()

$.key

現在のレコードのキーにアクセスします。MAPコンテキストでのみ定義され、SRCでは undefined です。

1
2
3
4
5
6
7
8
9
SCRIPT({
    for( i = 0; i < 3; i++) {
        $.yieldKey(i, "hello-"+(i+1));
    }
})
SCRIPT({
    $.yieldKey($.key, $.values[0], 'key is '+$.key);
})
CSV()
hello-1,key is 0
hello-2,key is 1
hello-3,key is 2

$.values

現在のレコードの値配列にアクセスします。MAPコンテキストでのみ使用でき、SRCでは undefined です。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
SCRIPT({
        $.yield("string", 10, 3.14);
})
SCRIPT({
    $.yield(
        "the first value is "+$.values[0],
        "2nd value is "+$.values[1],
        "3rd is "+$.values[2]
    );
})
CSV()

出力:

the first value is string,2nd value is 10,3rd is 3.14

$.yield()

新しいレコードを次のステップに出力します。キーには連番が自動的に割り当てられます。

$.yield(field1, field2, field3);

$.yieldKey()

yieldKey()$.yield() と同様に動作しますが、最初の引数でレコードのキーを指定します。

$.yieldKey(key, field1, field2, field3);

$.yieldArray()

Since v8.0.39

配列に格納されたレコードを出力します。 可変長引数を受け取る $.yield() と異なり、$.yieldArray() はレコードを表す配列1つを受け取ります。 配列を扱う場合に便利です。

var arr = [];
for( i = 0; i < unknown; i++) {
    arr.push(field_values[i]);
}
$.yieldArray(arr);

$.db()

新しいデータベース接続を返します。接続は query()exec() 関数を提供します。

$.db({bridge: "sqlite"}) のように option オブジェクトを引数に指定すると、 Machbaseデータベースの代わりに、ブリッジ接続したデータベースへの新しい接続を返します。

オプション

optionパラメーターは次のバージョンからサポートされます Since v8.0.37

{
    bridge: "name", // ブリッジ名
}

$.db().query()

JavaScriptから $.db().query() でデータベースを検索できます。 query() の戻り値に forEach() でコールバック関数を適用し、クエリ結果を反復処理します。

.forEach() のコールバック関数が明示的に false を返すと、反復処理は直ちに終了します。 true を返すか、何も返さない場合(undefined を返す場合)は、 クエリ結果の末尾まで反復処理を続けます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
SCRIPT({
  var data = $.payload;
  if (data === undefined) {
    data = '{ "tag": "cpu.percent", "offset": 0, "limit": 3 }';
  }
  var obj = JSON.parse(data);
  $.db()
   .query("SELECT name, time, value FROM example WHERE name = ? LIMIT ?, ?",
    obj.tag, obj.offset, obj.limit
  ).forEach( function(row){
    name = row[0]
    time = row[1]
    value = row[2]
    $.yield(name, time, value);
  })
})
CSV()
cpu.percent,1725330085908925000,73.9
cpu.percent,1725343895315420000,73.6
cpu.percent,1725343898315887000,6.1

$.db().query() の結果から特定のカラムを選び、$.yield() で出力できます。 すべてのカラムをまとめて出力するには、$.yieldArray() を使用します Since v8.0.39

1
2
3
4
5
6
7
SCRIPT({
    var sql = "SELECT name, time, value FROM example WHERE name = 'cpu.percent' LIMIT 3";
    $.db().query(sql).forEach( function(row){
        $.yieldArray(row);
    });
})
CSV()

または、$.db().query().yield() を使って自動的に出力できます Since v8.0.39

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
SCRIPT({
    var tags = ["mem.total", "mem.used", "mem.free"];
    for( i = 0; i < tags.length; i++) {
        $.yield(tags[i]);
    }
})
SCRIPT({
    var sql = "SELECT * FROM example WHERE name = ? LIMIT 1";
    $.db().query(sql, $.values[0]).yield();
})
CSV( header(true) )

$.db().exec()

SQLがSELECT文でない場合は、$.db().exec() を使用してINSERT、DELETE、CREATE TABLE文を実行します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
SCRIPT({
    for( i = 0; i < 3; i++) {
        ts = Date.now()*1000000; // ミリ秒をナノ秒に換算
        $.yield("testing", ts, Math.random()*100);
    }
})
SCRIPT({
    // このセクションは初期化コードです。
    // 最初のレコードを処理する前に1回だけ実行します。
    err = $.db().exec("CREATE TAG TABLE IF NOT EXISTS example ("+
        "NAME varchar(80) primary key,"+
        "TIME datetime basetime,"+
        "VALUE double"+
    ")");
    if (err instanceof Error) {
        console.error("Fail to create table", err.message);
    }
}, {
    // このセクションはメインコードです。
    // 各レコードに対して実行します。
    err = $.db().exec("INSERT INTO example values(?, ?, ?)", 
        $.values[0], $.values[1], $.values[2]);
    if (err instanceof Error) {
        console.error("Fail to insert", err.message);
    } else {
        $.yield($.values[0], $.values[1], $.values[2]);
    }
})
CSV()

$.request().do()

構文: $.request(url [, option]).do(callback)

リクエストオプション

{
    method: "GET|POST|PUT|DELETE", // 既定値は "GET"
    headers: { "Authorization": "Bearer auth-token"}, // キーと値のマップ
    body: "body content if the method is POST or PUT"
}

レスポンスを処理するコールバック関数を指定して .do() を呼び出すと、実際のリクエストを送信します。コールバック関数は、各種プロパティとメソッドを持つResponseオブジェクトを引数として受け取ります。

レスポンス

プロパティ説明
.okBooleanレスポンスのステータスコードが成功の場合に true200<= status < 300
.statusNumberHTTPレスポンスコード
.statusTextStringステータスコードとメッセージ(例:200 OK
.urlStringリクエストURL
.headersMapレスポンスヘッダー

Responseオブジェクトには、レスポンス本文を取得するメソッドがあります。

メソッド説明
.text(callback(txt))本文を文字列としてコールバックに渡す
.blob(callback(bin))本文をバイナリ配列としてコールバックに渡す
.csv(callback(row))本文をCSVとして解析し、各行(レコード)に対して callback() を呼び出す

使用方法

$.request("https://server/path", {
    method: "GET",
    headers: { "Authorization": "Bearer auth-token" }
  }).do( function(rsp){
    console.log("ok:", rsp.ok);
    console.log("status:", rsp.status);
    console.log("statusText:", rsp.statusText);
    console.log("url:", rsp.url);
    console.log("Content-Type:", rsp.headers["Content-Type"]);
});

finalize()

SCRIPT() 内のJavaScriptコードで function finalize() {} を定義すると、 すべてのレコードを処理した後に、システムがこの関数を自動的に呼び出します。

次の2つのコード例は同じ動作をし、どちらも最後のレコードとして 999 を出力します。

FAKE( arrange(1, 3, 1) )
SCRIPT({
    function finalize() {
        $.yield(999);
    }
    $.yield($.values[0]);
})
CSV()
FAKE( arrange(1, 3, 1) )
SCRIPT({
    // 初期化処理なし
},{
    // メイン処理
    $.yield($.values[0]);
}, {
    // 終了処理;
    $.yield(999);
})
CSV()

この例は、123999 の4レコードを出力します。

Hello World

SCRIPT({
    console.log("Hello World?");
})
DISCARD()

結果

組み込みMathオブジェクト

JavaScriptの組み込み関数を使用できます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
FAKE(meshgrid(linspace(0,2*3.1415,30), linspace(0, 3.1415, 20)))

SCRIPT({
  x = Math.cos($.values[0]) * Math.sin($.values[1]);
  y = Math.sin($.values[0]) * Math.sin($.values[1]);
  z = Math.cos($.values[1]);
  $.yield([x,y,z]);
})

CHART(
  plugins("gl"),
  size("600px", "600px"),
  chartOption({
    grid3D:{}, xAxis3D:{}, yAxis3D:{}, zAxis3D:{},
    visualMap:[{  min:-1, max:1, 
      inRange:{color:["#313695",  "#74add1", "#ffffbf","#f46d43", "#a50026"]
    }}],
    series:[ { type:"scatter3D", data: column(0)} ]
  })
)

結果

JSONの解析

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
SCRIPT({
    $.result = {
        columns: ["NAME", "AGE", "IS_MEMBER", "HOBBY"],
        types: ["string", "int32", "bool", "string"],
    }
},{
    content = $.payload;
    if (content === undefined) {
        content = '{"name":"James", "age": 24, "isMember": true, "hobby": ["book", "game"]}';
    }
    obj = JSON.parse(content);
    $.yield(obj.name, obj.age, obj.isMember, obj.hobby.join(","));
})
JSON()

結果

{
    "data": {
        "columns": [ "NAME", "AGE", "IS_MEMBER", "HOBBY" ],
        "types": [ "string", "int32", "bool", "string" ],
        "rows": [ [ "James", 24, true, "book,game" ] ]
    },
    "success": true,
    "reason": "success",
    "elapse": "627.958µs"
}

CSVの取得

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
SCRIPT({
    $.result = {
        columns: ["SepalLen", "SepalWidth", "PetalLen", "PetalWidth", "Species"],
        types: ["double", "double", "double", "double", "string"]
    };
},{
    $.request("https://docs.machbase.com/assets/example/iris.csv")
     .do(function(rsp){
        console.log("ok:", rsp.ok);
        console.log("status:", rsp.status);
        console.log("statusText:", rsp.statusText);
        console.log("url:", rsp.url);
        console.log("Content-Type:", rsp.headers["Content-Type"]);
        if ( rsp.error() !== undefined) {
            console.error(rsp.error())
        }
        var err = rsp.csv(function(fields){
            $.yield(fields[0], fields[1], fields[2], fields[3], fields[4]);
        })
        if (err !== undefined) {
            console.warn(err);
        }
    })
})
CSV(header(true))

JSONテキストの取得

この例は、リモートサーバーからJSONを取得し、JavaScriptで解析する方法を示します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
SCRIPT({
    $.result = {
        columns: ["ID", "USER_ID", "TITLE", "COMPLETED"],
        types: ["int64", "int64", "string", "boolean"]
    };
},{
    $.request("https://jsonplaceholder.typicode.com/todos")
     .do(function(rsp){
        console.log("ok:", rsp.ok);
        console.log("status:", rsp.status);
        console.log("statusText:", rsp.statusText);
        console.log("URL:", rsp.url);
        console.log("Content-Type:", rsp.headers["Content-Type"]);
        if ( rsp.error() !== undefined) {
            console.error(rsp.error())
        }
        rsp.text( function(txt){
            list = JSON.parse(txt);
            for (i = 0; i < list.length; i++) {
                obj = list[i];
                $.yield(obj.id, obj.userId, obj.title, obj.completed);
            }
        })
    })
})
CSV(header(false))
最終更新日