🥑

Graph DBを触ってみた: ArangoDB, Neo4j, Dgraphの比較

2024/02/26に公開

この記事では、Neo4j、ArangoDB、Dgraphの3つのGraph DBをDockerでサクッと動かし、Pythonからもサクッと動かしてみます。

各DBの特徴

Neo4j

古参Graph DB。Cypherでクエリを記述する。最近は生成AI向けに頑張ってる模様。

GraphDBのクエリ言語を見れば大体当てはまるが、CypherであればGraphデータを以下のように直感的に表現できる。

MATCH (p:Product)-[:CATEGORY]->(l:ProductCategory)-[:PARENT*0..]
->(:ProductCategory {name:"Dairy Products"})
RETURN p.name

SQLだとこんな感じ。一目瞭然ですね。

SELECT p.ProductName
FROM Product AS p
JOIN ProductCategory pc ON (p.CategoryID = pc.CategoryID 
AND pc.CategoryName = "Dairy Products")

JOIN ProductCategory pc1 ON (p.CategoryID = pc1.CategoryID)
JOIN ProductCategory pc2 ON (pc1.ParentID = pc2.CategoryID 
AND pc2.CategoryName = "Dairy Products")

JOIN ProductCategory pc3 ON (p.CategoryID = pc3.CategoryID)
JOIN ProductCategory pc4 ON (pc3.ParentID = pc4.CategoryID)
JOIN ProductCategory pc5 ON (pc4.ParentID = pc5.CategoryID 
AND pc5.CategoryName = "Dairy Products");

Pulseを見るとそこまで開発はアクティブではない。

ArangoDB

AQL(ArangoDB Query Language)を用いる。構造化データ、半構造化データ、および非構造化データを、スキーマフリーの JSON オブジェクトの形式で操作できる。柔軟性は高い。

以下はAQLでBooksコレクションからArangoDBのタイトルで検索し、関連するpersonを深さ2でグラフ探索している。

FOR book IN Books
  FILTER book.title == "ArangoDB"
  FOR person IN 2..2 INBOUND book Sales, OUTBOUND People
    RETURN person.name

GUIを見ると、ANALYZERSがあったりして機能も充実していそう。

Pulseを見ると、開発はかなりactive。

Dgraph

Dgraph は distributed graph database として設計されており、水平スケーリングに優れる。シャーディングされ、クラスタ内の複数のノードに分散される。GraphQLがベースでクエリを行う。

スキーマの定義から

type User {
  username: String! @id
  displayName: String
  avatarImg: String
  posts: [Post!]
  comments: [Comment!]
}


type Post {
  id: ID!
  title: String! @search(by: [term])
  text: String! @search(by: [fulltext])
  tags: String @search(by: [term])
  datePublished: DateTime
  author: User!  @hasInverse(field: posts)
  category: Category! @hasInverse(field: posts)
  comments: [Comment!]
}


type Comment {
  id: ID!
  text: String!
  commentsOn: Post! @hasInverse(field: comments)
  author: User! @hasInverse(field: comments)
}


type Category {
  id: ID!
  name: String! @search(by: [term])
  posts: [Post!]
}

mutation(やquery)を行う。

mutation {
  addUser(input: [
    { username: "User1" }
  ]) {
    user {
      username
      displayName
    }
  }
  addCategory(input: [
    { name: "Category1",
      posts : [
    {
      title: "Post1",
      text: "Post1",
      author: { username: "User1" }
    }, 
    {
      title: "Post2",
      text: "Post2",
      author: { username: "User1" }
    },
    {
      title: "Post3",
      text: "Post3",
      author: { username: "User1" }
    }   
    ]
    }
  ]) {
    category {
      id
      name
      posts { text }
    }
    
  }
  
}

Pulseを見ると開発はactiveではなさそう。

DockerでGraph DBを動かす

Neo4jのセットアップ

1. DockerでNeo4jを動かす。

docker run \
    --name neo4j \
    -p7474:7474 -p7687:7687 \
    -d \
    --env NEO4J_AUTH=neo4j/password \
    neo4j:latest

2. GUI へアクセスします。

  • user/password は neo4j/password です。

ArangoDBのセットアップ

1. DockerでArangoDBを動かす。

docker run -e ARANGO_ROOT_PASSWORD=password -p 8529:8529 -d arangodb/arangodb:latest

2. GUIへアクセスします。

  • root/password でログインします。

Dgraphのセットアップ

1. DockerでDgraphを動かす。

docker run -it -p 8080:8080 -p 9080:9080 -d dgraph/standalone:latest

2. GUIへアクセスします。

PythonからGraph DBを操作する

今回は2つのノードと1つのエッジで構成される以下のグラフデータを作成します。

Neo4jでの操作

Neo4jとの連携にはneo4jパッケージを使用します。

1. 接続とDBの作成

from neo4j import GraphDatabase

driver = GraphDatabase.driver("bolt://localhost:7687", auth=("neo4j", "password"))

2. nodeとrelationshipの追加

def create_and_link_nodes(tx):
    query = (
        "CREATE (a:Person {name: 'Alice'}) "
        "CREATE (b:Person {name: 'Bob'}) "
        "CREATE (a)-[:LIKES]->(b)"
    )
    tx.run(query)

def create_friendship():
    with driver.session() as session:
        session.write_transaction(create_and_link_nodes)

# グラフの作成を実行
create_friendship()

3. Neo4jのGUIへログインしてグラフを見てみる。

ArangoDBでの操作

ArangoDBとの連携にはpython-arangoパッケージを使用します。

1. 接続とDBの作成

from arango import ArangoClient

# ArangoDBサーバーへの接続を初期化
client = ArangoClient(hosts='http://localhost:8529')

# データベースに接続(存在しない場合は作成)
sys_db = client.db('_system', username='root', password='password')
if not sys_db.has_database('mydb'):
    sys_db.create_database('mydb')

db = client.db('mydb', username='root', password='password')

2. コレクションを作成

if not db.has_collection('users'):
    users = db.create_collection('users')

if not db.has_collection('likes'):
    likes = db.create_collection('likes', edge=True)

3. ノードとエッジをコレクションに追加

alice = users.insert({'_key': 'Alice', 'name': 'Alice'})
bob = users.insert({'_key': 'Bob', 'name': 'Bob'})

likes.insert({'_from': alice['_id'], '_to': bob['_id'], 'relation': 'like'})

4. ArangoDBのGUIへmydbでログインしてグラフを見てみる。

Dgraphでの操作

Dgraphとの連携にはpydgraphパッケージを使用します。

1. 接続とDB(mydb)の作成

import pydgraph

client_stub = pydgraph.DgraphClientStub('localhost:9080')
client = pydgraph.DgraphClient(client_stub)

2. スキーマの設定

# スキーマの設定
schema = """
name: string @index(exact) .
type Person {
    name
}
"""
client.alter(pydgraph.Operation(schema=schema))

3. ノードとエッジの作成

txn = client.txn()
try:
    # ノードの作成
    mutation = pydgraph.Mutation(set_nquads='''
        _:alice <name> "Alice" .
        _:bob <name> "Bob" .
        _:charlie <name> "Charlie" .
        _:alice <likes> _:bob .
    ''')
    txn.mutate(mutation)
    txn.commit()
finally:
    txn.discard()

4. DgraphのGUI(ratel)へログインしてグラフを見てみる。

Reference

Discussion