1ホップのパターン

はじめに

‌パターンマッチングは、その性質上、宣言によって処理されます。ユーザーは、クエリの土台となる処理の詳細にわずらわされることなく、クエリで求めたいものを指定することに専念できます。

求めるパターンは、通常、クエリの構造で最も基礎的な部分であるFROM句で宣言されます。パターンは、一連の頂点タイプのセットと、それらとエッジタイプとの結び付き方を指定されます。パターンは、WHERE句にある条件により、さらに微調整できます。このチュートリアルでは、まず単純な1ホップパスのパターンを説明し、次にマルチホップのパターン、最後にマルチパスのパターンへと進みます。

現在、パターンマッチングは、読み取りのみ(read-only)のクエリで利用可能です。今後のリリースでDMLのサポートが追加される予定です。

パターンマッチングはネストされたクエリに対応します。

1ホップのパターン

パターンについて理解するには、単純な1ホップパターンから始めるのが一番簡単です。1回ホップするだけのクエリでも選択肢がいくつもあります。1回のホップについて学んだら、反復を追加して可変長パターンを作ったり、1ホップクエリ同士をつなげて大きなパターンを作ったりする方法に進みます。

GSQL 101で説明されているような従来のGSQLクエリでは、FORM句で次の記号 -( )-> を使って1ホップクエリであることを表しました。ここでは、右向きの矢印が頂点の流れを指定し、括弧 ( ) の中にエッジタイプが置かれます。

Person:p -(LIKES:e)-> Message:m          /* 従来のGSQLの例 */

パターンマッチングでは、次の記号 -( )- を使って1ホップパターンを表します。ここでは、エッジタイプは括弧 () の中に置かれ、ハイフン - が接続を表し、方向は指定しません。その代わり、有向性は、各エッジタイプ 1つひとつ において明示されます。

  • 無向エッジEの場合、記号は付加しません。 E

  • 左から右向きの有向エッジEの場合、接尾文字>を付加します。 E>

  • 左から右向きの有向エッジEの場合、接尾文字>を付加します。 <E

例えば、LDBC SNBのスキーマでは、PersonとMessageの間に有向な関係性が2つ存在します。すなわち、人はメッセージが LIKES (好きである)という関係と、メッセージにとって人は HAS_CREATOR (その作成者である)という関係です。この2つの関係性は左右逆の方向性を持っていますが、交互セパレーター | を使うことによって、1つのパターンに簡潔に含むことができます。

Person:p -((LIKES>|<HAS_CREATOR):e)- Message:m         /* パターンの例 */

エッジタイプのワイルドカード

アンダースコア ``_ はワイルドカードで、任意のエッジタイプ を意味します。矢印はここでも方向を表します。例えば、 > または < または _
空の括弧 () は有向または無向の任意のエッジを意味します。

頂点タイプのワイルドカードとパスの対称性

TigerGraph 3.0 がリリースされる前は、ソースとなる(最も左に位置している)頂点セットを、SELECT文の前に、明示セットとして定義する必要がありました。典型的なアプローチをここに示します。

従来のGSQLで必要なシードセットの定義
CREATE QUERY seedSet() FOR GRAPH ldbc_snb SYNTAX v1 {
    Source = {Person}; // Seed set
    SELECT t FROM Source:s -(IS_LOCATED_IN:e)- :t;
    PRINT t;
}

TigerGraph 3.0からは、SYNTAX V2がソース頂点セットとターゲット頂点セットを同じように取り扱います。つまり、ソースまたはターゲットの頂点セットを次にように表すことができます。

  • 頂点タイプ SELECT t FROM Person:s -(IS_LOCATED_IN>:e) - City:t

  • 頂点タイプの交換 SELECT t FROM (Post|Comment):s -(IS_LOCATED_IN>:e) - Country:t

  • 頂点タイプが省かれて、エイリアスのみで表され、任意の頂点タイプを意味する SELECT s FROM :s -(IS_LOCATED_IN>:e) - Country:t

  • 頂点タイプが省かれて、エイリアスなしで、任意の頂点タイプを意味する SELECT t FROM -(IS_LOCATED_IN>:e) - Country:t

タイプを明示したほうが優秀なパフォーマンスが得られる可能性があります。

1ホップパターンの例

  1. FROM X:x - (E1:e1) - Y:y

    • E1は無方向エッジです。x と y がE1のエンドポイントに結び付けられており、e1 は E1のエイリアスです。

  2. FROM X:x - (E2>:e2) - Y:y

    • 右向きのエッジ x がE2のソースと結び付きます。y はE2のターゲットと結び付きます。

  3. FROM X:x - (<E3:e3) - Y:y

    • 左向きのエッジ y がE3のソースと結び付きます。x はE3のターゲットと結び付きます。

  4. FROM X:x - (_:e) - Y:y

    • X のメンバーとYのメンバー間にある任意の無向性エッジ。

  5. FROM X:x - (_>:e) - Y:y

    • XにソースがありYにターゲットがある、任意の右向きのエッジ。

  6. FROM X:x - (<_:e) - Y:y

    • YにソースがありXにターゲットがある、任意の左向きのエッジ。

  7. FROM X:x - ((<_|_):e) - Y:y

    • 任意の左向きまたは任意の無向性のエッジ。 "|" はORを意味し、括弧の中にエッジの説明がまとめられています。e はエッジパターン (<_|_) のエイリアスです。

  8. FROM X:x - ((E1|E2>|<E3):e) - Y:y

    • 3つのエッジパターンのいずれか1つ。

  9. FROM X:x - () - Y:y

    • 任意のエッジ(有向、無向)

    • (<_|>|) と同じ。

パターンマッチの構文モードを入力

パターンマッチングを使うには、セッションパラメータを設定するか、クエリ内で指定する必要があります。現在、クエリに使う構文には2つのバージョンがあります。

  • "v1" は従来の構文で、SELECT文毎に1ホップ横断します。これはデフォルトモードです。

  • "v2" はv1の構文をパターンマッチング機能で強化したものです。

セッションパラメータ: syntax_version

SETコマンドを使って syntax_version にセッションパラメータ値を付与することができます。従来の構文の場合はv1を、パターンマッチングの場合にはv2を指定します。パラメータの設定がない場合には、従来のv1構文が有効です。パラメータの設定は、そのGSQLクライアントセッション中有効です。セッション中に再度SETコマンドを使って変更することもできます。

GSQL: セッションパラメータで構文バージョンを設定する
SET syntax_version="v2"

クエリレベルでSYNTAXを使った設定

CREATE QUERY文の中のSYNTAX句 節を使って構文を選択することもできます。従来の構文 (デフォルト) はv1、パターンマッチングはv2です。クエリレベルでのSYNTAXの選択は、syntax_versionのセッションパラメータ設定を上書きします。

GSQL: クエリ内のグラフ名の後ろでバ―ションを指定して、構文のバージョンを設定する
CREATE QUERY test10 (string str ) FOR GRAPH ldbc_snb SYNTAX v2
{
  ...
}

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

このチュートリアルでは、TigerGraph 2.4で追加されたインタプリタモードのGSQLを使います。インタプリタモードを使うと、インストールのステップを省くことができ、再生したクエリをすぐに実行できるので、インタラクティブなエクスペリエンスが提供されます。このようなワンステップで実行できるインタプリタモードのクエリは、名前なし(無名)、パラメータなしで、SQLと全く同じです。

無名のクエリを実行するには、キーワードCREATEの代わりにINTERPRETを使います。パラメータは利用できないことに注意してください。

INTERPRET QUERY () FOR GRAPH graph_name SYNTAX v2 { <query body> }

推奨: クエリのタイムアウトのしきい値を上げてください。

インタプリタモードのクエリは、インストールされたクエリよりも時間がかかる場合があるので、クエリのタイムアウトのしきい値を上げることをお勧めします。

GSQL: タイムアウトの設定値を上げる
# クエリタイムアウトを1分に設定
# 1単位は1ミリ秒
SET query_timeout = 60000

1ホップの固定長クエリの例

例1. 「Viktor Akhiezer」という名前の人を知っている人たちを探して、その中から最高齢者3人を求める。

例1. 左向きのエッジのパターン
USE GRAPH ldbc_snb

INTERPRET QUERY () SYNTAX v2 {
   #1ホップパターン
   friends = SELECT p
             FROM Person:s -(KNOWS:e)- Person:p
             WHERE s.firstName == "Viktor" AND s.lastName == "Akhiezer"
             ORDER BY p.birthday ASC
             LIMIT 3;

    PRINT  friends[friends.firstName, friends.lastName, friends.birthday];
}

TigerGraph 3.0以降は構文機能が強化されました。

  • • 例1にある「FOR GRAPH ldbc_snb」は、クエリシグネチャの () の後ろに使われていません。これは、バージョン3.0以降で「USE GRAPH graphName」を使った場合に適用できるオプションのコンポーネントです。その他の場合、コマンドラインでのクエリの呼び出しは、必ずその前に「gsql -g graphName」が置かれます。

  • FROM句では、頂点タイプPersonを直接、始めの頂点セットとして使います。この構文機能の強化は、バージョン2の構文のみにて利用可能です。

上記のGSQLスクリプトをexample1.qsglと名付けたファイルにコピーしてスクリプトファイルとし、Linuxから呼び出すことができます。

Linuxバッシュ
gsql example1.gsql
例1の出力
{
  "error": false,
  "message": "",
  "version": {
    "schema": 0,
    "edition": "developer",
    "api": "v2"
  },
  "results": [{"friends": [
    {
      "v_id": "10995116279461",
      "attributes": {
        "friends.birthday": "1980-05-13 00:00:00",
        "friends.lastName": "Cajes",
        "friends.firstName": "Gregorio"
      },
      "v_type": "Person"
    },
    {
      "v_id": "4398046517846",
      "attributes": {
        "friends.birthday": "1980-04-24 00:00:00",
        "friends.lastName": "Glosca",
        "friends.firstName": "Abdul-Malik"
      },
      "v_type": "Person"
    },
    {
      "v_id": "6597069776731",
      "attributes": {
        "friends.birthday": "1981-02-25 00:00:00",
        "friends.lastName": "Carlsson",
        "friends.firstName": "Sven"
      },
      "v_type": "Person"
    }
  ]}]
}

例2. Viktorが好きなコメントと投稿の各々の合計を求める。Personは、有向エッジLIKESを使ってコメントや投稿と結び付くことができます。

例2. 右向きエッジのパターン
USE GRAPH ldbc_snb

INTERPRET QUERY () SYNTAX v2 {
   SumAccum<int> @commentCnt= 0;
   SumAccum<int> @postCnt= 0;

   #1ホップのパターン
   Result = SELECT s
            FROM Person:s -(LIKES>)- :tgt
            WHERE s.firstName == "Viktor" AND s.lastName == "Akhiezer"
            ACCUM CASE WHEN tgt.type == "Comment" THEN
                           s.@commentCnt += 1
                       WHEN tgt.type == "Post" THEN
                           s.@postCnt += 1
                   END;

    PRINT  Result[Result.@commentCnt, Result.@postCnt];
}

上記のGSQLスクリプトをexample2.qsglと名付けたファイルにコピーしてスクリプトファイルとし、Linuxから呼び出すことができます。

Linuxバッシュ
gsql example2.gsql
例2の出力
Using graph 'ldbc_snb'
{
  "error": false,
  "message": "",
  "version": {
    "schema": 0,
    "edition": "enterprise",
    "api": "v2"
  },
  "results": [{"Result": [{
    "v_id": "28587302323577",
    "attributes": {
      "Result.@commentCnt": 108,
      "Result.@postCnt": 51
    },
    "v_type": "Person"
  }]}]
}

例3. 例2と同じ問題を、左向きエッジのパターンを使って解決する。

次(8行目)の例では、ソースの頂点セットがCommentとPostで、ターゲットがPersonになっていることに注意してください。

例3. 左向きエッジのパターン
USE GRAPH ldbc_snb
​
INTERPRET QUERY () SYNTAX v2{
   SumAccum<int> @commentCnt= 0;
   SumAccum<int> @postCnt= 0;
​
   Result = SELECT tgt
            FROM Person:tgt -(<LIKES_REVERSE)- (Comment|Post):src
            WHERE tgt.firstName == "Viktor" AND tgt.lastName == "Akhiezer"
            ACCUM CASE WHEN src.type == "Comment" THEN
                           tgt.@commentCnt += 1
                       WHEN src.type == "Post" THEN
                           tgt.@postCnt += 1
                   END;
​
  PRINT Result[Result.@commentCnt, Result.@postCnt];
}

上記のGSQLスクリプトをexample3.qsglと名付けたファイルにコピーしてスクリプトファイルとし、Linuxから呼び出すことができます。出力は例2と同じになります。

例4. Viktor Akhiezerの関連したコメントと関連した投稿の各々の合計を探す。「関連した」とは、Viktorが作成した、またはいいね!したコメントや投稿を指します。ここで、HAS_CREATORのエッジタイプはComment|Postから始まり、LIKESのエッジタイプはPersonから始まっていることに注意してください。

例4. 分離型1ホップエッジのパターン
USE GRAPH ldbc_snb
set query_timeout=60000

INTERPRET QUERY () SYNTAX v2{
  SumAccum<int> @commentCnt= 0;
  SumAccum<int> @postCnt= 0;

  Result = SELECT tgt
           FROM Person:tgt -(<HAS_CREATOR|LIKES>)- (Comment|Post):src
           WHERE tgt.firstName == "Viktor" AND tgt.lastName == "Akhiezer"
           ACCUM CASE WHEN src.type == "Comment" THEN
                          tgt.@commentCnt += 1
                      WHEN src.type == "Post" THEN
                          tgt.@postCnt += 1
                 END;

  PRINT Result[Result.@commentCnt, Result.@postCnt];
}

上記のGSQLスクリプトをexample4.qsglと名付けたファイルにコピーしてスクリプトファイルとし、Linuxから呼び出すことができます。

Linuxバッシュ
gsql example4.gsql
例4の出力
Using graph 'ldbc_snb'
{
  "error": false,
  "message": "",
  "version": {
    "schema": 0,
    "edition": "enterprise",
    "api": "v2"
  },
  "results": [{"Result": [{
    "v_id": "28587302323577",
    "attributes": {
      "Result.@commentCnt": 152,
      "Result.@postCnt": 96
    },
    "v_type": "Person"
  }]}]
}

例5. Viktor Akhiezerに関連したコメントや投稿の合計数を求める。今度は、投稿とコメントを一緒に数えて、ワイルドカード "_" を使って、2種類のエッジタイプHAS_CREATORとLIKES_REVERSEを表します。両方とも同じ向きです。

例5. 分離型1ホップエッジのパターン
USE GRAPH ldbc_snb

INTERPRET QUERY () SYNTAX v2{
  SumAccum<int> @@cnt= 0;

  Result = SELECT tgt
           FROM Person:tgt -(<_)- (Comment|Post):src
           WHERE tgt.firstName == "Viktor" AND tgt.lastName == "Akhiezer"
           ACCUM  @@cnt += 1;

  PRINT @@cnt;
}

上記のGSQLスクリプトをexample5.qsglと名付けたファイルにコピーしてスクリプトファイルとし、Linuxから呼び出すことができます。

Linuxバッシュ
gsql example5.gsql
例5の出力
Using graph 'ldbc_snb'
{
  "error": false,
  "message": "",
  "version": {
    "schema": 0,
    "edition": "enterprise",
    "api": "v2"
  },
  "results": [{"@@cnt": 248}]
}