Slide 1

Slide 1 text

Apache AGE PostgreSQL Unconferecnce #35 (2022-09-09)

Slide 2

Slide 2 text

自己紹介 ● ぬこ@横浜 , @nuko_yokohama ● にゃーん ● 趣味でポスグレをやってる者だ ● 拡張機能、私の好きな言葉です

Slide 3

Slide 3 text

今日のお題 ● グラフデータベースって? ● Apache AGE ● インストール ● グラフの管理 ● Cypher 関数 ● Vertex と Edge の作成 ● 検索 ● 大量データのロード(基本編) ● 大量データのロード(応用編) ● まとめ

Slide 4

Slide 4 text

グラフデータベースって?

Slide 5

Slide 5 text

グラフデータベースの雑な説明 ● NoSQL の一種 ● ノード、エッジ(関連)、プロパティの三要素 ノード プロパティ ノード プロパティ エッジ ● グラフデータベースの実装製品は結構多い。 – https://en.wikipedia.org/wiki/Graph_database#List_of_graph_databases プロパティ 以前は Neo4j を 使ってました。

Slide 6

Slide 6 text

グラフデータベースの適用領域 ● ソーシャルグラフ ● レコメンデーション ● 経路探索 ● ネットワーク管理 ● 不正検知 ● などなど 自分は ソーシャルグラフに 興味があって グラフデータベースを 使い始めました

Slide 7

Slide 7 text

グラフデータベース &PostgreSQL ● 以前からグラフデータベース& PostgreSQL には興味があった – neo4j_fdw の実装 ● Neo4j への検索結果を PostgreSQL に返す FDW – ロジカルデーコーディング& Neo4j ● PostgreSQL の論理 WAL を Neo4j に適用する スクリプトの実装

Slide 8

Slide 8 text

Apache AGE

Slide 9

Slide 9 text

Apache AGE とは ● グラフデータベース機能を提供する PostgreSQL の拡張機能 – https://age.apache.org/ – Apache Licence 2.0 で提供 ● ソースコード – https://github.com/apache/age ● ドキュメント – https://age.apache.org/age-manual/master/index.html

Slide 10

Slide 10 text

Apache AGE とは ● 目指しているもの – PostgreSQL の既存のリレーショナルモデルと一体となって グラフモデルを使用できるようにすること SQL SQL データベース 表 グラフ 拡張 機能

Slide 11

Slide 11 text

ロードマップ ● 対応 PostgreSQL バージョン – 現在は PostgreSQL 11 に対応 – 第 3 四半期に PostgreSQL 12 と 13 に対応 – 2022 年度第 4 四半期に PostgreSQL 14 に対応 ● 言語 – openCypher グラフ問い合わせ言語 ● AgensGraph (未調査) – PostgreSQL フォーク+ AGE のフル機能サポート

Slide 12

Slide 12 text

インストール&拡張機能の登録

Slide 13

Slide 13 text

インストール ● 現状、パッケージの提供等はない。 ● ソースコードからのビルドが必要 – 現状は PostgreSQL 11 環境でないとビルドに失敗する。 ● 他の拡張機能と同様に、 make, make install

Slide 14

Slide 14 text

拡張機能の登録 ● shared_preload_libraries の設定は不要。 ● Apache AGE を組み込みたいデータベースに CREATE EXTENSION で登録する。 $ psql sample -c "CREATE EXTENSION age" CREATE EXTENSION $ psql sample -c "\dx" List of installed extensions Name | Version | Schema | Description ---------+---------+------------+------------------------------ age | 1.1.0 | ag_catalog | AGE database extension plpgsql | 1.0 | pg_catalog | PL/pgSQL procedural language (2 rows)

Slide 15

Slide 15 text

search_path の設定 ● Apache AGE 登録後のスキーマ ● ag_catalog スキーマへの search_path 設定 $ psql sample -c "\dn" List of schemas Name | Owner ------------+---------- ag_catalog | postgres public | postgres (2 rows) search_path = 'ag_catalog, "$user", public'

Slide 16

Slide 16 text

グラフの管理

Slide 17

Slide 17 text

グラフの作成 ● Vertex と Edge を作成する場所を作る – PostgreSQL で言えばテーブルを作成する前にスキーマを 作るようなイメージ – create_graph( グラフ名 ) ● 作成時に指定した「グラフ名」はグラフ操作の関数で使う。 Age ではノードを Vertex と呼ぶ =# SELECT create_graph('sample'); NOTICE: graph "sample" has been created create_graph -------------- (1 row)

Slide 18

Slide 18 text

グラフの削除 ● 作成したグラフを削除することも可能 – drop_graph( グラフ名 , フラグ ) – 指定したグラフ内の Vertex と Edge も一緒に消える。 =# SELECT drop_graph('sample', true); NOTICE: drop cascades to 5 other objects DETAIL: drop cascades to table sample._ag_label_vertex drop cascades to table sample._ag_label_edge drop cascades to table sample."Person" drop cascades to table sample."Like" drop cascades to table sample."Dislike" NOTICE: graph "sample" has been dropped drop_graph ------------ (1 row)

Slide 19

Slide 19 text

Cypher 関数

Slide 20

Slide 20 text

Cypher 関数 ● Age の機能は Cypher 関数で実行する SELECT * FROM cypher(, $$ /* Cypher Query Here */ $$) AS (result agtype [,resule agtype]... ); ● セッション開始時に LOAD ‘age’ しておくこと。 ● は作成したグラフ名 ● /* Cypher Query Here */ – ここに OpenCypher というグラフ操作言語を書く – 作成、検索、更新、削除

Slide 21

Slide 21 text

Vertext と Edge の作成

Slide 22

Slide 22 text

Vertext と Edge の作成 ● 作成順序 – まず Vertex から作成 – 2 つ以上の Vertex を指定して Edge を作る Vertex Edge Vertex Step-1 Step-2 Step-3 Edge のない Vertex は OK 。 両端に Vertex のない Edge は NG 。

Slide 23

Slide 23 text

Vertext と Edge の作成 ● Vertex – CREATE コマンド ● Edge – MATCH と CREATE の組み合わせ CREATE( :label {prop-name:prop-value} ) MATCH , WHERE condition CREATE (Vertex)-[ :RELTYPE {prop-name:prop-value} ]-(Vertex) 実際の例を 見たほうが 早いかも

Slide 24

Slide 24 text

サンプル high high mid high high high high low mid mid 愛憎乱れる 人間関係・・・

Slide 25

Slide 25 text

-- create Person Vertex SELECT * FROM cypher('sample', $$ CREATE p = ( :Person {id: 101, name:'Alice', gender:'Female'} ) RETURN p $$) as (p agtype); SELECT * FROM cypher('sample', $$ CREATE p = ( :Person {id: 102, name:'Bob', gender:'Male'} ) RETURN p $$) as (p agtype); SELECT * FROM cypher('sample', $$ CREATE p = ( :Person {id: 103, name:'Casey', gender:'Female'} ) RETURN p $$) as (p agtype); SELECT * FROM cypher('sample', $$ CREATE p = ( :Person {id: 104, name:'Dave', gender:'Male'} ) RETURN p $$) as (p agtype); SELECT * FROM cypher('sample', $$ CREATE p = ( :Person {id: 105, name:'Erin', gender:'Female'} ) RETURN p $$) as (p agtype); サンプルの Vertex 作成 Vertex の作成は シンプル

Slide 26

Slide 26 text

-- Create Edge -- From Alice SELECT * FROM cypher('sample', $$ MATCH (a:Person), (b:Person) WHERE a.name = 'Alice' AND b.name = 'Bob' CREATE (a)-[e:Like{level: 'high'}]->(b) RETURN e $$) as (p agtype); SELECT * FROM cypher('sample', $$ MATCH (a:Person), (b:Person) WHERE a.name = 'Alice' AND b.name = 'Erin' CREATE (a)-[e:Dislike{level: 'high'}]->(b) RETURN e $$) as (p agtype); サンプルの Edge 作成(抜粋: Alice 起点) MATCH や WHERE で a,b の Vertex を特定して その間に Edge を作成する

Slide 27

Slide 27 text

検索

Slide 28

Slide 28 text

サンプル(再掲) high high mid high high high high low mid mid

Slide 29

Slide 29 text

検索 ● Person ノード全てを返却する =# SELECT * FROM cypher('sample', $$ MATCH (a:Person) RETURN a $$ ) as ("Person" agtype); Person ---------------------------------------------------------------------------------------------------------- ---------- {"id": 844424930131969, "label": "Person", "properties": {"id": 101, "name": "Alice", "gender": "Female"}}::vertex {"id": 844424930131970, "label": "Person", "properties": {"id": 102, "name": "Bob", "gender": "Male"}}::vertex {"id": 844424930131971, "label": "Person", "properties": {"id": 103, "name": "Casey", "gender": "Female"}}::vertex {"id": 844424930131972, "label": "Person", "properties": {"id": 104, "name": "Dave", "gender": "Male"}}::vertex {"id": 844424930131973, "label": "Person", "properties": {"id": 105, "name": "Erin", "gender": "Female"}}::vertex (5 rows)

Slide 30

Slide 30 text

検索( agtype->jsonb ) ● agtype(vertex) は JSON ぽく見えるが、現状の版では JSON/JSONB/TEXT への型変換はできない・・・ – バグなのか仕様なのか・・・(要調査) – 型変換できれば JSON/JSONB 関数で処理もできるのに。 =# SELECT "Person"::jsonb FROM cypher('sample', $$ MATCH (a:Person) RETURN a $$ ) as ("Person" agtype); ERROR: cannot cast type agtype to jsonb LINE 1: SELECT "Person"::jsonb FROM cypher('sample', $$ ^ =# SELECT ("Person"::text) FROM cypher('sample', $$ MATCH (a:Person) RETURN a $$ ) as ("Person" agtype); ERROR: agtype_value_to_text: unsupported argument agtype 6 にゃーん・・・

Slide 31

Slide 31 text

検索(属性を返却) ● Person ノードの name, gender 属性を返却 =# SELECT * FROM cypher('sample', $$ MATCH (a:Person) RETURN a.name, a.gender $$ ) as ("name" text, "gender" text); name | gender -------+-------- Alice | Female Bob | Male Casey | Female Dave | Male Erin | Female (5 rows)

Slide 32

Slide 32 text

検索(関連を使った MATCH ) ● 好き (Like) で結ばれた Person の name と好き度合い (level) =# SELECT * FROM cypher('sample', $$ MATCH (a:Person)-[e:Like]->(b:Person) RETURN a.name, e.level, b.name $$ ) as ("from" text, "level" text, "to" text); from | level | to -------+-------+------- Alice | high | Bob Bob | high | Erin Casey | high | Dave Dave | high | Alice Erin | mid | Dave Erin | mid | Alice (6 rows)

Slide 33

Slide 33 text

検索(関連を使った MATCH ) ● Erin(a) の好きな人 (b) が好きなのは誰 (c) ? =# SELECT * FROM cypher('sample', $$ MATCH (a:Person)-[l:Like]->(b:Person)-[ll:Like]->(c:Person) WHERE a.name = 'Erin' RETURN a.name, b.name, ll.level, c.name $$ ) as ("a" text, "b" text, "level" text, "c" text); a | b | level | c ------+-------+-------+------- Erin | Dave | high | Alice Erin | Alice | high | Bob (2 rows)

Slide 34

Slide 34 text

大量データのロード(基本編)

Slide 35

Slide 35 text

ロード用の関数 ● ラベル作成関数 – create_vlabel() – create_elabel() ● ロード用の関数 – load_labels_from_file() – load_edges_from_file()

Slide 36

Slide 36 text

ロード用 CSV ファイルの形式 ● Vertex 用 – 先頭行に列名を書く – id のみ固定。あとは任意 ● Edge 用 – 先頭行に列名を書く – start_id,start_vertex_type,end_id,end_vertex_type まで固定 – 以降の列は任意

Slide 37

Slide 37 text

Vertex ロード用 CSV ファイルの例 ● Vertex 用 $ cat persons.csv id,name,gender 101,Alice,Female 102,Bob,Male 103,Casey,Female 104,Dave,Male 105,Erin,Female

Slide 38

Slide 38 text

Edge ロード用 CSV ファイルの例 ● Edge 用 $ cat like-edge.csv start_id,start_vertex_type,end_id,end_vertex_type,level 101,Person,102,Person,high 102,Person,105,Person,high 103,Person,104,Person,high 104,Person,101,Person,high 105,Person,104,Person,mid 105,Person,101,Person,mid $ cat dislike-edge.csv start_id,start_vertex_type,end_id,end_vertex_type,level 101,Person,105,Person,high 102,Person,104,Person,mid 103,Person,101,Person,high 104,Person,102,Person,low $

Slide 39

Slide 39 text

ロード用 SQL スクリプトの例 ● ラベル作成→ Vertex 作成→ラベル作成→ Edge 作成 $ cat load.sql -- drop labels SELECT drop_label('sample','Person'); SELECT drop_label('sample','Like'); SELECT drop_label('sample','Dislike'); -- create node label SELECT create_vlabel('sample','Person'); -- load Person nodes. SELECT load_labels_from_file('sample', 'Person', '/tmp/persons.csv'); -- create edge label SELECT create_elabel('sample','Like'); SELECT create_elabel('sample','Dislike'); SELECT load_edges_from_file('sample', 'Like', '/tmp/like-edge.csv'); SELECT load_edges_from_file('sample', 'Dislike', '/tmp/dislike-edge.csv');

Slide 40

Slide 40 text

ロード用 SQL スクリプトの実行例 ● 実行例(抜粋) SELECT load_labels_from_file('sample', 'Person', '/tmp/persons.csv'); load_labels_from_file ----------------------- (1 row) Time: 0.931 ms Time: 4.223 ms SELECT load_edges_from_file('sample', 'Like', '/tmp/like-edge.csv'); load_edges_from_file ---------------------- (1 row) Time: 1.125 ms SELECT load_edges_from_file('sample', 'Dislike', '/tmp/dislike-edge.csv'); load_edges_from_file ---------------------- (1 row) Time: 0.826 ms $ 作成件数とか 返却してほしい

Slide 41

Slide 41 text

大量データのロード(応用編)

Slide 42

Slide 42 text

モデル ● 自分の手元には「たまたま」某ラーメンデータベースの スクレイピングデータがあったりする。 テーブル名 内容 件数 shops 店舗情報 126,760 reviews ラーメン等へのレビュー情報 1,118,022 reviews_text レビューのテキスト。 今回は未使用 ー users ユーザの情報 239,191 comments レビューに対するコメントの情報 1,812,666

Slide 43

Slide 43 text

ラーメンデータベース用グラフモデル

Slide 44

Slide 44 text

Vertex ロード用 CSV ファイルを SQL で生成 ● Vertex はテーブルへの検索結果を CSV 化すれば OK -- extract node data -- extract shop CSV COPY (SELECT sid, status, name, branch, pref, area FROM shops ORDER BY sid) TO '/tmp/shop.csv' CSV; -- extract user CSV COPY (SELECT uid, name FROM users ORDER BY uid) TO '/tmp/user.csv' CSV; -- extract review CSV COPY (SELECT rid, menu, score, category, type, soup, likes, reg_date FROM reviews ) TO '/tmp/review.csv' CSV;

Slide 45

Slide 45 text

Edge ロード用 CSV ファイルを SQL で生成 ● Edge は Vertex 元を特定できるテーブル / カラムを指定して 両端の Vertex の label と id を CSV 化する。 ● 必要に応じて JOIN もする。 -- extract edge data -- extract Rview-[eval]->Shop CSV COPY (SELECT r.rid, 'Review', r.sid, 'Shop' FROM reviews r ORDER BY rid ) TO '/tmp/eval-edge.csv' CSV; -- extract User-[write]->Review CSV COPY (SELECT r.uid, 'User', r.rid, 'Review' FROM reviews r ORDER BY rid ) TO '/tmp/write-edge.csv' CSV; -- extract User-[comment]->Review CSV COPY (SELECT c.comment_uid, 'User', c.rid, 'Review' FROM reviews r JOIN comments c ON (r.rid = c.rid) ORDER BY r.rid ) TO '/tmp/comment-edge.csv' CSV;

Slide 46

Slide 46 text

ロード用 SQL スクリプトの例 ● ロード用スクリプト -- drop graph \timing SELECT drop_graph('ramendb',true); SELECT create_graph('ramendb'); -- create node label SELECT create_vlabel('ramendb','Shop'); SELECT create_vlabel('ramendb','Review'); SELECT create_vlabel('ramendb','User'); -- load Person nodes. SELECT load_labels_from_file('ramendb', 'Shop', '/tmp/shop.csv'); SELECT load_labels_from_file('ramendb', 'Review', '/tmp/review-1.csv'); SELECT load_labels_from_file('ramendb', 'Review', '/tmp/review-2.csv'); SELECT load_labels_from_file('ramendb', 'User', '/tmp/user.csv'); -- create edge label SELECT create_elabel('ramendb','eval'); SELECT create_elabel('ramendb','write'); SELECT create_elabel('ramendb','comment'); SELECT load_edges_from_file('ramendb', 'eval', '/tmp/eval-edge.csv'); SELECT load_edges_from_file('ramendb', 'write', '/tmp/write-edge.csv'); SELECT load_edges_from_file('ramendb', 'comment', '/tmp/comment-edge.csv');

Slide 47

Slide 47 text

大量データのロード時間 ● 各ファイルの行数とロード処理時間 種別 ファイル名 ラベル名 行数 ファイルサイズ 処理時間 (ms) Vertex shop.csv shop 126,956 7,824,506 772.035 Vertex reviews-1.csv review 623,660 55,690,479 6858.148 Vertex reviews-1.csv review 496,509 45,108,957 5168.257 Vertex user.csv user 239,191 4,599,858 2126.057 Edge eval-edge.csv eval 1,120,168 28,180,164 4198.854 Edge write-edge.csv write 1,120,168 28,684,956 4221.373 Edge comment-edge.csv comment 1,803,158 46,126,517 7555.221 1 ファイルだと ロードに失敗 したので分割

Slide 48

Slide 48 text

大量データのロード時間 ● データロード用のファイルサイズと処理時間は関連している 0 10000000 20000000 30000000 40000000 50000000 60000000 0 1000 2000 3000 4000 5000 6000 7000 8000 ファイルサイズとロード処理時間 ファイルサイズ (bye) ロード処理時間 (ms) 6MB/ 秒か・・・ ちょい遅いかな。

Slide 49

Slide 49 text

検索の例 ● 7 月にぬこが 80 点以上つけたラーメンレビューのお店と レビューにコメントしてくれた人を検索 =# SELECT * FROM cypher('ramendb', $$ MATCH (u:User{id:'8999'})-[w:write]->(r:Review)<-[comment]-(cu:User),(r)-[eval]->(s:Shop) WHERE r.reg_date >= '2022-07-01' AND r.reg_date <= '2022-07-31' AND r.score >= '80' AND r.category = ' ラーメン ' RETURN s.area, r.menu, cu.id $$ ) as ("area" text, "menu" text, "comment-user id" text); area | menu | comment-user id --------------+----------------------------------+----------------- 横浜市緑区 | 伊吹いりこの塩煮干し | 56 横浜市緑区 | 伊吹いりこの塩煮干し | 44254 (略) 横浜市南区 | 豚骨ラーメン固め+替玉・バリカタ | 194811 横浜市南区 | 豚骨ラーメン固め+替玉・バリカタ | 155687 (略) 横浜市金沢区 | 豚バラもやキャベラーメン・太麺 | 56 横浜市金沢区 | 豚バラもやキャベラーメン・太麺 | 11736 (略) 平塚市 | ラーメン | 155300 平塚市 | ラーメン | 56 (略) (32 rows) Time: 3388.601 ms (00:03.389) 3000ms は 実用だと厳しい?

Slide 50

Slide 50 text

品質はまだ不安・・・ ● WITH, ORDER BY まわりは記述の制約も多いし、 クエリを投げるとクラッシュしたりする・・・ =# SELECT * FROM cypher('ramendb', $$ MATCH (u:User)-[w:write]->(r:Review)<-[comment]-(cu:User),(r)-[eval]->(s:Shop) WITH s, r ORDER BY s.id WHERE r.reg_date >= '2022-01-01' AND r.reg_date <= '2022-06-30' AND s.name = ' ゆで太郎 ' AND r.category = ' そば ' RETURN s.id, r.menu, u.id, cu.id $$ ) as ("area" text, "menu" text, "user id" text, "comment-user id" text); server closed the connection unexpectedly This probably means the server terminated abnormally before or while processing the request. The connection to the server was lost. Attempting reset: Failed. =?> にゃーん・・・

Slide 51

Slide 51 text

まとめ

Slide 52

Slide 52 text

まとめ ● Apache AGE はグラフデータベースを PostgreSQL 上で実現する拡張機能 ● たのしい ● 性能・品質は現段階では改善が必要そう ● 今後も期待したい拡張機能 RDBMS +グラフの 高まりを感じる

Slide 53

Slide 53 text

おしまい