Upgrade to Pro — share decks privately, control downloads, hide ads and more …

An introduction to Graph Databases in PHP, using Neo4j

An introduction to Graph Databases in PHP, using Neo4j

Traditional relational databases — ironically — are not that good at the complex relationships some modern applications need.

Multiple joins and complex sub-queries can gradually take a toll on performance.

Graph Databases, on the other hand, are all about relationships. In this talk we will look at using the popular Neo4j graph database with PHP to build efficient relational data for OmNomHub: not your average recipe site.

Michelle Sanver

April 13, 2019
Tweet

More Decks by Michelle Sanver

Other Decks in Programming

Transcript

  1. ( @michellesanver )-[:LOVES]->(Neo4j)
    Neo4J is AWESOME

    View full-size slide

  2. ( @michellesanver )-[:LOVES]->(Neo4j)
    WIIIIIIIE \o/
    “Learn the most by sharing your
    knowledge with others”
    - @coderabbi

    View full-size slide

  3. This talk is entry-level
    ( @michellesanver )-[:LOVES]->(Neo4j)
    It’s Open Source

    View full-size slide

  4. ( @michellesanver )-[:LOVES]->(Neo4j)
    Accent(s)!?

    View full-size slide

  5. ( @michellesanver )-[:LOVES]->(Neo4j)

    View full-size slide

  6. ( @michellesanver )-[:LOVES]->(Neo4j)
    I’m a data nerd
    Symfony is one of my main tools

    View full-size slide

  7. ( @michellesanver )-[:LOVES]->(Neo4j)
    YOU
    New to Graph DBs?

    View full-size slide

  8. ( @michellesanver )-[:LOVES]->(Neo4j)
    neo4j.com
    An open source
    graph database from
    Sweden

    View full-size slide

  9. ( @michellesanver )-[:LOVES]->(Neo4j)
    Community Edition

    View full-size slide

  10. ( @michellesanver )-[:LOVES]->(Neo4j)
    Enterprise Edition

    View full-size slide

  11. ( @michellesanver )-[:LOVES]->(Neo4j)
    Meetups everywhere
    neo4j.meetup.com

    View full-size slide

  12. ( @michellesanver )-[:LOVES]->(Neo4j)
    Graph theory has
    been studied since
    Leonard Euler’s
    Bridges 1736

    View full-size slide

  13. ( @michellesanver )-[:LOVES]->(Neo4j)
    Body Text

    View full-size slide

  14. ( @michellesanver )-[:LOVES]->(Neo4j)

    View full-size slide

  15. ( @michellesanver )-[:LOVES]->(Neo4j)
    Graphs is data, that is connected…
    They are everywhere

    View full-size slide

  16. ( @michellesanver )-[:LOVES]->(Neo4j)
    EVERYWHERE

    View full-size slide

  17. ( @michellesanver )-[:LOVES]->(Neo4j)

    View full-size slide

  18. ( @michellesanver )-[:LOVES]->(Neo4j)
    It’s modern.

    View full-size slide

  19. ( @michellesanver )-[:LOVES]->(Neo4j)
    Facebook Open Graph

    View full-size slide

  20. ( @michellesanver )-[:LOVES]->(Neo4j)
    Graphs \o/

    View full-size slide

  21. ( @michellesanver )-[:LOVES]->(Neo4j)
    A graph is an easy way to visualise
    connected data.
    Michelle Graphs
    likes

    View full-size slide

  22. ( @michellesanver )-[:LOVES]->(Neo4j)
    ( Nodes )

    View full-size slide

  23. ( @michellesanver )-[:LOVES]->(Neo4j)
    (Node) { Properties }
    As many as you want
    Name:
    Michelle
    Nick:
    geekie
    Name:
    Cees-Jan
    Nick:
    WyriHaximus

    View full-size slide

  24. ( @michellesanver )-[:LOVES]->(Neo4j)
    Nick:
    geekie
    Nick:
    WyriHaximus
    knows
    (Node) [ Relationships ]
    As many as you want

    View full-size slide

  25. ( @michellesanver )-[:LOVES]->(Neo4j)
    Labels: As many as you want
    Nick:
    geekie
    Label: Person

    View full-size slide

  26. ( @michellesanver )-[:LOVES]->(Neo4j)
    Indexes for easy lookup

    View full-size slide

  27. ( @michellesanver )-[:LOVES]->(Neo4j)
    Constraints

    View full-size slide

  28. ( @michellesanver )-[:LOVES]->(Neo4j)
    Constraints + Indexes = Optional schema

    View full-size slide

  29. ( @michellesanver )-[:LOVES]->(Neo4j)
    Local McLaughlin graph

    View full-size slide

  30. ( @michellesanver )-[:LOVES]->(Neo4j)
    Diamond Butterfly Bull
    Franklin Tutte Wagner

    View full-size slide

  31. ( @michellesanver )-[:LOVES]->(Neo4j)
    You can make art out of your DB.
    (Don’t, or do?)

    View full-size slide

  32. ( @michellesanver )-[:LOVES]->(Neo4j)
    Graphs vs. Relational

    View full-size slide

  33. ( @michellesanver )-[:LOVES]->(Neo4j)
    Relational question:
    Get me all recipes for this user
    Graph question:
    What recipe should we recommend the user to cook next?

    View full-size slide

  34. ( @michellesanver )-[:LOVES]->(Neo4j)
    Relational databases have tables
    Recipes

    View full-size slide

  35. ( @michellesanver )-[:LOVES]->(Neo4j)
    Tables have relationships
    Recipes Groups

    View full-size slide

  36. ( @michellesanver )-[:LOVES]->(Neo4j)
    Which causes a join table…
    Recipes Groups
    RecipeToGroup

    View full-size slide

  37. ( @michellesanver )-[:LOVES]->(Neo4j)
    You query via the table
    Recipes Groups
    RecipeToGroup

    View full-size slide

  38. ( @michellesanver )-[:LOVES]->(Neo4j)
    Imagine *actual*
    relationships
    Pasta
    (group)
    Spaghetti

    Bolognese

    (recipe)
    Italian
    (group)
    is_in
    is_in

    View full-size slide

  39. ( @michellesanver )-[:LOVES]->(Neo4j)
    Relational vs. Graph
    (1) Spaghetti
    Bolognese
    (2) Pasta
    (1) Italian
    Recipes
    Groups
    RecipeToGroup
    recipeId:1 | groupId: 1
    recipeId:1 | groupId: 2
    Pasta
    (group)
    Spaghetti

    Bolognese

    (recipe)
    Italian
    (group)
    is_in
    is_in

    View full-size slide

  40. ( @michellesanver )-[:LOVES]->(Neo4j)
    (1) Name: Pasta …
    Ingredients
    (1) Spaghetti
    Bolognese
    (2) Pasta
    (1) Italian
    Recipes
    Groups
    RecipeToGroup
    recipeId:1 | groupId: 1
    recipeId:1 | groupId: 2
    IngredientId: 1
    RecipeId: 1
    Unit: g
    Amount: 500
    IngredientToRecipe
    (2) Name: Meat …
    IngredientId: 2
    RecipeId: 1
    Unit: g
    Amount: 800

    View full-size slide

  41. ( @michellesanver )-[:LOVES]->(Neo4j)
    Pasta
    (group)
    Italian
    (group)
    is_in
    is_in
    Italian
    (group)
    Meat
    (Ingredient)
    Pasta
    (Ingredient)
    contains
    amount: 500
    unit: g
    contains

    amount: 800
    unit: g
    Spaghetti

    Bolognese

    (recipe)

    View full-size slide

  42. ( @michellesanver )-[:LOVES]->(Neo4j)
    Joins in relational
    Performance degrades exponentially
    Corresponding in Neo4j
    Linear performance

    View full-size slide

  43. ( @michellesanver )-[:LOVES]->(Neo4j)
    Up to four million hops per second and core

    View full-size slide

  44. ( @michellesanver )-[:LOVES]->(Neo4j)
    Whiteboard Friendly

    View full-size slide

  45. ( @michellesanver )-[:LOVES]->(Neo4j)
    Relational databases have a structure

    View full-size slide

  46. ( @michellesanver )-[:LOVES]->(Neo4j)
    Neo4j has an optional structure

    View full-size slide

  47. ( @michellesanver )-[:LOVES]->(Neo4j)
    A big query.
    The EAV model.

    View full-size slide

  48. ( @michellesanver )-[:LOVES]->(Neo4j)
    SET @entityid = '3';
    SELECT ea.attribute_id,
    ea.attribute_code, eav.value AS
    'value', 'varchar' AS 'type'
    FROM catalog_category_entity e
    JOIN
    catalog_category_entity_varchar eav
    ON e.entity_id = eav.entity_id
    JOIN eav_attribute ea
    ON eav.attribute_id =
    ea.attribute_id
    WHERE e.entity_id = @entityid
    UNION
    SELECT ea.attribute_id,
    ea.attribute_code, eav.value AS
    'value', 'int' AS 'type'
    FROM catalog_category_entity
    e
    JOIN
    catalog_category_entity_int eav
    ON e.entity_id = eav.entity_id
    JOIN eav_attribute ea
    ON eav.attribute_id =
    ea.attribute_id
    WHERE e.entity_id = @entityid
    UNION

    View full-size slide

  49. ( @michellesanver )-[:LOVES]->(Neo4j)
    SELECT ea.attribute_id,
    ea.attribute_code, eav.value AS 'value',
    'decimal' AS 'type'
    FROM catalog_category_entity e
    JOIN catalog_category_entity_decimal
    eav
    ON e.entity_id = eav.entity_id
    JOIN eav_attribute ea
    ON eav.attribute_id = ea.attribute_id
    WHERE e.entity_id = @entityid
    UNION
    selecting ea.attribute_id, ea.attribute_code, eav.value AS 'value', 'datetime' AS
    'type'
    FROM catalog_category_entity e
    JOIN catalog_category_entity_datetime eav
    ON e.entity_id = eav.entity_id
    JOIN eav_attribute ea
    ON eav.attribute_id = ea.attribute_id
    WHERE e.entity_id = @entityid
    UNION
    SELECT ea.attribute_id, ea.attribute_code, eav.value AS 'value', 'text' AS 'type'
    FROM catalog_category_entity e
    JOIN catalog_category_entity_text eav
    ON e.entity_id = eav.entity_id
    JOIN eav_attribute ea
    ON eav.attribute_id = ea.attribute_id
    WHERE e.entity_id = @entityid

    View full-size slide

  50. ( @michellesanver )-[:LOVES]->(Neo4j)
    Flexibility in a structured database is expensive.

    View full-size slide

  51. Choose the right tool for the right job.

    View full-size slide

  52. ( @michellesanver )-[:LOVES]->(Neo4j)
    OmNomHub
    My pet project that is never
    done.

    View full-size slide

  53. ( @michellesanver )-[:LOVES]->(Neo4j)
    Fork A Recipe

    View full-size slide

  54. ( @michellesanver )-[:LOVES]->(Neo4j)
    Send change requests

    View full-size slide

  55. ( @michellesanver )-[:LOVES]->(Neo4j)
    Join groups and share recipe collections among
    them

    View full-size slide

  56. ( @michellesanver )-[:LOVES]->(Neo4j)
    Have your own private recipe collections

    View full-size slide

  57. ( @michellesanver )-[:LOVES]->(Neo4j)
    Search similar recipes
    (This is a part where the graph structure shines)

    View full-size slide

  58. ( @michellesanver )-[:LOVES]->(Neo4j)
    Neo4j architecture

    View full-size slide

  59. ( @michellesanver )-[:LOVES]->(Neo4j)
    Disks
    File System Cache
    Record Files
    Object Cache
    Cypher
    Core API
    Traversal API
    Transaction Management
    Transaction Log

    View full-size slide

  60. ( @michellesanver )-[:LOVES]->(Neo4j)
    Disks
    File System Cache
    Record Files
    Object Cache
    Cypher
    Core API
    Traversal API
    Transaction Management
    Transaction Log

    View full-size slide

  61. ( @michellesanver )-[:LOVES]->(Neo4j)
    Disks
    File System Cache
    Record Files
    Object Cache
    Cypher
    Core API
    Traversal API
    Transaction Management
    Transaction Log

    View full-size slide

  62. ( @michellesanver )-[:LOVES]->(Neo4j)
    Disks
    File System Cache
    Record Files
    Object Cache
    Cypher
    Core API
    Traversal API
    Transaction Management
    Transaction Log

    View full-size slide

  63. ( @michellesanver )-[:LOVES]->(Neo4j)
    Disks
    File System Cache
    Record Files
    Object Cache
    Cypher
    Core API
    Traversal API
    Transaction Management
    Transaction Log

    View full-size slide

  64. ( @michellesanver )-[:LOVES]->(Neo4j)
    Cypher
    Like SQL for graph databases.

    View full-size slide

  65. ( @michellesanver )-[:LOVES]->(Neo4j)
    )
    Parentheses means nodes
    (Or hugs)
    (

    View full-size slide

  66. ( @michellesanver )-[:LOVES]->(Neo4j)
    }
    Curly braces means
    properties
    {

    View full-size slide

  67. ( @michellesanver )-[:LOVES]->(Neo4j)
    ]
    Square brackets means
    relationships
    [

    View full-size slide

  68. ( @michellesanver )-[:LOVES]->(Neo4j)
    Makes it easy to visualise and
    query the data.
    Browser

    View full-size slide

  69. DEMO: Cypher and Browser
    ( @michellesanver )-[:LOVES]->(Neo4j)

    View full-size slide

  70. ( @michellesanver )-[:LOVES]->(Neo4j)
    Neo4j architecture
    Neostore File Storage

    View full-size slide

  71. ( @michellesanver )-[:LOVES]->(Neo4j)
    Neostore has several different store files

    View full-size slide

  72. ( @michellesanver )-[:LOVES]->(Neo4j)
    Each store has specific data

    View full-size slide

  73. ( @michellesanver )-[:LOVES]->(Neo4j)
    Nodes
    neostore.nodestore.db

    9 bytes

    View full-size slide

  74. ( @michellesanver )-[:LOVES]->(Neo4j)
    Neostore
    Fixed size === FAST

    View full-size slide

  75. ( @michellesanver )-[:LOVES]->(Neo4j)
    Relationships
    neostore.relationshipstore.db

    33 bytes

    View full-size slide

  76. ( @michellesanver )-[:LOVES]->(Neo4j)
    Properties
    neostore.propertystore.db

    neostore.propertystore.db.index

    neostore.propertystore.db.strings

    neostore.propertystore.db.arrays

    View full-size slide

  77. ( @michellesanver )-[:LOVES]->(Neo4j)
    Hardware matters!

    View full-size slide

  78. ( @michellesanver )-[:LOVES]->(Neo4j)
    Caching in memory, or
    filesystem

    View full-size slide

  79. ( @michellesanver )-[:LOVES]->(Neo4j)
    OmNomHub
    The future*
    * Maybe when my son moved away from home… In a land, far far away

    View full-size slide

  80. ( @michellesanver )-[:LOVES]->(Neo4j)
    Connecting users
    “You both like”

    View full-size slide

  81. ( @michellesanver )-[:LOVES]->(Neo4j)
    Connecting recipes
    “These have similar ingredients
    and user base”

    View full-size slide

  82. ( @michellesanver )-[:LOVES]->(Neo4j)
    Being smart
    “You might like”

    View full-size slide

  83. ( @michellesanver )-[:LOVES]->(Neo4j)
    Being smart
    Smart recipe collections!

    View full-size slide

  84. ( @michellesanver )-[:LOVES]->(Neo4j)
    Being creepy
    “Don’t like meat huh?”

    View full-size slide

  85. ( @michellesanver )-[:LOVES]->(Neo4j)
    Neo4j in PHP

    View full-size slide

  86. ( @michellesanver )-[:LOVES]->(Neo4j)
    As we saw
    There’s a REST interface!

    View full-size slide

  87. ( @michellesanver )-[:LOVES]->(Neo4j)
    GraphAware PHP Client
    https://github.com/graphaware/neo4j-php-client

    View full-size slide

  88. ( @michellesanver )-[:LOVES]->(Neo4j)
    $client = ClientBuilder::create()

    ->addConnection('bolt', 'bolt://neo4j:password@localhost:7687')

    ->build();

    $query = "MATCH (n:User) RETURN n.name”;

    $result = $client->run($query);


    foreach ($result->getRecords() as $record) {

    echo(count($record->value('name'));

    }


    View full-size slide

  89. ( @michellesanver )-[:LOVES]->(Neo4j)
    $client = ClientBuilder::create()

    ->addConnection('bolt', 'bolt://neo4j:password@localhost:7687')

    ->build();

    $query = "MATCH (n:User) RETURN n.name”;

    $result = $client->run($query);


    foreach ($result->getRecords() as $record) {

    echo(count($record->value('name'));

    }


    View full-size slide

  90. ( @michellesanver )-[:LOVES]->(Neo4j)
    $client = ClientBuilder::create()

    ->addConnection('bolt', 'bolt://neo4j:password@localhost:7687')

    ->build();

    $query = "MATCH (n:User) RETURN n.name”;

    $result = $client->run($query);


    foreach ($result->getRecords() as $record) {

    echo(count($record->value('name'));

    }


    View full-size slide

  91. ( @michellesanver )-[:LOVES]->(Neo4j)
    $client = ClientBuilder::create()

    ->addConnection('bolt', 'bolt://neo4j:password@localhost:7687')

    ->build();

    $query = "MATCH (n:User) RETURN n.name”;

    $result = $client->run($query);


    foreach ($result->getRecords() as $record) {

    echo(count($record->get('name'));

    }


    View full-size slide

  92. ( @michellesanver )-[:LOVES]->(Neo4j)
    GraphAware also released an OGM

    View full-size slide

  93. ( @michellesanver )-[:LOVES]->(Neo4j)
    /**

    * @OGM\GraphId()

    * @var int

    */

    protected $id;


    /**

    * @OGM\Property(type="string")

    * @var string

    */

    protected $recipeName;


    /**

    * @OGM\Relationship(type="FORKED", direction="OUTGOING",
    targetEntity="User", collection=true)

    * @var ArrayCollection|User[]

    */

    protected $users;

    View full-size slide

  94. ( @michellesanver )-[:LOVES]->(Neo4j)
    /**

    * @OGM\GraphId()

    * @var int

    */

    protected $id;


    /**

    * @OGM\Property(type="string")

    * @var string

    */

    protected $recipeName;


    /**

    * @OGM\Relationship(type="FORKED", direction="OUTGOING",
    targetEntity="User", collection=true)

    * @var ArrayCollection|User[]

    */

    protected $users;

    View full-size slide

  95. ( @michellesanver )-[:LOVES]->(Neo4j)
    /**

    * @OGM\GraphId()

    * @var int

    */

    protected $id;


    /**

    * @OGM\Property(type="string")

    * @var string

    */

    protected $recipeName;


    /**

    * @OGM\Relationship(type="FORKED", direction="OUTGOING",
    targetEntity="User", collection=true)

    * @var ArrayCollection|User[]

    */

    protected $users;

    View full-size slide

  96. ( @michellesanver )-[:LOVES]->(Neo4j)
    /**

    * @OGM\GraphId()

    * @var int

    */

    protected $id;


    /**

    * @OGM\Property(type="string")

    * @var string

    */

    protected $recipeName;


    /**

    * @OGM\Relationship(type="FORKED", direction="OUTGOING",
    targetEntity="User", collection=true)

    * @var ArrayCollection|User[]

    */

    protected $users;

    View full-size slide

  97. ( @michellesanver )-[:LOVES]->(Neo4j)
    $user = $em->getRepository(User::class)
    ->findOneBy(‘name’, 'Michelle');

    View full-size slide

  98. ( @michellesanver )-[:LOVES]->(Neo4j)
    Neo4j + PHP is well
    documented
    https://neo4j.com/developer/php/
    https://github.com/graphaware/neo4j-php-client
    https://github.com/graphaware/neo4j-php-ogm

    View full-size slide

  99. ( @michellesanver )-[:LOVES]->(Neo4j)
    Wrapup

    View full-size slide

  100. ( @michellesanver )-[:LOVES]->(Neo4j)
    Graphs are
    everywhere

    View full-size slide

  101. ( @michellesanver )-[:LOVES]->(Neo4j)
    Graphs make it easier to
    visualise complex data

    View full-size slide

  102. ( @michellesanver )-[:LOVES]->(Neo4j)
    Graphs make it easier
    to…
    Be creepily smart <3

    View full-size slide

  103. ( @michellesanver )-[:LOVES]->(Neo4j)
    Resources
    neo4j.org/learn
    neo4j.com/developer/php
    github.com/neo4j-contrib/neo4j-symfony

    View full-size slide

  104. ( @michellesanver )-[:LOVES]->(Neo4j)
    Thank you
    Questions?

    View full-size slide

  105. ( @michellesanver )-[:LOVES]->(Neo4j)
    YOU are AWESOME
    Thank you

    View full-size slide

  106. ( @michellesanver )-[:LOVES]->(Neo4j)
    Graph databases
    How would you use them?

    View full-size slide