League of Graphs

DmitrijsVrublevskis 10,221 views 58 slides May 31, 2016
Slide 1
Slide 1 of 58
Slide 1
1
Slide 2
2
Slide 3
3
Slide 4
4
Slide 5
5
Slide 6
6
Slide 7
7
Slide 8
8
Slide 9
9
Slide 10
10
Slide 11
11
Slide 12
12
Slide 13
13
Slide 14
14
Slide 15
15
Slide 16
16
Slide 17
17
Slide 18
18
Slide 19
19
Slide 20
20
Slide 21
21
Slide 22
22
Slide 23
23
Slide 24
24
Slide 25
25
Slide 26
26
Slide 27
27
Slide 28
28
Slide 29
29
Slide 30
30
Slide 31
31
Slide 32
32
Slide 33
33
Slide 34
34
Slide 35
35
Slide 36
36
Slide 37
37
Slide 38
38
Slide 39
39
Slide 40
40
Slide 41
41
Slide 42
42
Slide 43
43
Slide 44
44
Slide 45
45
Slide 46
46
Slide 47
47
Slide 48
48
Slide 49
49
Slide 50
50
Slide 51
51
Slide 52
52
Slide 53
53
Slide 54
54
Slide 55
55
Slide 56
56
Slide 57
57
Slide 58
58

About This Presentation

In this show-and-tell session, we’ll find out how Neo4j can make us a professional League of Legends player. First, we’ll import game data into a Neo4j graph. Then we will push the limits of Cypher to uncover hidden secrets that lie in the depth of LoL.
• Which champions perform best as allie...


Slide Content

Name Surname
[email protected]
Presentation Title
League of
Graphs

Dmitry Vrublevsky
@Me
Software engineer @
! [email protected]
" @FylmTM
# vrublevsky.me

Neueda4j / awesome-neo4j
{75 #} A curated list of Neo4j resources.
Neueda4j / jetbrains-plugin-cypher
{24 #} Cypher plugin for Jetbrains IDE’s.
? / ?
{? #} Next awesome project!

Agenda
1. League of Legends
2. Neo4j
3. Data import
4. Queries

Winner?
League of Legends
“Poros are the mysterious, magical and most-loved
inhabitants originating from the Howling Abyss.”

League of Legends
is multiplayer online battle arena,
real-time strategy video game
developed and published by
Riot Games

Me
Unranked Bronze Silver Gold
Platinum Diamond Master Challenger

League of Legends?
1. I play this game a lot
2. Rich API
3. There is library for API
https://developer.riotgames.com

meraki-analytics/Orianna
A Java adaptation of the Riot Games LoL API
Summoner summoner =
RiotAPI.getSummonerByName(“FylmTM");

println(summoner.getName()
+ " is a level “ + summoner.getLevel()) ;

Neo4j
Highly scalable native graph
database that leverages data
relationships as 

first-class entities.
by Neo Technology, Inc.

http://db-engines.com/en/ranking

GRAPH

NOT GRAPH

Features
Native Processing &
Storage
ACID
Cypher - Graph Query
Language
REST & Native API
Lock Manager
High-performance cache
Clustering
Backups
Monitoring
Community Enterprise

First-class
details: :LIKES
:DMITRY
:JUG
works_with: Neo4j
day: 28.05.2016
Properties
Labels
Type

Native
1. “Native” storage
2. “Native” processing

“Native” storage
Specifically designed to
store and manage graphs.

http://neo4j.com/developer/graph-db-vs-rdbms/

http://neo4j.com/developer/graph-db-vs-rdbms/

http://neo4j.com/developer/graph-db-vs-rdbms/

“Native” processing
Efficient way of processing
graph data since connected
nodes physically “point” to
each other
a.k.a. “index-free adjacency”

Storage layout
Node (15 bytes)
in_use
next_rel_id
next_prop_id
labels
extra
Relationship (34 bytes)
directed | in_use
first_node
second_node
rel_type
first_prev_rel_id
first_next_rel_id
second_prev_rel_id
second_next_rel_id
next_prop_id
first_in_chain_markers

Storage math
Node_Offset = Node_Size * Node_ID
Rel_Offset = Rel_Size * Rel_ID

Traversal (Node -> Relationship)
{5} Node (15 bytes)
next_rel_id {2}
{<int>} - entity id
<int>B - offset
Relationships (34 bytes)
{0}
2 * 34 = 68
0B
{1}34B
{2}68B
{3}102B
{4}136B
{5}170B

Traversal (Relationship -> Node)
{2} Relationship (34 bytes)
{<int>} - entity id
<int>B - offset
Nodes (15 bytes)
{0}0B
{1}15B
{2}30B
{3}45B
{4}60B
{5}75B
first_node {1}
second_node {4}

“Native” summary
O(1) traversal hops
Avoid super nodes!

Cypher
Cypher is a declarative
graph query language that
allows for expressive and
efficient querying.
https://github.com/opencypher/openCypher

Cypher
( ) - node
--> - relationship
Keywords:
-MATCH
-CREATE
-WHERE
-RETURN
MATCH (jug) RETURN jug
MATCH (attendees)-->(jug) 

RETURN *
MATCH (attendees)-->(jug)
WHERE jug.city = “Kaunas”

RETURN *

Cypher
MATCH (you:Person {name: "Dmitry"})
CREATE (you)-[like:LIKE]->(neo:Database {name: "Neo4j"})
RETURN you, like, neo

Whiteboard friendly
data modelling

CREATE (alice:Person {name: "Alice"})

CREATE (bob:Person {name: "Bob"})

CREATE (carol:Person {name: "Carol"})

CREATE (iphone:Device {name: "iPhone"})


CREATE (alice)-[:HAS]->(iphone) 

CREATE (bob)-[:WANTS]->(iphone) 


CREATE (alice)<-[:FOLLOWS]-(bob) 

CREATE (alice)-[:FOLLOWS]->(bob) 

CREATE (carol)-[:FOLLOWS]->(bob) 


CREATE (bob)<-[:AUTHOR]-(:Comment {text: "Thoughts?"})

<-[:COMMENT]-(iphone) 

CREATE (carol)<-[:AUTHOR]-(:Comment {text: "<3 it"})

<-[:COMMENT]-(iphone)

Data import
1. Retrieve data using Orianna
2. Load data as-is in Neo4j

Dependencies
dependencies {

compile 'com.robrua:orianna:2.4.3-SNAPSHOT' 

compile 'org.neo4j:neo4j:2.3.3' 

}

Create database
File dbDir = new File(databasePath);

GraphDatabaseService db = new GraphDatabaseFactory() 

.newEmbeddedDatabaseBuilder(dbDir) 

.newGraphDatabase() ;

Transaction
try (Transaction tx = db.beginTx()) { 

// Do cool stuff

tx.success();

}

Load data
Summoner summoner =
RiotAPI.getSummonerByName(“FylmTM");
Node node = db.createNode() ;

node.addLabel(Labels. Summoner);

node.setProperty(KEY_ID, summoner.getID());

node.setProperty(KEY_NAME, summoner.getName());

node.setProperty(KEY_LEVEL, summoner.getLevel()) ;

In the end

neo4j-contrib/neo4j-apoc-procedures
Awesome procedures for Neo4j 3.0 - codenamed "apoc"
// examines the full graph to create the meta-graph 

CALL apoc.meta.graph();
Apoc was the technician and driver on board of the
Nebuchadnezzar in the Matrix movie.
He was killed by Cypher.

Import result
•Season 6, EUNE region
•Players: me & 7 friends
MATCH (n) RETURN count(n) as nodeCount
nodeCount
336729

Queries

Query #1How many games I have played?
MATCH (s:Summoner {name: "FylmTM"})

MATCH (s)-[:PARTICIPATED|:PARTICIPATED_IN_MATCH]->(sMatch) 

RETURN count(sMatch); // 59 ms
Count
110

Query #2Who are my friends?
MATCH (s:Summoner {name: "FylmTM"})

MATCH (s)-[:PARTICIPATED]-()-[:PARTICIPATED_IN_MATCH] 

->(sMatch)<-

[:PARTICIPATED_IN_MATCH]-()-[:PARTICIPATED]-(f) 

WITH f.name as friendName, count(f) as gamesTogether

WHERE gamesTogether > 2

RETURN friendName, gamesTogether

ORDER BY gamesTogether DESC // 55 ms
friendName gamesTogether
Cryptael 107
Henua 28
Iger 11
XXpoMMou 10
yesCold 8
eskiuzi 3

Query #3Most dangerous champions?
name total won lost %
Alistar 7 1 6 86%
Jax 12 3 9 75%
LeBlanc 8 2 6 75%
Dr. Mundo 7 2 5 71%
Miss Fortune 10 3 7 70%
MATCH (s:Summoner {name: "FylmTM"})

MATCH (s)-[:PARTICIPATED]->()-[:PLAYED_FOR_TEAM]-> 

(team {winner: true})<-[:HAS_TEAM]-()-[:HAS_TEAM]->(o) 

<-[:PLAYED_FOR_TEAM]-()-[:PARTICIPATED_WITH_CHAMPION]->(c) 

RETURN c.name as championName, count(c) as winCount

ORDER BY winCount DESC

Alistar Jax

Query #4Any statistics?
MATCH (s:Summoner) WHERE s.name IN ["MrMgr", "Cryptael"]

MATCH (s)-[:PARTICIPATED|:PARTICIPANT_TIMELINE* 2]->(pt)-[r]->(ptd)

WHERE pt.role = "SOLO" AND pt.lane = "MIDDLE"

AND r.name IN ["getCreepsPerMinDeltas" , "getGoldPerMinDeltas" ]

WITH s.name as name, r.name as stat,

sum(ptd.zero_to_ten) as `sum_0-10`,

size(filter(x IN collect(ptd.zero_to_ten) WHERE x <> 0))

as `size_0-10`,

sum(ptd.ten_to_twenty) as `sum_10-20`,

size(filter(x IN collect(ptd.ten_to_twenty) WHERE x <> 0))

as `size_10-20`,

sum(ptd.twenty_to_thirty) as `sum_20-30`,

size(filter(x IN collect(ptd.twenty_to_thirty) WHERE x <> 0))

as `size_20-30`,

sum(ptd.thirty_to_end) as `sum_30+`,

size(filter(x IN collect(ptd.thirty_to_end) WHERE x <> 0))

as `size_30+`

RETURN name, stat,

`sum_0-10` / `size_0-10` as `0-10`,

`sum_10-20` / `size_10-20` as `10-20`,

`sum_20-30` / `size_20-30` as `20-30`,

`sum_30+` / `size_30+` as `30+`

ORDER BY name, stat

creeps/min
0
2
4
6
8
0-10 10-20 20-30 30+
Cryptael
MrMgr
gold/min
0
150
300
450
600
0-10 10-20 20-30 30+
Cryptael
MrMgr
Cryptael
MrMgr (#27 in EUNE)

Takeaways
•Neo4j is easy to use
•Build schema on the fly
•Cypher is a handy tool for exploration
•Find value in your data