SQLite と Neo4j を組み合わせgraphDB作成

Neo4j

GraphDB はノード(商品・店舗)とエッジ(購入履歴)で関係を管理できるため、例えば「特定の商品をどの店舗で買うべきか」の分析が容易になります。

データの構造(ノードとリレーション)
* ノード
* (:Item {name: “牛乳”})
* (:Store {name: “スーパーA”})
* リレーション
* (:Item)-[:SOLD_AT {price: 198, date: “2025-02-01”}]->(:Store)

Neo4j でのデータ登録

from neo4j import GraphDatabase

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

def add_purchase(tx, item, store, price, date):
    query = """
    MERGE (i:Item {name: $item})
    MERGE (s:Store {name: $store})
    CREATE (i)-[:SOLD_AT {price: $price, date: $date}]->(s)
    """
    tx.run(query, item=item, store=store, price=price, date=date)

with driver.session() as session:
    session.write_transaction(add_purchase, "牛乳", "スーパーA", 198, "2025-02-01")

購入履歴から最適な店舗を検索

MATCH (i:Item {name: "牛乳"})-[r:SOLD_AT]->(s:Store)
RETURN s.name AS store, MIN(r.price) AS lowest_price

SQLite と GraphDB の使い分け

| 機能          | SQLite          | GraphDB              |
| ----------- | --------------- | -------------------- |
| データの保存      | 高速・軽量           | 遅め(構造化に向いている)        |
| 最安値検索       | シンプルな SQL で実装可能 | 関係性を活かした分析が得意        |
| データの可視化     | 表形式が得意          | ネットワーク分析が可能          |
| セールや価格変動の分析 | やや複雑            | 履歴をノード間リレーションで管理しやすい |

* SQLite: 基本的な購入データ管理(最安値検索・履歴保存)に向いている。

* GraphDB: 「どの店舗でどの商品を買うべきか?」を関係性で分析するのに適している。
まずは SQLite をメインに使用し、分析が必要な部分を GraphDB に移行するのが良さそう

SQLite と Neo4j を組み合わせることは可能
それぞれの役割を明確にし、データの流れを整理すれば、最適な購買管理システムを構築できる

| データ               | SQLite | Neo4j          |
| ----------------- | ------ | -------------- |
| 購入履歴(商品・価格・店舗・日付) | ✅      | ✅              |
| 最安値の検索            | ✅(SQL) | ✅(Graph Query) |
| 価格の履歴管理           | ✅      | ✅(関係性を活用)      |
| 商品と店舗の関係分析        | ❌      | ✅(ネットワーク解析)    |
| セール・特売情報との連携      | ❌      | ✅(リレーション活用)    |

SQLite は「履歴管理」「最安値検索」に適している
Neo4j は「店舗と商品の関係分析」「価格変動の視覚化」に向いている

SQLite のデータを Neo4j に同期させることで、購入履歴と分析機能を両方活用できる

現在のディレクトリは
ls
の結果

csv_files				receipts.db
import_receipts_with_dedup_and_log.py	skipped.csv
medication_summary.py

となっている

次に SqliteDBに
purchases テーブルがあることを確認

sqlite3 receipts.db 

を実行し

.tables

を実行

import_log  purchases 

とテーブルが表示されればOK

さらに構造を確認するには

 .schema purchases

とすれば

CREATE TABLE purchases (
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        store TEXT,
        date TEXT,
        item TEXT,
        unit_price INTEGER,
        total_price INTEGER,
        quantity INTEGER,
        self_med_amount INTEGER,
        UNIQUE(date, store, item)
    );

というように表示される

次に
SQLite のデータを Neo4j に同期させる

現在SQLite DBで

Enter ".help" for usage hints.
sqlite> .tables
import_log  purchases 
sqlite> .schema purchases
CREATE TABLE purchases (
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        store TEXT,
        date TEXT,
        item TEXT,
        unit_price INTEGER,
        total_price INTEGER,
        quantity INTEGER,
        self_med_amount INTEGER,
        UNIQUE(date, store, item)
    );

という構造になっている

これは

購入店舗名,購入年月日,商品名,単品税抜価格,価格,個数,セルフメディケーション対象金額
杏林堂 袋井旭町店,2025/06/18,サンテビオ 15mL,968,871,1,871
ピアゴ 袋井店,2025/04/18,タイレノールA 30錠,1790,1772,1,1772
COOP ユーコープ 袋井町店,2025/06/19,COゼリーエネルギー,118,354,3,0
COOP ユーコープ 袋井町店,2025/06/19,鮭・くず桜,238,190,1,0
COOP ユーコープ 袋井町店,2025/06/19,天ぷら用えび,398,238,1,0
COOP ユーコープ 袋井町店,2025/06/19,たい,398,398,1,0
COOP ユーコープ 袋井町店,2025/06/19,塩銀鮭切身,398,398,1,0
COOP ユーコープ 袋井町店,2025/06/19,真あじ開き,398,796,2,0
ドラッグストア クリエイト 袋井山名店,2025/06/18,日興フーズ 王様のチーズ,298,298,1,0
ドラッグストア クリエイト 袋井山名店,2025/06/18,ロイヤルブレッド山型(5),139,139,1,0
ドラッグストア クリエイト 袋井山名店,2025/06/18,家族の潤い白桃ミックス,108,216,2,0
ドラッグストア クリエイト 袋井山名店,2025/06/18,鶏卵卵パック MS 10個,228,228,1,0

という購入履歴をDBにしたもの

これを
SQLite と Neo4j を組み合わせ
それぞれの役割を明確にし、データの流れを整理すれば、最適な購買管理システムを構築する

なお元になるcsvファイルは毎日の買い物なので毎日更新される

目的が「毎日追加されるCSVの購入データを元に、SQLiteとNeo4jを連携して購買管理を最適化すること」

✅ システム構成の概要
項目 内容
SQLiteの役割 生データの保存。CSVから読み込んだ構造化データを記録。
→ 変更・削除・追加が簡単なローカル台帳的役割。
Neo4jの役割 関係性を活用した分析(例:購入頻度・店舗との関係・カテゴリ分析)
→ 推薦、店舗の傾向把握、消費パターン予測
CSVの流れ 日次で新しいCSVが追加 → SQLiteに取り込み → Neo4jに同期(新規 or 更新)

SQLiteスキーマ(そのままでOK)
UNIQUE(date, store, item) により同じ日・店舗・商品は1件のみ保存される(重複防止)。

CREATE TABLE purchases (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    store TEXT,
    date TEXT,
    item TEXT,
    unit_price INTEGER,
    total_price INTEGER,
    quantity INTEGER,
    self_med_amount INTEGER,
    UNIQUE(date, store, item)
);

Neo4jの構造例
以下のようにグラフ構造で格納

(:Store {name: "杏林堂 袋井旭町店"})
    -[:PURCHASED_ON {date: "2025-06-18", unit_price: 968, ...}]->
(:Item {name: "サンテビオ 15mL"})

Store ノード:店舗
Item ノード:商品
:PURCHASED_ON リレーション:購入関係と属性(日付・価格・数量など)

🔁 データフロー(毎日更新の流れ)
1. CSV読み込み → SQLiteにINSERT
2. SQLiteから新規・更新データ抽出
3. Neo4jに同期(MERGEでノードとリレーションを作成)

次に実践

まずは docker を起動する
Mac の場合

docker compose up -d

を実行しても

Cannot connect to the Docker daemon at unix:///Users/snowpool/.docker/run/docker.sock. Is the docker daemon running?

となるため
あらかじめ docker desktop を立ち上げておく必要がある

 docker compose up -d

を再度実行

[+] Running 1/1
 ✔ Container ne4j_pg-neo4j-1  Started                                      

次に

vim link_sql_in_ne04jdb.py

from neo4j import GraphDatabase
import sqlite3

# SQLiteに接続
conn = sqlite3.connect('receipts.db')
cursor = conn.cursor()

# Neo4jに接続(適宜、ユーザー名・パスワードを置き換えてください)
neo4j_driver = GraphDatabase.driver("bolt://localhost:7687", auth=("neo4j", "your_password"))

# データ取得
cursor.execute("SELECT store, date, item, unit_price, total_price, quantity, self_med_amount FROM purchases")
rows = cursor.fetchall()

def insert_to_neo4j(tx, store, date, item, unit_price, total_price, quantity, self_med_amount):
    tx.run("""
    MERGE (s:Store {name: $store})
    MERGE (i:Item {name: $item})
    MERGE (s)-[r:PURCHASED_ON {date: $date}]->(i)
    SET r.unit_price = $unit_price,
        r.total_price = $total_price,
        r.quantity = $quantity,
        r.self_med_amount = $self_med_amount
    """, store=store, date=date, item=item,
         unit_price=unit_price, total_price=total_price,
         quantity=quantity, self_med_amount=self_med_amount)

# Neo4jに書き込み
with neo4j_driver.session() as session:
    for row in rows:
        session.write_transaction(insert_to_neo4j, *row)

# 接続終了
conn.close()
neo4j_driver.close()

これを実行すると

Traceback (most recent call last):
  File "/Users/snowpool/aw10s/purcharge_history/link_sql_in_ne04jdb.py", line 1, in <module>
    from neo4j import GraphDatabase
ModuleNotFoundError: No module named 'neo4j'

となる

Pythonバージョンをアップしたのが原因

pip install neo4j

でインストール

Collecting neo4j
  Downloading neo4j-5.28.1-py3-none-any.whl.metadata (5.9 kB)
Requirement already satisfied: pytz in /Users/snowpool/.pyenv/versions/3.11.0/lib/python3.11/site-packages (from neo4j) (2024.2)
Downloading neo4j-5.28.1-py3-none-any.whl (312 kB)
Installing collected packages: neo4j
Successfully installed neo4j-5.28.1

[notice] A new release of pip is available: 24.3.1 -> 25.1.1
[notice] To update, run: pip install --upgrade pip

今度は

/Users/snowpool/aw10s/purcharge_history/link_sql_in_ne04jdb.py:31: DeprecationWarning: write_transaction has been renamed to execute_write
  session.write_transaction(insert_to_neo4j, *row)

となる

これは
write_transaction() は 非推奨(Deprecated) になり、代わりに execute_write() を使うように変更されているため

このためコード変更

# 旧(警告が出る)
session.write_transaction(insert_to_neo4j, *row)

# 新(推奨される書き方)
session.execute_write(insert_to_neo4j, *row)

このため

with neo4j_driver.session() as session:
    for row in rows:
        session.execute_write(insert_to_neo4j, *row)

と変更

| 状態     | メソッド名                 | 備考            |
| ------ | --------------------- | ------------- |
| ✅ 推奨   | `execute_write()`     | Neo4j 5.x以降向け |
| ⚠️ 非推奨 | `write_transaction()` | 古いコードとの互換性用   |

これで再度

python link_sql_in_ne04jdb.py

を実行すると
graphDBが作成される