カレー情報のRDFデータを使用したNeo4j知識グラフ構築チュートリアル

## はじめに

本チュートリアルでは、カレーに関する情報をRDFデータとして作成し、それをNeo4jの知識グラフに変換する方法を学びます。RDFデータの作成から始めることで、データモデリングの基礎から実際のグラフデータベースへの変換まで、一貫したプロセスを体験できます。

![napkin-selection (34).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648069/5bf6bc79-ef27-4126-acfc-d79c1859fb1f.png)

## 目次

1. カレー情報のRDFデータの作成
2. 必要なツールのインストール
3. RDFデータの読み込みと解析
4. Neo4jデータベースの準備
5. RDFデータのNeo4jへの変換
6. 結果の確認とクエリ

## 1. カレー情報のRDFデータの作成

まず、カレーに関する簡単なRDFデータを作成します。以下の内容を`curry.ttl`というファイル名で保存してください。このファイルはTurtle形式で記述されています。

```turtle
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
@prefix curry: <http://example.org/curry/> .

curry:JapaneseCurry rdf:type curry:CurryType ;
    rdfs:label "Japanese Curry"@en ;
    curry:origin "Japan" ;
    curry:popularDish curry:KatsuCurry, curry:CurryUdon .

curry:IndianCurry rdf:type curry:CurryType ;
    rdfs:label "Indian Curry"@en ;
    curry:origin "India" ;
    curry:popularDish curry:ButterChicken, curry:TikkaMasala .

curry:ThaiCurry rdf:type curry:CurryType ;
    rdfs:label "Thai Curry"@en ;
    curry:origin "Thailand" ;
    curry:popularDish curry:GreenCurry, curry:MassamanCurry .

curry:KatsuCurry rdf:type curry:Dish ;
    rdfs:label "Katsu Curry"@en ;
    curry:mainIngredient "Pork cutlet" .

curry:CurryUdon rdf:type curry:Dish ;
    rdfs:label "Curry Udon"@en ;
    curry:mainIngredient "Udon noodles" .

curry:ButterChicken rdf:type curry:Dish ;
    rdfs:label "Butter Chicken"@en ;
    curry:mainIngredient "Chicken" .

curry:TikkaMasala rdf:type curry:Dish ;
    rdfs:label "Tikka Masala"@en ;
    curry:mainIngredient "Chicken" .

curry:GreenCurry rdf:type curry:Dish ;
    rdfs:label "Green Curry"@en ;
    curry:mainIngredient "Vegetables" .

curry:MassamanCurry rdf:type curry:Dish ;
    rdfs:label "Massaman Curry"@en ;
    curry:mainIngredient "Beef" .
```

このRDFデータは、3つのカレータイプ（日本、インド、タイ）と、それぞれに関連する代表的な料理を記述しています。

## 2. 必要なツールのインストール

以下のツールをインストールしてください：

1. Python 3.x
2. RDFlib: `pip install rdflib`
3. Neo4j Python Driver: `pip install neo4j`
4. Neo4jデータベース（ローカルまたはクラウド版）

## 3. RDFデータの読み込みと解析

以下のPythonスクリプトを使用して、RDFデータを読み込み、解析します。

```python
from rdflib import Graph, Namespace, RDF, RDFS

# RDFグラフの読み込み
g = Graph()
g.parse("curry.ttl", format="turtle")

# 名前空間の定義
CURRY = Namespace("http://example.org/curry/")

# トリプルの出力
print("RDFデータの内容:")
for subj, pred, obj in g:
    print(f"Subject: {subj}, Predicate: {pred}, Object: {obj}")

# カレータイプの数を表示
curry_types = list(g.subjects(predicate=RDF.type, object=CURRY.CurryType))
print(f"\n総カレータイプ数: {len(curry_types)}")

# カレータイプとその原産国を表示
print("\nカレータイプと原産国:")
for curry_type in curry_types:
    label = g.value(subject=curry_type, predicate=RDFS.label)
    origin = g.value(subject=curry_type, predicate=CURRY.origin)
    print(f"{label}: {origin}")

# 各カレータイプの代表的な料理を表示
print("\n代表的な料理:")
for curry_type in curry_types:
    label = g.value(subject=curry_type, predicate=RDFS.label)
    dishes = list(g.objects(subject=curry_type, predicate=CURRY.popularDish))
    dish_names = [g.value(subject=dish, predicate=RDFS.label) for dish in dishes]
    print(f"{label}: {', '.join(dish_names)}")
```

このスクリプトを実行すると、RDFデータの内容、カレータイプの数、各カレータイプの原産国、および代表的な料理が表示されます。

## 4. Neo4jデータベースの準備

1. Neo4jデータベースを起動します。
2. 新しいデータベースを作成するか、既存のデータベースを使用します。
3. 接続情報（URL、ユーザー名、パスワード）をメモしておきます。

## 5. RDFデータのNeo4jへの変換

以下のPythonスクリプトを使用して、RDFデータをNeo4jに変換します。

```python
from rdflib import Graph, Namespace, RDF, RDFS
from neo4j import GraphDatabase

# RDFグラフの読み込み
g = Graph()
g.parse("curry.ttl", format="turtle")

# 名前空間の定義
CURRY = Namespace("http://example.org/curry/")

# Neo4jへの接続
driver = GraphDatabase.driver("bolt://localhost:7687", auth=("neo4j", "password"))

def create_curry_type(tx, uri, label, origin):
    tx.run("MERGE (c:CurryType {uri: $uri}) "
           "SET c.label = $label, c.origin = $origin", 
           uri=uri, label=label, origin=origin)

def create_dish(tx, uri, label, main_ingredient):
    tx.run("MERGE (d:Dish {uri: $uri}) "
           "SET d.label = $label, d.mainIngredient = $main_ingredient", 
           uri=uri, label=label, main_ingredient=main_ingredient)

def create_curry_dish_relation(tx, curry_uri, dish_uri):
    tx.run("MATCH (c:CurryType {uri: $curry_uri}), (d:Dish {uri: $dish_uri}) "
           "MERGE (c)-[:HAS_POPULAR_DISH]->(d)",
           curry_uri=curry_uri, dish_uri=dish_uri)

with driver.session() as session:
    # カレータイプノードの作成
    for curry_type in g.subjects(predicate=RDF.type, object=CURRY.CurryType):
        label = g.value(subject=curry_type, predicate=RDFS.label)
        origin = g.value(subject=curry_type, predicate=CURRY.origin)
        session.execute_write(create_curry_type, str(curry_type), str(label), str(origin))

    # 料理ノードの作成とリレーションシップの設定
    for curry_type in g.subjects(predicate=RDF.type, object=CURRY.CurryType):
        for dish in g.objects(subject=curry_type, predicate=CURRY.popularDish):
            label = g.value(subject=dish, predicate=RDFS.label)
            main_ingredient = g.value(subject=dish, predicate=CURRY.mainIngredient)
            session.execute_write(create_dish, str(dish), str(label), str(main_ingredient))
            session.execute_write(create_curry_dish_relation, str(curry_type), str(dish))

driver.close()

print("データの変換が完了しました。")
```

このスクリプトを実行すると、RDFデータがNeo4jデータベースに変換されます。

## 6. 結果の確認とクエリ

Neo4j Browserを開き、以下のCypherクエリを実行して結果を確認します：

```cypher
// すべてのカレータイプを取得
MATCH (c:CurryType) RETURN c
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648069/77c717af-8727-f1e9-358e-3dcb09c88f4a.png)


```cypher
// すべての料理を取得
MATCH (d:Dish) RETURN d
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648069/0a90e016-eed3-ab36-9a2e-52307b31f99c.png)

```cypher
// カレータイプとその代表的な料理を取得
MATCH (c:CurryType)-[:HAS_POPULAR_DISH]->(d:Dish)
RETURN c.label, c.origin, COLLECT(d.label) AS popular_dishes
```

```cypher
// 特定の主要食材を使用する料理を取得
MATCH (d:Dish)
WHERE d.mainIngredient = "Chicken"
RETURN d.label
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648069/ae4b4004-ecd8-7ad2-aee0-3f896a29a7f5.png)

```cypher
// カレータイプごとの料理数を取得
MATCH (c:CurryType)-[:HAS_POPULAR_DISH]->(d:Dish)
RETURN c.label, COUNT(d) AS dish_count
ORDER BY dish_count DESC
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648069/919a8e86-9918-5dfa-1c55-08a9b074aba6.png)

これらのクエリを実行することで、RDFデータが正しくNeo4jの知識グラフに変換されたことを確認できます。

## まとめ

![napkin-selection (35).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648069/e1c9c120-b104-080c-237c-d5ba3821adfc.png)

このチュートリアルでは、カレーに関する情報をRDFデータとして作成し、それをNeo4jの知識グラフに変換する方法を学びました。RDFデータの作成から始めることで、データモデリングの基礎から実際のグラフデータベースへの変換まで、一貫したプロセスを体験できました。

重要な点として、このプロセスは既存の公開されているRDFデータにも適用できます。多くの組織や機関が様々な分野のRDFデータを公開しており、これらを活用することで、より豊富で複雑な知識グラフを構築することができます。例えば、DBpedia、GeoNames、FOAFなどの広く使用されているRDFデータセットを利用することで、カレーデータと地理情報や人物情報を組み合わせた多角的な分析が可能になります。

次のステップとして、以下の発展的なトピックを探求することをお勧めします：

1. より大規模で複雑なカレー関連データの作成と処理
2. 外部のオープンデータソースとの統合（例：料理レシピデータベース）
3. 高度なNeo4jクエリの作成とグラフアルゴリズムの適用
4. カレーレコメンデーションシステムの構築

RDFデータとグラフデータベースを組み合わせることで、複雑なデータ間の関係性を効果的に表現し、分析することができます。この知識を活用して、料理データの分析や、より広範な分野でのデータモデリングに取り組んでみてください。