パラメーター化されたクエリの作成

単純なビルトインクエリがいかに簡単にすばやく実行できるか見てきました。ここから、クエリをさらにカスタマイズしたり、複雑なクエリを作成することについて、説明します。GSQLでは、複数の頂点を対象とする(頂点セットの)パラメーター化されたクエリによりを、ユーザーに最大のパワーを提供しています。パラメーター化されたクエリは、ある頂点のセットから近隣の頂点のセットに行き来して、それを何度も繰り返す間、システムに作り込まれている並行処理機能と便利な集合処理機能を使って計算と集合を実行します。1つのクエリから別のクエリを呼び出すこともできます。まず、基本的な例から始めましょう。

GSQLのパラメーター化されたクエリには3つのステップがあります。

  1. GSQLでクエリを定義する。GSQLで定義されたクエリは、GSQLのカタログに追加されます。

  2. カタログに、1つ以上のクエリをインストールする。各クエリに対してRESTエンドポイントが生成されます。

  3. インストール済みのクエリを実行する。適切なパラメーターを挿入して、GSQLコマンドとして実行、またはRESTエンドポイントへHTTPリクエストを送信して実行します。

基本的な1ホップのクエリ

最初のGSQLクエリを作成してみましょう。あるpersonのパラメーターを入力して、そのpersonの隣にある(1ホップの)頂点をすべて表示させます。

GSQLコマンド
USE GRAPH social
CREATE QUERY hello(VERTEX<person> p) {
  Start = {p};
  Result = SELECT tgt
           FROM Start:s-(friendship:e) ->person:tgt;
  PRINT Result;
}

このクエリでは1つのSELECT文を使用します。ここで使うSELECT文は、ビルトインのクエリよりもはるかに強力で、次のように構成されています。まずクエリのコールで渡されたパラメーター p によって特定されたpersonの頂点を、「Start」という頂点セットのシード値として与えます。中括弧 ({ }) がGSQLに、括弧内の項目を含むセットを作成するように指示しています。

次に、SELECT文は、FROM句で説明されているパターンに従って1ホップのトラバースを説明しています。

Start:s -(friendship:e)-> person:tgt

この構文は、基本的にビルトインクエリでエッジを選択したときと同じです。つまり、指定されたソースのセット(Start)から始まるすべてのエッジの中から、指定されたエッジタイプ(Friendship)であり、かつ、指定された頂点タイプ(Person)で終わっているものを選択します。ここで新しく登場した機能は、頂点セットとエッジセットのエイリアスを「:alias:」として定義できることで、「s」は、ソース頂点セットのエイリアス、「e」はエッジセットのエイリアスで、「tgt」はターゲットとなる頂点セットのエイリアスです。

最初の句に戻って、句に何が割り当てられているか見てください ("Result = SELECT tgt")。 ここではターゲットのセットのエイリアスに「tgt」が指定されています。従って、SELECT文は、(SELECTクエリブロック内のすべての句のセットがフィルタリングして処理して得られた)ターゲットの頂点セットを返し、出力された頂点セットをResultという変数に割り当てていることを意味します。

最後に、Resultの頂点セットをJSON形式で出力します。

クエリの作成

クエリは、インタラクティブモードで定義しなくてもファイルで保管すれば、GSQLシェルの中から@filenameの構文を使って呼び出すことができます。上のクエリを次のファイル /home/tigergraph/hello.gsql に貼り付けます。次に、GSQLシェルに入って、@hello.qsqlを使って呼び出します。(なお、/home/tigergraphフォルダ以外からGSQLを開く場合には、次の例のような絶対パスを使ってGSQLファイルを呼び出すことができます。例: @/home/tigergraph/hello.gsql)次に、lsコマンドを実行して、クエリがカタログに登録されているかを確認します。

GSQLシェル
GSQL > @hello.gsql
Using graph 'social'
The query hello has been added!
GSQL > ls
---- Graph social
Vertex Types:
  - VERTEX person(PRIMARY_ID name STRING, name STRING, age INT, gender STRING, state STRING) WITH STATS="OUTDEGREE_BY_EDGETYPE"
Edge Types:
  - UNDIRECTED EDGE friendship(from person, to person, connect_day DATETIME)

Graphs:
  - Graph social(person:v, friendship:e)
Jobs:
  - CREATE LOADING JOB load_social FOR GRAPH social {
      DEFINE FILENAME file2 = "/home/tigergraph/friendship.csv";
      DEFINE FILENAME file1 = "/home/tigergraph/person.csv";

      LOAD file1 TO VERTEX person VALUES($"name", $"name", $"age", $"gender", $"state") USING SEPARATOR=",", HEADER="true", EOL="\n";
      LOAD file2 TO EDGE friendship VALUES($0, $1, $2) USING SEPARATOR=",", HEADER="true", EOL="\n";
    }

Queries:
  - hello(vertex<person> p)

クエリのインストール

これからクエリをインストールして実行できるようにします。GSQLシェルで次のコマンドを入力して、今作成した「hello」というクエリをインストールします。

GSQLコマンド
INSTALL QUERY hello
GSQLシェル
GSQL > INSTALL QUERY hello
Start installing queries, about 1 minute ...
hello query: curl -X GET 'http://127.0.0.1:9000/query/social/hello?p=VALUE'. Add -H "Authorization: Bearer TOKEN" if authentication is enabled.

[========================================================================================================] 100% (1/1)

約1分でデータベースが新しいクエリをインストールします。気長に待ちましょう。大規模なデータセットに対するクエリの場合、インストールが待ちきれないように感じられても、将来クエリの実行時間が短縮されるので、何倍もの時間が節約できます。特に、パラメーターを入れ替えて何回も繰り返し実行する場合に効果的です。インストール処理は、マシンに対する指示とRESTエンドポイントを生成します。プログレスバーが100%になったら、クエリを実行することができます。

Run a Query in GSQL

GSQLでクエリを実行するには、「RUN QUERY」を使い、続いてクエリ名と一連のパラメーター値を入力します。

GSQLコマンド - クエリ実行の例
RUN QUERY hello("Tom")

結果はJSON形式で表示されます。Tomには、DanとJennyという、1ホップで隣り合わせの人が2人いることがわかりました。

GSQLシェル
GSQL > RUN QUERY hello("Tom")
{
  "error": false,
  "message": "",
  "version": {
    "edition": "developer",
    "schema": 0,
    "api": "v2"
  },
  "results": [{"Result": [
    {
      "v_id": "Dan",
      "attributes": {
        "gender": "male",
        "name": "Dan",
        "state": "ny",
        "age": 34
      },
      "v_type": "person"
    },
    {
      "v_id": "Jenny",
      "attributes": {
        "gender": "female",
        "name": "Jenny",
        "state": "tx",
        "age": 25
      },
      "v_type": "person"
    }
  ]}]
}

エンドポイントとしてクエリの実行

クエリのインストールが実行されると、処理の一部としてRESTエンドポイントも生成されます。このエンドポイントを使えば、HTTPコールによってパラメーター化されたクエリを呼び出すことができます。Linuxでは、curlコマンドが最も一般的なHTTPリクエストの送信方法です。次の例では、すべてのクエリで標準的な部分は太字 **; で表記されています。太字でない表記が、この特定のクエリとパラメーター値の部分です。JSONの結果は、Linuxシェルの標準出力で返されます。これでパラメーター化されたクエリがHTTPサービスになって使えるようになりました。

Linuxシェル
curl -X GET 'http://localhost:9000/query/social/hello?p=Tom'

最後に、次のコマンドを使って、カタログにあるクエリのGSQLテキストを見ることができます。

GSQLコマンド - クエリを表示する例
#SHOW QUERY query_name. E.g.
SHOW QUERY hello

お疲れ様でした。この時点で、クエリの定義、インストール、実行のすべてのプロセスが終了しました。

インストールなしで無名のクエリを実行

クエリをインストールすれば、最も迅速な処理速度が得られますが、ユーザーにはインストール時間の負荷がかかります。

TigerGraph 2.4で導入されたGSQLのインタプリタモードは、INSTALLステップを省いてクエリを作成するとすぐに実行できる機能です。インタラクティブなエクスペリエンスを提供します。このワンステップで実行できるインタプリタモードのクエリは、名前 なし(無名)、パラメーターなしで、SQLと全く同じです。このモードについて詳しくは、パターンマッチングを参照してください。

より高度なクエリ

次により高度なクエリを実行しましょう。今度は、強力な、ビルトインのアキュムレータの使い方を説明します。アキュムレータは、クエリがグラフをトラバースしながらアクセスする各頂点に付加できるランタイム属性(プロパティ)の役割を果たします。ランタイムとは、クエリが実行されている時間だけに存在する属性という意味で使われています。アキュムレータは、クエリが暗黙的に並列処理されている間にデータを収集 (蓄積) するために特別に設計されているため、「蓄積するもの(アキュムレータ)」と呼ばれています。

GSQLコマンドファイル - hello2.gsql
USE GRAPH social
CREATE QUERY hello2 (VERTEX<person> p) {
  OrAccum  @visited = false;
  AvgAccum @@avgAge;
  Start = {p};

  FirstNeighbors = SELECT tgt
                   FROM Start:s -(friendship:e)-> person:tgt
                   ACCUM tgt.@visited += true, s.@visited += true;

  SecondNeighbors = SELECT tgt
                    FROM FirstNeighbors -(:e)-> :tgt
                    WHERE tgt.@visited == false
                    POST_ACCUM @@avgAge += tgt.age;

  PRINT SecondNeighbors;
  PRINT @@avgAge;
}
INSTALL QUERY hello2
RUN QUERY hello2("Tom")

このクエリでは、パラメーターで入力されたpersonから2ホップの距離にあるpersonをすべて検索します。お遊びで、この2ホップの隣接者の平均年齢も計算してみます。

このようなグラフ探索アルゴリズムの標準的なアプローチでは、ブール値の変数を使用し、アルゴリズムが頂点を初回「訪問」したことを記録して、その後は訪問しても回数を数えないようにさせます。そのように動作させるには、OrAccumという種類のローカルアキュムレータを使います。ローカルアキュムレータの宣言には、その名称の前に「@」(アットマーク)を1つ、接頭文字として付けます。どのアキュムレータの種類にも、デフォルトで初期値が設定されています。アキュミュレーターのデフォルトの論理値はfalseです。また、ユーザーが初期値を指定することもできます。

ここでは、平均値も計算したいので、グローバルのAvgAccumを定義します。グローバルのアキュムレータの接頭文字は、「@」2つです。

「Start」セットを定義した後、最初の1ホップ探索が実行されます。SELECTとFROMの句は、上の最初の例と同じですが、そのほかにACCUM句が追加されています。ACCUM句の中にある+=演算子は、FROM句のパターンと一致するエッジ各辺について、右辺の式(true)を左側のアキュムレータ(tgt.@visited と s.@visitedの両方)に蓄積していくことを意味します。ソース頂点またはターゲット頂点は複数回訪問される可能性があることに注意してください。図1の場合、頂点Tomから始めると、関連するエッジが2本あるので、最初のSELECT文にあるACCUM句はTomを2回訪問します。アキュムレータの種類はOrAccumなので、この2回のトラバースが蓄積された結果は、次のようになります。

Tom.@visited ⇐= (初期値: false) OR (true) OR (true)

2本のエッジのどちらが先に処理されても結果には影響しないので、この演算は、マルチスレッドの並行処理に適しています。ベースラインは、頂点が最低1回でも訪問されれば、結果は@visited = trueになるということです。この最初のSELECT文の結果は、変数FirstNeighborsに割り当てられます。

2番目のSELECTのブロックは、もう1回ホップします。FirstNeighborsの頂点セットの変数から始めて、2ホップ先の頂点に到達します。ここでは、エッジタイプfriendshipとターゲットの頂点タイプpersonがFROM句から省かれていますが、エイリアスはそのままであることに注意してください。エイリアスにタイプが指定されていない場合は、ALLタイプが対象であると解釈されます。このグラフには頂点タイプとエッジタイプが各々1つしかないので、論理的にタイプを指定した場合と同じ結果になります。WHERE句は、訪問済と記録されている頂点(1ホップ先の頂点とスタート頂点p)を除外します。このSELECT文では、ACCUMではなく、POST_ACCUMを使います。その理由は、POST_ACCUMはエッジセットではなく頂点セットをトラバースするからで、従って頂点の重複カウントを避けることが保証できます。ここでは、2ホップ隣接の人達の年齢を蓄積して平均値を出します。

最後に、pのSecondNeighborsが出力されます。

次に、以下のすべてのGSQLコマンドを1つのファイルhello2.gsqlに入れます。

  • USE GRAPH social

  • クエリの定義

  • クエリのインストール

  • クエリの実行

このコマンド一式をGSQLシェルを使わずに実行することができます。上のGSQLコマンドを/home/tigergraph/hello2.gsqlという名前のLinuxファイルにコピー、貼り付けてください。

Linuxシェルで、/home/tigergraphから次のように入力します。

Linuxシェル
gsql hello2.gsql

GSQLクエリのまとめ

  • クエリはカタログにインストールされます。また、1つ以上のパラメーターが入力でき、再利用が可能です。

  • GSQLクエリは、一連のSELECTクエリのブロックによって構成され、各々のブロックから名前が指定された頂点セットが生成されます。

  • 各SELECTクエリのブロックは、すでに定義されてある頂点セットのどれからでもグラフのトラバースを開始することができます。(頂点の定義の順に制約されません。)

  • アキュムレータは実行時の変数で、マルチスレッドで効率よく計算できるように、積算演算が作り込まれています。

  • クエリは別のクエリを呼び出すことができます。

  • 出力はJSON形式です。