GraphDBの「TigerGraph」の話
はじめに
先日、こんな記事がありました → Netflixの屋台骨 「AIレコメンド」技術最前線
Neo4jの横にに並ぶ、TigerGraph。
ちなみに、ちょっと前に大型の調達もありました。 → エンタープライズ・グラフ・データベースのTigerGraphが111.3億円を調達
前回、Dgraphの記事を書きましたが「GraphDBの「Dgraph」の話 - Goで叩く」、時代はTigerGraphな雰囲気なのでしょうか。
ランキングもちょっと前まで、下にいたのに、
もはや、Amazon Neptuneを抜く勢いです。(本日2021/5/29)
参考: DB Engines
ベンチマークでも、TigerGraphの方がDgraphより圧倒的に早いもよう…
Comparing TigerGraph and Dgraph Using the LDBC SNB Benchmark
さて。
そうか。
これはやらねば。
と、公式をやっってみただけですが、記事を書いていきます。
準備
公式ではdockerのコマンドがありますが、Docker Composeで準備します。
Docker Compose
ymlはこんな感じ。portを三つ使います。
tigergraph:
image: docker.tigergraph.com/tigergraph:latest
ports:
- "14022:22"
- "9000:9000"
- "14240:14240"
ulimits:
nofile:
soft: 1000000
hard: 1000000
volumes:
- tigergraph
volumes:
tigergraph:
実行。
むっちゃイメージのダウンロード遅い……。
イメージが遠くのサーバーにあるのを体感できます。
TigarGraphをスタートする
22のポートを14022にForwardするようにしているので、14022ポートを指定して接続します。
ちなみに、パスワードも、tigergraph
。
ssh -p 14022 tigergraph@localhost
接続後、これでスタートさせます。
gadmin start all
mysql.server start
的な感じですかね。
なるほど。
tigergraphをuserとしてsshで接続して、サービスを起こさないといけない…と言うことは、docker-composeのcommandで素直にスタートできない系ですね。起動に何ステップか踏まないといけないのは面倒…
いろいろやってみる
GSQL Shellを実行は、このコマンド。
sshで接続したコンテナ内で実行します。
gsql
GSQL Shell内でls
を実行すると、verticesやedgesなどの一覧が見られます。
GSQL > ls
---- Global vertices, edges, and all graphs
Vertex Types:
Edge Types:
Graphs:
Jobs:
JSON API version: v2
Syntax version: v1
drop all
で、全部削除することができます。(まだ、何もないですが)
GSQL > drop all
Dropping all, about 1 minute ...
Abort all active loading jobs
Resetting GPE...
Successfully reset GPE
Stopping GPE GSE
Successfully stopped GPE GSE in 0.706 seconds
Clearing graph store...
Successfully cleared graph store
Starting GPE GSE RESTPP
Successfully started GPE GSE RESTPP in 0.101 seconds
Everything is dropped.
定義する
vertex
vertexの定義のためのクエリは、こんな感じ。
SQLのテーブルの定義と似てるので、理解しやすいですね。
CREATE VERTEX person (
PRIMARY_ID name STRING,
name STRING,
age INT,
gender STRING,
state STRING
)
attributeの名前とデータのタイプで定義します。
基本的なタイプはこんな感じ.
|Type|Default value|
| ---- | ---- | ---- |
|INT|0|
|UINT|0|
|FLOAT|0|
|DOUBLE|0|
|BOOL|false|
|STRING| ""|
|DATETIME|1970-01-01 00:00:00|
|VERTEX|"Unknown"|
|EDGE|No edge: {}|
|JSONOBJECT|An empty object: {}|
|JSONARRAY|An empty array: []|
vertexのデータには、ユニークな識別子が必要になります。
なので、PRIMARY_IDを指定し、それ以降に、optionalなattributeを記載していきます。
name、2回書くのですね…
実行。
GSQL > CREATE VERTEX person (PRIMARY_ID name STRING,name STRING,age INT,gender STRING,state STRING)
The vertex type person is created.
edge
edgeの定義のクエリは、こんな感じ。
personからpersonへのedgeを作ります。
UNDIRECTED
というインディケーターが付いてます。
これは、edgeが双方向性のあるedgeですよー、という意味です。
一方通行で良いときは、DIRECTED
を使います。
connectd_dayは、optionalなattributeです。
こんな形で、edgeに対して、attributeを定義することができます。
CREATE UNDIRECTED EDGE friendship (
FROM person,
TO person,
connect_day DATETIME
)
これで、Fromのprimary_idとToのprimary_idが繋がるようになります。
実行。
GSQL > CREATE UNDIRECTED EDGE friendship (FROM person, TO person, connect_day DATETIME)
The edge type friendship is created.
graph
グラフを定義します。
このクエリで、socailというグラフが作成されます。
CREATE GRAPH social (person, friendship)
グラフというのは、そうですね……抜き出した点と線のグループとでも言いましょうか…立ち位置的にはRDBのdatabaseと似たような立ち位置になるのかなー、と思います。
(後ほどUSE GRAPH hoge
というようなクエリもでてきますので)
実行。
GSQL > CREATE GRAPH social (person, friendship)
Stopping GPE GSE RESTPP
Successfully stopped GPE GSE RESTPP in 15.393 seconds
Starting GPE GSE RESTPP
Successfully started GPE GSE RESTPP in 0.089 seconds
The graph social is created.
以上で、グラフの定義ができました。
ls
で、これまでの定義が確認できます。
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:
Queries:
作る:Load Job
2つのCSVをコンテナに置いて、これをロードしてみます。
Jobの作成
CSVは、こんな感じ
/home/tigergraph/person.csv
name,gender,age,state
Tom,male,40,ca
Dan,male,34,ny
Jenny,female,25,tx
Kevin,male,28,az
Amily,female,22,ca
Nancy,female,20,ky
Jack,male,26,fl
/home/tigergraph/person.csv
person1,person2,date
Tom,Dan,2017-06-03
Tom,Jenny,2015-01-01
Dan,Jenny,2016-08-03
Jenny,Amily,2015-06-08
Dan,Nancy,2016-01-03
Nancy,Jack,2017-03-02
Dan,Kevin,2015-12-30
コマンドはこんな感じ。
これも、SQLにあるLOAD DATA
とそっくりですね。
USE GRAPH social
BEGIN
CREATE LOADING JOB load_social FOR GRAPH social {
DEFINE FILENAME file1="/home/tigergraph/person.csv";
DEFINE FILENAME file2="/home/tigergraph/friendship.csv";
LOAD file1 TO VERTEX person VALUES ($"name", $"name", $"age", $"gender", $"state") USING header="true", separator=",";
LOAD file2 TO EDGE friendship VALUES ($0, $1, $2) USING header="true", separator=",";
}
END
-
USE GRAPH social
どのグラフか指定します。 -
BEGIN ... END
複数行のコマンドを実行する時に囲んで、1ステートメントさせます。
実行。
GSQL > USE GRAPH social
Using graph 'social'
GSQL > BEGIN
GSQL > CREATE LOADING JOB load_social FOR GRAPH social {
GSQL > DEFINE FILENAME file1="/home/tigergraph/person.csv";
GSQL > DEFINE FILENAME file2="/home/tigergraph/friendship.csv";
GSQL >
GSQL > LOAD file1 TO VERTEX person VALUES ($"name", $"name", $"age", $"gender", $"state") USING header="true", separator=",";
GSQL > LOAD file2 TO EDGE friendship VALUES ($0, $1, $2) USING header="true", separator=",";
GSQL > }
GSQL > END
The job load_social is created.
GSQL >
Jobの実行
Job実行のクエリはこんな感じ。
RUN LOADING JOB load_social
実行。
GSQL > RUN LOADING JOB load_social
[Tip: Use "CTRL + C" to stop displaying the loading status update, then use "SHOW LOADING STATUS jobid" to track the loading progress again]
[Tip: Manage loading jobs with "ABORT/RESUME LOADING JOB jobid"]
Starting the following job, i.e.
JobName: load_social, jobid: social.load_social.file.m1.1622273212213
Loading log: '/home/tigergraph/tigergraph/log/restpp/restpp_loader_logs/social/social.load_social.file.m1.1622273212213.log'
Job "social.load_social.file.m1.1622273212213" loading status
[FINISHED] m1 ( Finished: 2 / Total: 2 )
[LOADED]
+---------------------------------------------------------------------------+
| FILENAME | LOADED LINES | AVG SPEED | DURATION|
|/home/tigergraph/friendship.csv | 8 | 7 l/s | 1.00 s|
| /home/tigergraph/person.csv | 8 | 7 l/s | 1.00 s|
+---------------------------------------------------------------------------+
ロードされました……みたいです。
見る:GSQL
QUERYの作成
以下のようなクエリで、クエリを作成できます。
ややこしいので、独自に定義するクエリを「QUERY」と書くことにしましょう。
USE GRAPH social
CREATE QUERY hello(VERTEX<person> p) {
Start = {p};
Result = SELECT tgt
FROM Start:s-(friendship:e) ->person:tgt;
PRINT Result;
}
helloはクエリ名です。
Start
は、QUERYの実行時に呼び出されるパラメータp
で代入されたpersonの識別子が入ります。
FROMで、取得するvertex、edgeを指定します。
実行。
GSQL > USE GRAPH social
Using graph 'social'
GSQL > CREATE QUERY hello(VERTEX<person> p) { Start = {p}; Result = SELECT tgt FROM Start:s-(friendship:e) ->person:tgt; PRINT Result; }
The query hello has been added!
ls
で確認すると、hello Queryが確認できますね。
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)
QUERYのインストール
この時点では、QUERYの定義はされているものの、まだインストールがされておらず、使うことができません。
インストールを行います!
クエリはこんな感じ。
INSTALL QUERY hello
実行。
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)
QUERYの実行
QUERYの実行のクエリはこんな感じ。
personをパラメータとするクエリなので、personのprimary_idを指定してあげます。
RUN QUERY hello("Tom")
実行。
GSQL > RUN QUERY hello("Tom")
{
"error": false,
"message": "",
"version": {
"schema": 0,
"edition": "enterprise",
"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"
}
]}]
}
Tomさんの友達が出力されました。
でもこれ、いちいちQUERY定義つくるのでしょうか…?
サービス上でどう使えばいいんだろう……migrationする時とかに、QUERYも定義すればいいのですかね?
もうちょっと複雑なQUERY
このQUERYは、2ホップ隣のpersonを取得し且つその平均年齢を取ってくるQUERYです。
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;
}
@
でlocalのaccumulatorを定義できます。(variableではない)
or条件のaccumulatorを定義するため、OrAccumというtypeを指定しています。
今回、初期値にfalseを与えています。
@@
でglobalなaccumulatorを定義できます。
averageのaccumulatorを定義するため、AvgAccumというtypeを指定しています。
1つめのselect文で、1ホップ隣のperson vertexを取得しているのですが、その時に@visitedにtrueを入れていき、既出のpersonかどうかを判定できるようにしています。
+=でTom.@visited <== (initial value: false) OR (true) OR (true)
こんなふうに値が蓄積(accumulate)されていきます。
2つめのselect文で、2ホップ隣のperson vertexを取得しているのですが、whereで、1ホップ隣のpersonを除くようにして、2ホップ隣のpersonだけを取得しています。
なるほどぉ。
見る:curl(Built-in Queries)
curlでBiult-inのクエリを実行してみます。
docker composeで空けていた9000のポートが、httpを受け付けているポートです。
count
これは、stat_vertex_number
というクエリで、vetexの数を返してくれるクエリです。
typeは、vertexの名前を入れるようですが、ワイルドカードも使えるようです。
curl -X POST 'http://localhost:9000/builtins/social' -d '{"function":"stat_vertex_number","type":"*"}' | jq .
実行。
curl -X POST 'http://localhost:9000/builtins/social' -d '{"function":"stat_vertex_number","type":"*"}' | jq .
{
"version": {
"edition": "enterprise",
"api": "v2",
"schema": 0
},
"error": false,
"message": "",
"results": [
{
"v_type": "person",
"count": 7
}
]
}
select
vertexの詳細を取得するパスはこちら。"http://localhost:9000/graph/{graph_name}/vertices/{vertex_type}/{vertex_id}"
実行。
curl -X GET "http://localhost:9000/graph/social/vertices/person/Tom" | jq .
{
"version": {
"edition": "enterprise",
"api": "v2",
"schema": 0
},
"error": false,
"message": "",
"results": [
{
"v_id": "Tom",
"v_type": "person",
"attributes": {
"name": "Tom",
"age": 40,
"gender": "male",
"state": "ca"
}
}
]
}
Tomさんの情報が取得できました。
edge取得のパスは、http://localhost:9000/graph/edges/{source_vertex_type}/{source_vertex_id}/{edge_type}/
ですが、edgeの先のvertexのidで絞り込む時は、http://localhost:9000/graph/edges/{source_vertex_type}/{source_vertex_id}/{edge_type}/{target_vertex_type}/{target_vertex_id}
です。
実行。
curl -X GET "http://localhost:9000/graph/social/edges/person/Tom/friendship/" | jq .
{
"version": {
"edition": "enterprise",
"api": "v2",
"schema": 0
},
"error": false,
"message": "",
"results": [
{
"e_type": "friendship",
"directed": false,
"from_id": "Tom",
"from_type": "person",
"to_id": "Dan",
"to_type": "person",
"attributes": {
"connect_day": "2017-06-03 00:00:00"
}
},
{
"e_type": "friendship",
"directed": false,
"from_id": "Tom",
"from_type": "person",
"to_id": "Jenny",
"to_type": "person",
"attributes": {
"connect_day": "2015-01-01 00:00:00"
}
}
]
}
見る:curl(独自クエリ)
先ほどhello QUERYを投げてみましょう。
パスは、http://localhost:9000/query/{graph_name}/{query_name}?p=Tom
実行。
curl -X GET 'http://localhost:9000/query/social/hello?p=Tom' | jq
{
"version": {
"edition": "enterprise",
"api": "v2",
"schema": 0
},
"error": false,
"message": "",
"results": [
{
"Result": [
{
"v_id": "Dan",
"v_type": "person",
"attributes": {
"name": "Dan",
"age": 34,
"gender": "male",
"state": "ny"
}
},
{
"v_id": "Jenny",
"v_type": "person",
"attributes": {
"name": "Jenny",
"age": 25,
"gender": "female",
"state": "tx"
}
}
]
}
]
}
Tomさんの友達が取得できました。
作る:GSQL
CSVからperson作る方法を書きましたが、SQLのようにINSERT文でも書けます。
ただし、直接INSERTを流せるわけではなく、QUERYとしてインストールして、流す仕様です。
(なので、QUERY作成の後に書きました。)
INSERTのクエリはこんな感じ。
INSERT INTO vertex_or_edge_type VALUES (full_list_of_parameter_values)
すべてのパラメータを使わない場合は、こんな感じ。
INSERT INTO vertex_type (PRIMARY_ID, specified_attributes) VALUES (ID, values_for_specified_attributes)
クエリの作成
CREATE QUERY insertPerson(STRING name, INT age, STRING gender, STRING state) FOR GRAPH social {
INSERT INTO person VALUES (name, name, age, gender, state);
}
実行。
GSQL > CREATE QUERY insertPerson(STRING name, INT age, STRING gender, STRING state) FOR GRAPH social { INSERT INTO person VALUES (name, name, age, gender, state);}
The query insertPerson has been added!
クエリの実行
まずは、インストール。
このクエリを、
INSTALL QUERY insertPerson
実行。
GSQL > INSTALL QUERY insertPerson
Start installing queries, about 1 minute ...
insertPerson query: curl -X GET 'http://127.0.0.1:9000/query/social/insertPerson?name=VALUE&age=VALUE&gender=VALUE&state=VALUE'. Add -H "Authorization: Bearer TOKEN" if authentication is enabled.
[========================================================================================================] 100% (1/1)
そしてQUERYの実行のクエリ
RUN QUERY insertPerson("Masamiki",32,"male","ca")
の実行。
GSQL > RUN QUERY insertPerson("Masamiki",32,"male","ca")
{
"error": false,
"message": "",
"version": {
"schema": 0,
"edition": "enterprise",
"api": "v2"
},
"results": []
}
curlで確認すると、ちゃんとMasamikiの詳細が返ってきました。
curl -X GET "http://localhost:9000/graph/social/vertices/person/Masamiki" | jq .
{
"version": {
"edition": "enterprise",
"api": "v2",
"schema": 0
},
"error": false,
"message": "",
"results": [
{
"v_id": "Masamiki",
"v_type": "person",
"attributes": {
"name": "Masamiki",
"age": 32,
"gender": "male",
"state": "ca"
}
}
]
}
GraphStudio
gadmin start all
の後、http://localhost:14240をブラウザで表示するとGraphStudioというGUIを開くことができます。
右上の「admin」をクリックすると、ダッシュボードページに遷移できます。
どうやら、ここでinfraの監視が行えるようです。
トップページの「DesingSchema」をクリックすると、定義したvertexとedgeが見られます。
マウスオーバーでattributeも確認できます。
クリックするとプロパティが表示されて、編集も行えるようです。
アイコンも設定できるみたいです。
おもしろいUIですね。
クエリを使わなくても、vertexとedgeはここで定義していくことができるようになっているようです。
exploreを使ってみようと、クリックするもgraphを選択してくださいとの警告。
おっと、どこで選択するんだ…あぁ、ここか。
グラフを選択して、各メニューがアクティベートされました。
さて、exploreですが…まず、ALLでピック!
ちゃんと登録したpersonが出てきてますね。
ちなみに、どんなクエリ投げてるかも見えるので、こっちで最初は勉強できそう。
おわりに
Dgraphを最近は使っていたのですが、TigerGraphの方が素直で分かり易かったです。
SQLに似たクエリが使えるので、大半のエンジニアがそうだと思いますが、SQLに慣れ親しんだ人が多いので、嫌悪感を湧かさずに触れます。
基本的に定義を作る時とかで時間がかかっていた印象ですが、クエリの実行がTigerGraphの方が速いのであれば、ほぼ文句ないですね…
懸念点としては、最小のサイズで(フリーを除く)1インスタンスで1時間120円程度、つまり年間最低100万円程度…。
サービスとしてうまくいってからでないと、利用するのが難しい値段だなという。
いや、Dgraphもマネージドはそれ以上にかかるか。
よし、移行しよう。
Discussion