1. 初めに
- プロパティグラフに触れる機会があったので、備忘録として残す気持ちで書きます。
- グラフと聞くと、グラフ理論のようなアカデミックな数学を連想し易いですが、「グラフデータベース」を活用する場合はグラフ理論のような複雑な話はあまり必要ありません。
- ここでは「グラフデータベース」にフォーカスを当ててながら認めます。
2. グラフとは
グラフは一言で言うと、単なる頂点(Vertex)と辺(edge)の集合です。
グラフはエンティティをノードとして表し、エンティティが世界とどのように関連しているかを関係として表現します。
グラフのデータモデルは以下の3つがあります。
プロパティグラフ
プロパティグラフの特徴として下記4つあります。
- プロパティグラフにはノードと関係が含まれている。
- ノードには、プロパティ(Key/Value)が含まれている。
- 関係には名前と方向(direction)があり、必ず開始ノードと終了ノードがあります。
- 関係にもプロパティを含めることができます。
例) 簡単なプロパティグラフ

ハイパーグラフ
ハイパーグラフは関係を任意の数のノードに接続できる汎用的なグラフモデルです。
前述のプロパティグラフでは関係は1つの開始ノードから終了ノードしか持てないが、ハイパーグラフでは任意の数のノードに接続できます。
例) アリスとボブが車を3台所有していることを表現するハイパーグラフ

トリプル
トリプルは「主語- 述語- 目的語」というデータ構造になります。
トリプルはRDF(Resource Description Framework)のメタデータモデルに当たります。
逆に言うと、RDFはリソース(定義されていないこと)をトリプルで表現します。
例) 「Ora Lassilaは、資源(http://www.w3.org/Home/Lassila) の作者である」 という自然言語をトリプルで表現するRDF
- 文の構成
主語 | リソース | http://www.w3.org/Home/Lassila |
---|---|---|
述語 | プロパティ | Creator |
目的語 | プロパティ値 | "Ora Lassila" |
- RDF/XML表現
<rdf:RDF>
<rdf:Description about="http://www.w3.org/Home/Lassila">
<s:Creator>Ora Lassila</s:Creator>
</rdf:Description>
</rdf:RDF>
※今回はプロパティグラフのお話なので上記ハイパーグラフとトリプルの話は関連しておりません。
3. 環境(Parallel Graph Analytix)
PGX
今回はParallel Graph Analytix(通称:PGX)と呼ばれるグラフツールキットを用います。
PGXには、グラフクエリ言語、多様な分析機能やMachine Learningのサポートが含まれています。
下記の絵はPGXの全体概要です。
Oracle Cloud Infrastructure
PGXをどのように用いるのか、簡単なクラウドアーキテクチャは下記の通りです。
簡素化のために全てPublic Subnetベースで環境を整えます。
中身は下記の通りです。
クライアントサーバ
- oracle-instantclient19.6-basic
- oracle-graph-client-20.3.0
- jdk-11.0.3
グラフサーバ
- oracle-graph-20.3.0
- jdk-1.8.0
- ※今回は特に使用しておりませんが、Tomcatへデプロイすることも可能です
DBサーバ
- Autonomous Database(19c) <- DBCSでも構いません
- OPG_APIS.CREATE_PG()プロシージャでグラフを作成する
SQL> Execute OPG_APIS.CREATE_PG('Graph',4,8,'USERS');
SQL> select table_name from user_tables;
TABLE_NAME
--------------------
GRAPHGT$
GRAPHIT$
GRAPHSS$
GRAPHVT$
GRAPHGE$
SQL> desc GRAPHVT$
Name Null? Type
------------------ ---------- ---------------------
VID NOT NULL NUMBER
VL NVARCHAR2(3100)
K NVARCHAR2(3100)
T NUMBER(38)
V NVARCHAR2(15000)
VN NUMBER
VT TIMESTAMP(6) WITH TIME ZONE
SL NUMBER
VTS DATE
VTE DATE
FE NVARCHAR2(4000)
4. グラフクエリ(PGQL)を投げるところまで
RDBデータ
作成するプロパティグラフ
Googleで検索するとサンプルデータが豊富に出てきますが、それらを使ってもつまらないので、ショボいグラフではありますが自分で作ってみます。素直にRDB側のGRAPHVT$表とGRAPHGE$表へInsertします。
ノード作成
insert into GRAPHVT$ (VID,VL,T,K,V) values (1,'person',1,'name','Sato');
insert into GRAPHVT$ (VID,VL,T,K,VN) values(1,'person',2,'age',40);
insert into GRAPHVT$ (VID,VL,T,K,V) values(2,'person',1,'name','Suzuki');
insert into GRAPHVT$ (VID,VL,T,K,VN) values(2,'person',2,'age',20);
insert into GRAPHVT$ (VID,VL,T,K,V) values(3,'person',1,'name','Yamamoto');
insert into GRAPHVT$ (VID,VL,T,K,VN) values(3,'person',2,'age',35);
insert into GRAPHVT$ (VID,VL,T,K,V) values(4,'person',1,'name','Tanaka');
insert into GRAPHVT$ (VID,VL,T,K,VN) values(4,'person',2,'age',25);
エッジ作成
create sequence graph_eid_seq;
alter sequence graph_eid_seq restart;
insert into GRAPHGE$ (EID,SVID,DVID,EL,K,T,VN) values(graph_eid_seq.nextval,1,2,'knows','weight',3,0.5);
insert into GRAPHGE$ (EID,SVID,DVID,EL,K,T,VN) values(graph_eid_seq.nextval,1,4,'knows','weight',3,0.5);
insert into GRAPHGE$ (EID,SVID,DVID,EL,K,T,VN) values(graph_eid_seq.nextval,4,2,'likes','weight',3,0.8);
insert into GRAPHGE$ (EID,SVID,DVID,EL,K,T,VN) values(graph_eid_seq.nextval,4,3,'knows','weight',3,0.7);
insert into GRAPHGE$ (EID,SVID,DVID,EL,K,T,VN) values(graph_eid_seq.nextval,3,1,'knows','weight',3,0.9);
JShellによる接続
Jshellを用いた場合の接続方法を記します。
[oracle@cli bin] curl -X POST -H 'Content-Type: application/json' -d '{"username": "***", "password": "***"}' http://10.51.0.2:7007/auth/token
->正しくコマンドを打つと、Access Tokenが返ってきます
[oracle@cli bin] ./oracle-graph-client-20.3.0/bin/opg-jshell --base_url http://10.51.0.2:7007
enter authentication token (press Enter for no token): <-Curlコマンドで取得したTokenをコピペします
For an introduction type: /help intro
Oracle Graph Client Shell 20.3.0
PGX server version: 20.1.1 type: SM
PGX server API version: 3.8.1
PGQL version: 1.3
Variables instance, session, and analyst ready to use.
opg-jshell> GraphConfig cfg = GraphConfigBuilder.forPropertyGraphRdbms()
.setName("Graph")
.addVertexProperty("name",PropertyType.STRING)
.addVertexProperty("age",PropertyType.INTEGER)
.addEdgeProperty("weight",PropertyType.FLOAT)
.setLoadVertexLabels(true)
.setLoadEdgeLabel(true).build(); <-扱うグラフを定義する
opg-jshell> PgxGraph graph = session.readGraphWithProperties(cfg); <-RDBからOn-Memoryへグラフをロードする
graph ==> PgxGraph[name=Graph,N=4,E=5,created=1596986537591]
opg-jshell> graph.queryPgql("SELECT count(v) FROM Graph MATCH (v)").print(10).close(); <-PGQL(1)
+----------+
| count(v) |
+----------+
| 4 |
+----------+
opg-jshell>
opg-jshell> graph.queryPgql("SELECT id(n), label(n),n.name as name1,n.age as age1,label(e), e.weight, id(m),label(m),m.name as name2,m.age as age2 FROM MATCH (n) -[e]-> (m)").print(10).close();
<- PGQL(2)
+---------------------------------------------------------------------------------------------+
| id(n) | label(n) | name1 | age1 | label(e) | weight | id(m) | label(m) | name2 | age2 |
+---------------------------------------------------------------------------------------------+
| 3 | person | Yamamoto | 35 | knows | 0.9 | 1 | person | Sato | 40 |
| 4 | person | Tanaka | 25 | knows | 0.7 | 3 | person | Yamamoto | 35 |
| 4 | person | Tanaka | 25 | likes | 0.8 | 2 | person | Suzuki | 20 |
| 1 | person | Sato | 40 | knows | 0.5 | 4 | person | Tanaka | 25 |
| 1 | person | Sato | 40 | knows | 0.5 | 2 | person | Suzuki | 20 |
+---------------------------------------------------------------------------------------------+
opg-jshell>
Javaによる接続
Javaでの接続を記します。JshellでしかPGXを触れない場合はこちらは必要ありません。
ただ、Javaの方がInteractiveに実行せずに済むのでラクです。
import oracle.pgx.api.*;
import oracle.pgx.config.*;
import oracle.pg.rdbms.*;
import oracle.pgx.common.types.*;
import java.util.function.Supplier;
public class TokenConnect{
public static void main(Srting[] args) throws Exception{
/*引数にはURLとCurlで取得したTokenを指定する仕様にしてあります*/
String baseUrl = args[0];
String token = args[1];
ServerInstance instance = Pgx.setInstance(baseUrl,token);
try (PgxSession session = instance.createSession("my-session")){
Supplier<GraphConfig> cfg = () ->{return GraphConfigBuilder.forPropertyGraphRdbms()
.forPropertyGraphRdbms()
.setName("Graph")
.addVertexProperty("name",PropertyType.STRING)
.addVertexProperty("age",PropertyType.INTEGER)
.addEdgeProperty("weight",PropertyType.FLOAT)
.setLoadVertexLabels(true)
.setLoadEdgeLabel(true)
.build();
PgxGraph graph = session.readGraphWithProperties(cfg.get());
System.out.println("N = " + graph.getNumVertices()+ " <-> E = " + graph.getNumEdges());
graph.queryPgql("SELECT id(n), label(n),n.name as name1,n.age as age1,label(e), e.weight, id(m),label(m),m.name as name2,m.age as age2 FROM MATCH (n) -[e]-> (m)").print(10).close();
}
}
}
[oracle@cli oracle-graph-client-20.3.0] javac -cp 'lib/*' TokenConnect.java
warning: Supported source version 'RELEASE_8' from annotation processor 'org.apache.tinkerpop.gremlin.process.traversal.dsl.GremlinDslProcessor' less than -source '11'
1 warning
[oracle@cli oracle-graph-client-20.3.0] java -cp '.:conf:lib/*' TokenConnect *baseUrl *Token
WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by com.google.inject.internal.cglib.core.$ReflectUtils$1 (file:/home/opc/oracle-graph-client-20.3.0/lib/guice-4.2.2.jar) to method java.lang.ClassLoader.defineClass(java.lang.String,byte[],int,int,java.security.ProtectionDomain)
WARNING: Please consider reporting this to the maintainers of com.google.inject.internal.cglib.core.$ReflectUtils$1
WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release
N = 4 <-> E = 5
+---------------------------------------------------------------------------------------------+
| id(n) | label(n) | name1 | age1 | label(e) | weight | id(m) | label(m) | name2 | age2 |
+---------------------------------------------------------------------------------------------+
| 3 | person | Yamamoto | 35 | knows | 0.9 | 1 | person | Sato | 40 |
| 4 | person | Tanaka | 25 | knows | 0.7 | 3 | person | Yamamoto | 35 |
| 4 | person | Tanaka | 25 | likes | 0.8 | 2 | person | Suzuki | 20 |
| 1 | person | Sato | 40 | knows | 0.5 | 4 | person | Tanaka | 25 |
| 1 | person | Sato | 40 | knows | 0.5 | 2 | person | Suzuki | 20 |
+---------------------------------------------------------------------------------------------+
[oracle@cli oracle-graph-client-20.3.0]
しっかりとPGQLクエリの結果が返ってきました。
5. まとめ
- PGXを用いて自作グラフからクエリ検索しました。
- サーバをそれぞれ3層に分けてリモートで接続できることを確認できました。
- JshellとJavaの両方で使い分けできることも確認しました。
参考
- インストール手順の参考 ->
PGXのDocument