Skip to main content
NTT Engineers' Meetup #3
無駄に Neo4j を使っている日々
自己紹介
● ぬこ@横浜 (@nuko_yokohama)
● にゃーん
● 趣味でポスグレや Neo4j をやってる者だ
Neo4j って?
Neo4j
● グラフデータベース
● 結構歴史は古い。登場したのは 2007 年頃。
● NTT コムウェアさんでも導入事例あり
– NTT グループ企業のネットワーク機器管理
– https://www.creationline.com/clientvoice/case17
● Cypher という独自の問い合わせ言語( SQL 相当)
グラフデータベース
● ノード、エッジ(関連)、プロパティの三要素
ノード
プロパティ
ノード
プロパティ
エッジ
● グラフデータベースの実装製品は結構多い。
– https://en.wikipedia.org/wiki/Graph_database#List_of_graph_databases
グラフデータベースの適用領域
● ソーシャルグラフ
● レコメンデーション
● 経路探索
● ネットワーク管理
● 不正検知
● などなど
ぬこの Neo4j の使い方
すべては QoN(Quality of Noodle) の向上のために
題材:なんとかデータベース
● ラーメン等のレビューサイト
– https://supleks.jp/
● 自分も「ちょっとだけ」
利用している
ぬこ環境の PostgreSQL と Neo4j 構成
AWS EC2
スクレイピング
スクリプト
PostgreSQL
14-devel
俺々なんとか
データベース
エクスポート
Cypher 生成
インポート
関連生成
レビュー数 96 万件
店舗数 11 万件
ユーザ数 21 万件
コメント数 162 万件
neo4j-community-4.1.1
PostgreSQL でのデータ構造
● users, reviews, shops か
ら、 Neo4j ノード用の
CSV をエクスポート
● users と
reviews 、 reviews と
sops 、 comments と
reviews の関連から Neo4j
関連生成コマンドを作成
ノード用の CSV を PostgreSQL で生成
ramendb=# COPY (
SELECT rid, menu, score, category, type, soup, reg_date
FROM reviews
WHERE reg_date BETWEEN '2019-10-01' AND '2020-09-30'
) TO '/tmp/r.csv' CSV;
COPY 79412
ramendb=# q
[ec2-user@ip-10-0-1-10 ramendb]$ head -3 /tmp/r.csv
1349100, 小(麺少な目)ニンニク少し、アブラ、ショウガ 750 円 ,83, ラーメン , ラーメン , 豚骨醤油 ,2020-09-18
1349101, ラーメン 並(1玉) ※麺:かため&味:濃い ,90, ラーメン , ラーメン , 豚骨醤油 ,2020-09-26
1349102, やばにぼ、和え玉 ( 白 ),95, ラーメン , ラーメン , 煮干し ,2020-09-13
[ec2-user@ip-10-0-1-10 ramendb]$
● Review ノード用の CSV ファイルを生成
Neo4j コマンドを PostgreSQL で生成
ramendb=# COPY (
SELECT 'MATCH (u:User{uid:' || uid || '}),(r:Review{rid:' || rid || '}) CREATE (u)-[:write]->(r);'
FROM reviews r
WHERE r.reg_date BETWEEN '2019-10-01' AND '2020-09-30'
) TO '/tmp/u-r.cql';
COPY 79412
ramendb=# q
[ec2-user@ip-10-0-1-10 ramendb]$ tail -3 /tmp/u-r.cql
MATCH (u:User{uid:142431}),(r:Review{rid:1336079}) CREATE (u)-[:write]->(r);
MATCH (u:User{uid:69891}),(r:Review{rid:1336080}) CREATE (u)-[:write]->(r);
MATCH (u:User{uid:56}),(r:Review{rid:1336081}) CREATE (u)-[:write]->(r);
[ec2-user@ip-10-0-1-10 ramendb]$
● User ノードから Review ノードへの関連生成コマンドを生成
Neo4j でのグラフデータ構造
● 3 種類のノードと 3 種類の関連。シンプル。
User
プロパティ
Review
プロパティ
write
Shop
プロパティ
eval
comment
uid ユーザ ID
name ユーザ名
pref 本拠地 ( 都道府県 )
area 本拠地 ( 市区町村 )
rid レビュー ID
menu 品名
score 採点 (0-100)
category カテゴリ
reg_date レビュー登録日
etc...
sid 店舗 ID
name 店舗名
pref 本拠地 ( 都道府県 )
area 本拠地 ( 市区町村 )
etc...
色々な検索例
検索例 1 (年始期間の本拠地レビュー)
● 2020-01-01 から 2020-01-07 の間、横浜市中区の店へのレ
ビューに対してコメントしたユーザのグラフ
MATCH (u:User)-[:comment]-(r:Review)-[:write]-(ru:User), (r)-[:eval]-(s:Shop)
WHERE s.area = ' 横浜市中区 '
AND (r.reg_date >= '2020-01-01'
AND r.reg_date <= '2020-01-07')
RETURN r,ru, u,s
検索例 1 (年始期間の本拠地レビュー)
● 結果
● このあたりのユーザは
自分に良くコメントしてくれる。
ファンは大事にしよう。
● このあたりのユーザとは接点が
ないので、アクセスしてみるか。
● 自分だけでなく、都内の人気ユーザ
もフォローしている人たち。
検索例 2 (高評価レビューを上げたユーザ)
● 2020-07-01 ~ 2020-09-30 の期間に、
自分が 80 点以上をつけたレビュー対象の店舗を、 80 点以上でレビューした人。
MATCH (u:User{uid:8999})-[:write]-(r:Review)-[:eval]-
(s:Shop)-[:eval]-(r2:Review)-[:write]-(u2:User)
WHERE r.score >=80 AND r2.score >= 80
AND u2.uid <> 8999
AND r.reg_date >='2020-07-01' AND r.reg_date <= '2020-09-30'
RETURN s,r2,u2
検索例 2 (高評価レビューを上げたユーザ)
● 結果
● この 2 店舗はやはり
人気があるようだ。
(はやし田、王道之印)
検索例 3 (ゆでタリアンクラスタ)
● 2020-04-01 から 2020-09-30 の間、全国の    に
レビューした人たちのソーシャルグラフ
MATCH (u:User)-[:write]-(r:Review)-[:eval]-(s:Shop)
WHERE s.name = ' ゆで太郎 '
AND r.reg_date >='2020-04-01' AND r.reg_date <= '2020-09-30'
RETURN s,r, u
検索例 3 (ゆでタリアンクラスタ)
● 結果
ぬこはここにいる。
● 埼玉の小規模
「ゆでタリアン」クラスタ● 都内の大規模
「ゆでタリアン」クラスタ
● 茨城の小規模
「ゆでタリアン」クラスタ
さらに無駄な使い方
登場人物の関連可視化
登場人物の関連可視化(例:「屍鬼」)
● 「屍鬼」に登場した人物、施設、地名等をノードにし、関連を
設定する。
● これをやるために、「屍鬼」 1 巻を何度読み返したことかw
● ただ、今のグラフデータモデルは暫定的なもの。
– 章ノードを作って、章ノードに紐づく登場人物の関係性を表
現するほうがいいのかも・・・
– 章ノード間には時系列を示す関連を設定するとか
登場人物の関連可視化(例:インド神話)
● インド神話の世界は複雑怪奇。
● 2 大叙情詩「ラーマーヤナ」「マハーバーラタ」と「リグ・
ヴェーダ」
● 同じような話が収録されている叙情詩によって、ちょっと違っ
たりしている。
● 神の別名、人間への転生など何でもあり。
● これはグラフで整理しなければならない。
以上!