$30 off During Our Annual Pro Sale. View Details »

Apache AGE

nuko_yokohama
September 09, 2022

Apache AGE

Description of the "Apache AGE" extension that enables the use of graph data model on PostgreSQL.

nuko_yokohama

September 09, 2022
Tweet

More Decks by nuko_yokohama

Other Decks in Technology

Transcript

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

  2. 自己紹介 • ぬこ@横浜 , @nuko_yokohama • にゃーん • 趣味でポスグレをやってる者だ •

    拡張機能、私の好きな言葉です
  3. 今日のお題 • グラフデータベースって? • Apache AGE • インストール • グラフの管理

    • Cypher 関数 • Vertex と Edge の作成 • 検索 • 大量データのロード(基本編) • 大量データのロード(応用編) • まとめ
  4. グラフデータベースって?

  5. グラフデータベースの雑な説明 • NoSQL の一種 • ノード、エッジ(関連)、プロパティの三要素 ノード プロパティ ノード プロパティ

    エッジ • グラフデータベースの実装製品は結構多い。 – https://en.wikipedia.org/wiki/Graph_database#List_of_graph_databases プロパティ 以前は Neo4j を 使ってました。
  6. グラフデータベースの適用領域 • ソーシャルグラフ • レコメンデーション • 経路探索 • ネットワーク管理 •

    不正検知 • などなど 自分は ソーシャルグラフに 興味があって グラフデータベースを 使い始めました
  7. グラフデータベース &PostgreSQL • 以前からグラフデータベース& PostgreSQL には興味があった – neo4j_fdw の実装 •

    Neo4j への検索結果を PostgreSQL に返す FDW – ロジカルデーコーディング& Neo4j • PostgreSQL の論理 WAL を Neo4j に適用する スクリプトの実装
  8. Apache AGE

  9. 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
  10. Apache AGE とは • 目指しているもの – PostgreSQL の既存のリレーショナルモデルと一体となって グラフモデルを使用できるようにすること SQL

    SQL データベース 表 グラフ 拡張 機能
  11. ロードマップ • 対応 PostgreSQL バージョン – 現在は PostgreSQL 11 に対応

    – 第 3 四半期に PostgreSQL 12 と 13 に対応 – 2022 年度第 4 四半期に PostgreSQL 14 に対応 • 言語 – openCypher グラフ問い合わせ言語 • AgensGraph (未調査) – PostgreSQL フォーク+ AGE のフル機能サポート
  12. インストール&拡張機能の登録

  13. インストール • 現状、パッケージの提供等はない。 • ソースコードからのビルドが必要 – 現状は PostgreSQL 11 環境でないとビルドに失敗する。

    • 他の拡張機能と同様に、 make, make install
  14. 拡張機能の登録 • 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)
  15. 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'
  16. グラフの管理

  17. グラフの作成 • Vertex と Edge を作成する場所を作る – PostgreSQL で言えばテーブルを作成する前にスキーマを 作るようなイメージ

    – create_graph( グラフ名 ) • 作成時に指定した「グラフ名」はグラフ操作の関数で使う。 Age ではノードを Vertex と呼ぶ =# SELECT create_graph('sample'); NOTICE: graph "sample" has been created create_graph -------------- (1 row)
  18. グラフの削除 • 作成したグラフを削除することも可能 – 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)
  19. Cypher 関数

  20. Cypher 関数 • Age の機能は Cypher 関数で実行する SELECT * FROM

    cypher(<graph_name>, $$ /* Cypher Query Here */ $$) AS (result agtype [,resule agtype]... ); • セッション開始時に LOAD ‘age’ しておくこと。 • <graph_name> は作成したグラフ名 • /* Cypher Query Here */ – ここに OpenCypher というグラフ操作言語を書く – 作成、検索、更新、削除
  21. Vertext と Edge の作成

  22. Vertext と Edge の作成 • 作成順序 – まず Vertex から作成

    – 2 つ以上の Vertex を指定して Edge を作る Vertex Edge Vertex Step-1 Step-2 Step-3 Edge のない Vertex は OK 。 両端に Vertex のない Edge は NG 。
  23. Vertext と Edge の作成 • Vertex – CREATE コマンド •

    Edge – MATCH と CREATE の組み合わせ CREATE( :label {prop-name:prop-value} ) MATCH <Vertex>,<Vertex> WHERE condition CREATE (Vertex)-[ :RELTYPE {prop-name:prop-value} ]-(Vertex) 実際の例を 見たほうが 早いかも
  24. サンプル high high mid high high high high low mid

    mid 愛憎乱れる 人間関係・・・
  25. -- 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 の作成は シンプル
  26. -- 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 を作成する
  27. 検索

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

    mid
  29. 検索 • 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)
  30. 検索( 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 にゃーん・・・
  31. 検索(属性を返却) • 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)
  32. 検索(関連を使った 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)
  33. 検索(関連を使った 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)
  34. 大量データのロード(基本編)

  35. ロード用の関数 • ラベル作成関数 – create_vlabel() – create_elabel() • ロード用の関数 –

    load_labels_from_file() – load_edges_from_file()
  36. ロード用 CSV ファイルの形式 • Vertex 用 – 先頭行に列名を書く – id

    のみ固定。あとは任意 • Edge 用 – 先頭行に列名を書く – start_id,start_vertex_type,end_id,end_vertex_type まで固定 – 以降の列は任意
  37. Vertex ロード用 CSV ファイルの例 • Vertex 用 $ cat persons.csv

    id,name,gender 101,Alice,Female 102,Bob,Male 103,Casey,Female 104,Dave,Male 105,Erin,Female
  38. 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 $
  39. ロード用 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');
  40. ロード用 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 $ 作成件数とか 返却してほしい
  41. 大量データのロード(応用編)

  42. モデル • 自分の手元には「たまたま」某ラーメンデータベースの スクレイピングデータがあったりする。 テーブル名 内容 件数 shops 店舗情報 126,760

    reviews ラーメン等へのレビュー情報 1,118,022 reviews_text レビューのテキスト。 今回は未使用 ー users ユーザの情報 239,191 comments レビューに対するコメントの情報 1,812,666
  43. ラーメンデータベース用グラフモデル

  44. 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;
  45. 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;
  46. ロード用 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');
  47. 大量データのロード時間 • 各ファイルの行数とロード処理時間 種別 ファイル名 ラベル名 行数 ファイルサイズ 処理時間 (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 ファイルだと ロードに失敗 したので分割
  48. 大量データのロード時間 • データロード用のファイルサイズと処理時間は関連している 0 10000000 20000000 30000000 40000000 50000000 60000000

    0 1000 2000 3000 4000 5000 6000 7000 8000 ファイルサイズとロード処理時間 ファイルサイズ (bye) ロード処理時間 (ms) 6MB/ 秒か・・・ ちょい遅いかな。
  49. 検索の例 • 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 は 実用だと厳しい?
  50. 品質はまだ不安・・・ • 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. =?> にゃーん・・・
  51. まとめ

  52. まとめ • Apache AGE はグラフデータベースを PostgreSQL 上で実現する拡張機能 • たのしい •

    性能・品質は現段階では改善が必要そう • 今後も期待したい拡張機能 RDBMS +グラフの 高まりを感じる
  53. おしまい