neo4j 実例で学ぶCypher -1-


はじめに前提的なこと

  • 楽天のような店子をたくさん持つプラットフォーマーの立場で考えます
  • たくさんのユーザが色々なショップで色々な商品を購入し、その購入履歴データを保持している
  • 前回から引き続きです

サンプルデータ

データ構造は下記

CSVとしては下記

category.csv
category_id:ID,name,:LABEL
cate1,カテゴリ1,category
cate2,カテゴリ2,category
shops.csv
shop_id:ID,name,category_id,:LABEL
shop1,ショップA,cate1,shop
shop2,ショップB,cate1,shop
shop3,ショップC,cate2,shop
item.csv
item_id:ID,name,shop_id,:LABEL
item1,商品A,shop1,item
item2,商品B,shop1,item
item3,商品C,shop2,item
item4,商品D,shop3,item
item5,商品E,shop3,item
orders.csv
order_id:ID,date,user_id,shop_id,category_id,price,item_id,:LABEL
order1,2016-04-02,user1,shop1,cate1,9500,item1,order
order2,2016-04-03,user2,shop1,cate1,1000,item1,order
order3,2016-04-04,user3,shop1,cate1,1200,item2,order
order4,2016-04-05,user4,shop2,cate1,15000,item3,order
order5,2016-03-22,user1,shop3,cate2,2000,item4,order
order6,2016-02-11,user2,shop3,cate2,4000,item4,order
order7,2016-03-25,user1,shop3,cate2,5000,item5,order
users.csv
user_id:ID,name,email,age:int,industry,gender,:LABEL
user1,user_name1,[email protected],37,サービス業,male,user
user2,user_name2,[email protected],17,ネットサービス,male,user
user3,user_name3,[email protected],27,公務員,female,user
user4,user_name4,[email protected],44,公務員,male,user

データ投入は

こんな感じで

neo4j-import --into /usr/local/Cellar/neo4j/2.3.2/libexec/data/graph5.db --nodes category.csv --nodes items.csv --nodes orders.csv --nodes shops.csv --nodes users.csv

リレーション

ユーザと購入を紐付け

MATCH (o:order),(u:user) 
WHERE o.user_id = u.user_id 
CREATE (u)-[:BUY]->(o);

購入データと商品を紐付け

MATCH (o:order),(i:item) 
WHERE o.item_id = i.item_id 
CREATE (o)-[:ORDER]->(i);

お店と商品を紐付け

MATCH (s:shop),(i:item)
WHERE s.shop_id = i.shop_id
CREATE (i)-[:PART]->(s);

お店とお店のジャンルを紐付け

MATCH (c:category),(s:shop)
WHERE c.category_id = s.category_id
CREATE (s)-[:CATE]->(c);

抽出したいデータからCypherを考える

購入確率の高いユーザを抽出する

  • 過去の購入履歴から電化製品カテゴリのショップで購入したことのあるユーザを電化製品好きのユーザとする
  • 新しい電化製品の販売が始まってが、まだ購入していない電化製品好きのユーザを抽出する

サンプルデータから正解を考える

  • まずカテゴリ1を電化製品を扱ってるショップのカテゴリとする
  • 新しい電化製品の販売が始まったお店をショップAとする
  • ショップAの購入データは無いが、カテゴリ1=電化製品カテゴリに分類されるお店での購入履歴のあるユーザを抽出すれば良い
  • 図からたどるとuser4が抽出されれば正解

Cypherで書き正解を出す

neo4j-sh (?)$ MATCH (u:user)-->(o:order)-->(i:item)-->(s:shop)-->(c:category)
> WHERE  NOT s.name="ショップA" AND c.name="カテゴリ1"
> RETURN u.name;
+--------------+
| u.name       |
+--------------+
| "user_name4" |
+--------------+
  • 回答が一致

購入者の内訳で男女べつにどのくらいの割合かを知りたい

  • ショップAに絞って考える

サンプルデータから正解を考える

  • ショップAで購入しているユーザはuser1,user2,user3
  • user1はmale,user2はmale,user3はfemale
  • なのでmale:2,female:1となる

Cypherで書き正解を出す

  • UNION ALLで続けて出してしまう
neo4j-sh (?)$ MATCH (u:user)-->(o:order)-->(i:item)-->(s:shop)-->(c:category)
> WHERE s.name="ショップA" AND u.gender='male'
> RETURN count(u)
> UNION ALL
> MATCH (u:user)-->(o:order)-->(i:item)-->(s:shop)-->(c:category)
> WHERE s.name="ショップA" AND u.gender='female'
> RETURN count(u)
> ;
+----------+
| count(u) |
+----------+
| 2        |
| 1        |
+----------+
2 rows
21 ms
  • 回答が一致

購入者の内訳で誰が何個購入しているのかを知りたい

  • ショップCに絞って考える

サンプルデータから正解を考える

  • ショップAで購入しているユーザはuser1,user2
  • user1は商品Dと商品Eを購入
  • user2は商品Dを購入
  • なのでuser1は2回購入、user2は1回購入となる

Cypherで書き正解を出す

neo4j-sh (?)$ MATCH (u:user)-->(o:order)--(i:item)-->(s:shop)
> WHERE s.name="ショップC"
> RETURN u.name, count(o) as order_cnt;
+--------------------------+
| u.name       | order_cnt |
+--------------------------+
| "user_name1" | 2         |
| "user_name2" | 1         |
+--------------------------+
2 rows
  • 回答が一致

まとめ、雑感

  • relationshipをもっと貼ってみてCypher考えた方が良さそう
    • relationshipの貼り方次第的な感覚も出てきた
  • SQL書くより簡単なのは確か