$30 off During Our Annual Pro Sale. View Details »

League of Graphs \w Neo4j

League of Graphs \w Neo4j

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 allies?
• What is the most effective strategy to advance your champion?
• What enemy team setup is the most dangerous?
This session covers a rarely seen graph domain (gaming) and will provide you will solid, factual advice on how to play LoL.

https://github.com/FylmTM/league-of-legends-research

Dmitrijs Vrublevskis

May 26, 2016
Tweet

More Decks by Dmitrijs Vrublevskis

Other Decks in Programming

Transcript

  1. Name Surname
    [email protected]
    Presentation Title
    League of
    Graphs

    View Slide

  2. Dmitry Vrublevsky
    @Me
    Software engineer @
    ƀ [email protected]
    @FylmTM
    į vrublevsky.me

    View Slide

  3. View Slide

  4. View Slide

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

    View Slide

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

    View Slide

  7. View Slide

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

    View Slide

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

    View Slide

  10. View Slide

  11. Me
    Unranked Bronze Silver Gold
    Platinum Diamond Master Challenger

    View Slide

  12. View Slide

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

    View Slide

  14. View Slide

  15. 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());

    View Slide

  16. Neo4j
    Highly scalable native graph
    database that leverages data
    relationships as 

    first-class entities.
    by Neo Technology, Inc.

    View Slide

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

    View Slide

  18. GRAPH

    View Slide

  19. NOT GRAPH

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  28. 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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

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

    RETURN *

    View Slide

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

    View Slide

  36. Whiteboard friendly
    data modelling

    View Slide

  37. View Slide

  38. View Slide

  39. 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)

    View Slide

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

    View Slide

  41. Dependencies
    dependencies {

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

    compile 'org.neo4j:neo4j:2.3.3'

    }

    View Slide

  42. Create database
    File dbDir = new File(databasePath);

    GraphDatabaseService db = new GraphDatabaseFactory()

    .newEmbeddedDatabaseBuilder(dbDir)

    .newGraphDatabase();

    View Slide

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

    // Do cool stuff

    tx.success();

    }

    View Slide

  44. 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());

    View Slide

  45. In the end

    View Slide

  46. View Slide

  47. 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.

    View Slide

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

    View Slide

  49. Queries

    View Slide

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

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

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

    View Slide

  51. Query #2 Who 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

    View Slide

  52. Query #3 Most 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

    View Slide

  53. Alistar Jax

    View Slide

  54. View Slide

  55. Query #4 Any 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

    View Slide

  56. 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)

    View Slide

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

    View Slide

  58. View Slide