Upgrade to Pro — share decks privately, control downloads, hide ads and more …

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)

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  8. 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

    View full-size slide

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

    グラフ
    拡張
    機能

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  13. 拡張機能の登録
    ● 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)

    View full-size slide

  14. 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'

    View full-size slide

  15. グラフの管理

    View full-size slide

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

    View full-size slide

  17. グラフの削除
    ● 作成したグラフを削除することも可能
    – 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)

    View full-size slide

  18. Cypher 関数

    View full-size slide

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

    View full-size slide

  20. Vertext と Edge の作成

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  24. -- 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 の作成は
    シンプル

    View full-size slide

  25. -- 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 を作成する

    View full-size slide

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

    View full-size slide

  27. 検索
    ● 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)

    View full-size slide

  28. 検索( 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
    にゃーん・・・

    View full-size slide

  29. 検索(属性を返却)
    ● 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)

    View full-size slide

  30. 検索(関連を使った 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)

    View full-size slide

  31. 検索(関連を使った 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)

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  36. 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
    $

    View full-size slide

  37. ロード用 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');

    View full-size slide

  38. ロード用 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
    $
    作成件数とか
    返却してほしい

    View full-size slide

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

    View full-size slide

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

    users ユーザの情報 239,191
    comments レビューに対するコメントの情報 1,812,666

    View full-size slide

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

    View full-size slide

  42. 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;

    View full-size slide

  43. 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;

    View full-size slide

  44. ロード用 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');

    View full-size slide

  45. 大量データのロード時間
    ● 各ファイルの行数とロード処理時間
    種別 ファイル名 ラベル名 行数 ファイルサイズ 処理時間 (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 ファイルだと
    ロードに失敗
    したので分割

    View full-size slide

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

    View full-size slide

  47. 検索の例
    ● 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 は
    実用だと厳しい?

    View full-size slide

  48. 品質はまだ不安・・・
    ● 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.
    =?>
    にゃーん・・・

    View full-size slide

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

    View full-size slide

  50. おしまい

    View full-size slide