Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
8
Help us understand the problem. What is going on with this article?
@nakamura-tsuyoshi

neo4j 実例で学ぶCypher -2-

More than 5 years have passed since last update.

はじめに前提的なこと

  • 楽天のような店子をたくさん持つプラットフォーマーの立場で考えます
  • 前回前々回から引き続きです

サンプルデータの用意

データ構造

ccc.png

CSVにすると

category.csv
category_id:ID,name,:LABEL
cate1,カテゴリ1,category
shops.csv
shop_id:ID,name,category_id,:LABEL
shop1,ショップA,cate1,shop
shop2,ショップB,cate1,shop
items.csv
tem_id:ID,name,shop_id,:LABEL
item1,商品A,shop1,item
item2,商品B,shop1,item
item3,商品C,shop2,item
item4,商品D,shop2,item
item5,商品E,shop2,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
users.csv
user_id:ID,name,email,age:int,industry,gender,:LABEL
user1,user_name1,1@example.com,37,サービス業,male,user
user2,user_name2,2@example.com,17,ネットサービス,male,user
user3,user_name3,3@example.com,27,公務員,female,user
user4,user_name4,4@example.com,44,公務員,male,user
user5,user_name5,4@example.com,55,公務員,male,user
owner.csv
owner_id:ID,name,:LABEL
owner1,owner1,owner
owner2,owner2,owner

サンプルデータの投入

➜  neo4j neo4j-import --into /usr/local/Cellar/neo4j/2.3.2/libexec/data/graph9.db --nodes category.csv --nodes items.csv --nodes orders.csv --nodes shops.csv --nodes users.csv --nodes owner.csv
Unable to find any JVMs matching version "1.7".
Importing the contents of these files into /usr/local/Cellar/neo4j/2.3.2/libexec/data/graph9.db:
Nodes:
  /Users/a12091/neo4j/category.csv

  /Users/a12091/neo4j/items.csv

  /Users/a12091/neo4j/orders.csv

  /Users/a12091/neo4j/shops.csv

  /Users/a12091/neo4j/users.csv

  /Users/a12091/neo4j/owner.csv

Available memory:
  Free machine memory: 18.05 MB
  Max heap memory : 1.78 GB

Nodes
[*>:??-------------------------------------------------------------------------------------|P||] 10k
Done in 594ms
Prepare node index
[*DETECT:7.63 MB-------------------------------------------------------------------------------]   0
Done in 24ms
Calculate dense nodes
[*>:??-----------------------------------------------------------------------------------------]   0
Done in 12ms
Relationships
[*>:??-----------------------------------------------------------------------------------------]   0
Done in 10ms
Node --> Relationship
[*>:??------------------------------------------|LINK-------------------|v:??------------------] 10k
Done in 11ms
Relationship --> Relationship
[*>:??-----------------------------------------------------------------------------------------]   0
Done in 12ms
Node counts
[*COUNT:76.29 MB-------------------------------------------------------------------------------] 10k
Done in 133ms
Relationship counts
[*>:??-----------------------------------------------------------------------------------------]   0
Done in 1ms

IMPORT DONE in 2s 345ms. Imported:
  19 nodes
  0 relationships
  85 properties

リレーションをはる

buy

  • 注文とuserを紐付ける
MATCH (o:order),(u:user) 
WHERE o.user_id = u.user_id 
CREATE (u)-[:BUY]->(o);

order

  • 注文データと商品を紐付ける
MATCH (o:order),(i:item) 
WHERE o.item_id = i.item_id 
CREATE (o)-[:ORDER]->(i);

part of

  • 商品とショップを紐付ける
MATCH (s:shop),(i:item)
WHERE s.shop_id = i.shop_id
CREATE (i)-[:PART]->(s);

categorize

  • ショップとカテゴリーを紐付ける
MATCH (c:category),(s:shop)
WHERE c.category_id = s.category_id
CREATE (s)-[:CATE]->(c);

frend

  • ショップオーナーと友人ユーザを紐付ける
MATCH (ow:owner),(u:user)
WHERE ow.owner_id = "owner1" AND u.user_id = "user1"
CREATE (ow)-[:FREND]->(u);

MATCH (ow:owner),(u:user)
WHERE ow.owner_id = "owner1" AND u.user_id = "user2"
CREATE (ow)-[:FREND]->(u);

recommend

  • ショップオーナーのおすすめ商品を紐付け
MATCH (ow:owner),(i:item)
WHERE ow.owner_id = "owner1" AND i.item_id = "item1"
CREATE (ow)-[:RECCOMEND]->(i);

favorite

  • ユーザとお気に入り商品を紐付ける
MATCH (u:user),(i:item)
WHERE u.user_id = "user3" AND i.item_id = "item2"
CREATE (u)-[:FAVORITE]->(i);

MATCH (u:user),(i:item)
WHERE u.user_id = "user3" AND i.item_id = "item3"
CREATE (u)-[:FAVORITE]->(i);

MATCH (u:user),(i:item)
WHERE u.user_id = "user4" AND i.item_id = "item2"
CREATE (u)-[:FAVORITE]->(i);

MATCH (u:user),(i:item)
WHERE u.user_id = "user4" AND i.item_id = "item4"
CREATE (u)-[:FAVORITE]->(i);

MATCH (u:user),(i:item)
WHERE u.user_id = "user5" AND i.item_id = "item5"
CREATE (u)-[:FAVORITE]->(i);

owner

  • ショップオーナーとショップを紐付ける
MATCH (ow:owner),(s:shop)
WHERE ow.owner_id = "owner1" AND s.shop_id = "shop1"
CREATE (ow)-[:OWMER]->(s);


MATCH (ow:owner),(s:shop)
WHERE ow.owner_id = "owner2" AND s.shop_id = "shop2"
CREATE (ow)-[:OWMER]->(s);

抽出したいデータ(ここからがメイン)

1:内訳としてショップAの購入者でオーナーの知人とそうでない人はどのくらいのか

サンプルデータから自分でデータを追うと

  • user1とuser2の2名が知人でuser3は知人ではない人からの購入
  • :FRENDというリレーションが貼られてるのはuser1とuser3のみ
  • なので知人が2でその他は1となる

Cypherで書いてみる

neo4j-sh (?)$ MATCH (u:user)-->(o:order)-->(i:item)-->(shop {name:"ショップA"})<--(ow:owner)-[:FREND]->(user)
> RETURN count(DISTINCT user) as frend_of_owner , count(DISTINCT u) - count(DISTINCT user) as other
> ;
+------------------------+
| frend_of_owner | other |
+------------------------+
| 2              | 1     |
+------------------------+
  • 知人が2でその他は1となり一致

2:favorite(お気に入り)が多い商品は

サンプルデータから表にまとめてみると

user favorite item
user3 商品B
user3 商品C
user4 商品B
user4 商品D
user5 商品E
  • 商品Bがuser3とuser4からお気に入り登録されているので2ユーザがfavorite
  • 商品Cがuser3からお気に入り登録されているので1ユーザがfavorite
  • 商品Dがuser4からお気に入り登録されているので1ユーザがfavorite
  • 商品Eがuser5からお気に入り登録されているので1ユーザがfavorite

が答え

Cypherで書いてみる

neo4j-sh (?)$ MATCH (u:user)-[:FAVORITE]->(i:item)
> RETURN i.name,count(*)
> ;
+-------------------+
| i.name | count(*) |
+-------------------+
| "商品E"  | 1        |
| "商品B"  | 2        |
| "商品D"  | 1        |
| "商品C"  | 1        |
+-------------------+
4 rows
37 ms
  • count(*)はユーザ数を表す
  • それぞれ一致

3:商品をfavorite(お気に入り)をして該当の商品を購入したユーザ数は?

サンプルデータから表にまとめてみると

商品 favorite 購入したか 購入user
商品A x user1
商品A x user2
商品B user3
商品B x user4
商品C x user3
商品C x user4
商品D x user4
商品E x user5
  • favorite=○で且つ購入したか=○を探す
  • user3だけが商品Bをfavoriteして購入しているので1ユーザが答え

ビジュアライズしてみる

Neo4j-111.png

Cypherで書いてみる パターン1

neo4j-sh (?)$ MATCH (u:user)-[:FAVORITE]->(i:item)<--(o:order)<--(u:user)
> RETURN count(*);
+----------+
| count(*) |
+----------+
| 1        |
+----------+

Cypherで書いてみる パターン2

neo4j-sh (?)$ MATCH (u:user)-[:FAVORITE]->(i:item),(u:user)-->(o:order)-->(i:item)
> RETURN COUNT(*);
+----------+
| COUNT(*) |
+----------+
| 1        |
+----------+
1 row
82 ms

Cypherで書いてみる パターン3

neo4j-sh (?)$ MATCH (u:user)-->(o:order)-->(i:item)
> WHERE u-[:FAVORITE]->i
> RETURN count(*);
+----------+
| count(*) |
+----------+
| 1        |
+----------+
1 row
109 ms
  • count(*)はユーザ数を表す
  • 1ユーザとなり一致。色々と書き方がある

4:商品をfavoite(お気に入り)をしてないけど購入したユーザ数は?

サンプルデータから表にまとめてみると

商品 favorite 購入したか 購入user
商品A x user1
商品A x user2
商品B user3
商品B x user4
商品C x user3
商品C x user4
商品D x user4
商品E x user5
  • 購入したか=○で且つfavorite=xを探す
  • ユーザはuser1,user2,user4はfavoriteしていなが購入しているので3ユーザが答え

ビジュアライズしてみる

Neo4j.png

Cypherで書いてみる

neo4j-sh (?)$ MATCH (u:user)-->(o:order)-->(i:item)
> WHERE NOT u-[:FAVORITE]->i
> RETURN count(*)
> ;
+----------+
| count(*) |
+----------+
| 3        |
+----------+
1 row
81 ms
  • count(*)はユーザ数を表す
  • 3ユーザとなり一致

5:商品をfavorite(お気に入り)をしたが該当の商品を買わなかったユーザ数は?

サンプルデータから表にまとめてみると

商品 favorite 購入したか 購入user
商品A x user1
商品A x user2
商品B user3
商品B x user4
商品C x user3
商品C x user4
商品D x user4
商品E x user5
  • favorite=○で且つ購入したか=xを探す
  • favorite(お気に入り)をしたが、購入していないユニークユーザはuser3,user4,user5で3ユーザが答え

ビジュアライズしてみる

Neo4j-1.png

  • 注意)商品Bはuser3からfavoriteされ、購入しているが、user4からfavoriteされて購入されていない

Cypherで書いてみる

neo4j-sh (?)$ MATCH (u:user)-[:FAVORITE]->(i:item),(o:order)
> WHERE NOT o<--u
> RETURN count(DISTINCT u);
+-------------------+
| count(DISTINCT u) |
+-------------------+
| 3                 |
+-------------------+
1 row
89 ms
  • count(*)はユーザ数を表す
  • 3ユーザとなり一致

まとめ、雑感的なもの

  • ちょっと複雑めにリレーションを張って色々な条件で抽出してみたが、まぁなんとなくできた
  • Webインターフェースがあったおかげで理解がスムーズだった
    • チュートリアルもわかりやすかった
  • グラフDBを使うことで割と欲しい集計データが簡単に抽出できるとわかった
  • プロダクジョンではmysqlを使ってて、どういう方法、タイミングでneo4jへ連携する部分でまだ考えなきゃいけないなぁと感じた
    • 夜間バッチかなぁ。。。毎晩夜間バッチでslaveのmysqlのデータをneo4jへ突っ込む的な
  • Cypherでサブクエリ的なものが使えないっぽいのでリレーションが重要だと感じた
  • CypherにはEXPLAIN,PROFILEがあるので自分のQLの計測が出来る。プロダクジョンとかの大量のデータを扱う際に必要になってくるだろうな
  • スケールに関するものも一通りあるっぽいなぁ http://neo4j.com/docs/stable/ha.html
  • 可能性はいろいろあるね
    • ただ負荷に対するノウハウ等が少ないのでいきなりユーザが使うサービスには使うのは怖いかな
    • 社内的な分析にまずは使ってみるが良いんじゃないかなと
8
Help us understand the problem. What is going on with this article?
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
nakamura-tsuyoshi
software engineer

Comments

No comments
Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account Login
8
Help us understand the problem. What is going on with this article?