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文の前に、明示セットとして定義する必要がありました。典型的なアプローチをここに示します。
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ホップパターンの例
-
FROM X:x - (E1:e1) - Y:y
-
E1は無方向エッジです。x と y がE1のエンドポイントに結び付けられており、e1 は E1のエイリアスです。
-
-
FROM X:x - (E2>:e2) - Y:y
-
右向きのエッジ x がE2のソースと結び付きます。y はE2のターゲットと結び付きます。
-
-
FROM X:x - (<E3:e3) - Y:y
-
左向きのエッジ y がE3のソースと結び付きます。x はE3のターゲットと結び付きます。
-
-
FROM X:x - (_:e) - Y:y
-
X のメンバーとYのメンバー間にある任意の無向性エッジ。
-
-
FROM X:x - (_>:e) - Y:y
-
XにソースがありYにターゲットがある、任意の右向きのエッジ。
-
-
FROM X:x - (<_:e) - Y:y
-
YにソースがありXにターゲットがある、任意の左向きのエッジ。
-
-
FROM X:x - ((<_|_):e) - Y:y
-
任意の左向きまたは任意の無向性のエッジ。 "|" はORを意味し、括弧の中にエッジの説明がまとめられています。e はエッジパターン (<_|_) のエイリアスです。
-
-
FROM X:x - ((E1|E2>|<E3):e) - Y:y
-
3つのエッジパターンのいずれか1つ。
-
-
FROM X:x - () - Y:y
-
任意のエッジ(有向、無向)
-
(<_|>|)
と同じ。
-
パターンマッチの構文モードを入力
パターンマッチングを使うには、セッションパラメータを設定するか、クエリ内で指定する必要があります。現在、クエリに使う構文には2つのバージョンがあります。
-
"v1" は従来の構文で、SELECT文毎に1ホップ横断します。これはデフォルトモードです。
-
"v2" はv1の構文をパターンマッチング機能で強化したものです。
インストールなしで無名のクエリを実行する
このチュートリアルでは、TigerGraph 2.4で追加されたインタプリタモードのGSQLを使います。インタプリタモードを使うと、インストールのステップを省くことができ、再生したクエリをすぐに実行できるので、インタラクティブなエクスペリエンスが提供されます。このようなワンステップで実行できるインタプリタモードのクエリは、名前なし(無名)、パラメータなしで、SQLと全く同じです。
無名のクエリを実行するには、キーワードCREATEの代わりにINTERPRETを使います。パラメータは利用できないことに注意してください。
INTERPRET QUERY () FOR GRAPH graph_name SYNTAX v2 { <query body> }
推奨: クエリのタイムアウトのしきい値を上げてください。 インタプリタモードのクエリは、インストールされたクエリよりも時間がかかる場合があるので、クエリのタイムアウトのしきい値を上げることをお勧めします。 GSQL: タイムアウトの設定値を上げる
|
1ホップの固定長クエリの例
例1. 「Viktor Akhiezer」という名前の人を知っている人たちを探して、その中から最高齢者3人を求める。
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以降は構文機能が強化されました。
|
上記のGSQLスクリプトをexample1.qsglと名付けたファイルにコピーしてスクリプトファイルとし、Linuxから呼び出すことができます。
gsql example1.gsql
{
"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を使ってコメントや投稿と結び付くことができます。
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から呼び出すことができます。
gsql example2.gsql
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になっていることに注意してください。
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から始まっていることに注意してください。
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から呼び出すことができます。
gsql example4.gsql
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を表します。両方とも同じ向きです。
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から呼び出すことができます。
gsql example5.gsql
Using graph 'ldbc_snb'
{
"error": false,
"message": "",
"version": {
"schema": 0,
"edition": "enterprise",
"api": "v2"
},
"results": [{"@@cnt": 248}]
}