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.

01da6d807a29ad6d49801c0157518148?s=128

Michelle Sanver

April 13, 2019
Tweet

Transcript

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

  2. ( @michellesanver )-[:LOVES]->(Neo4j) WIIIIIIIE \o/ “Learn the most by sharing

    your knowledge with others” - @coderabbi
  3. This talk is entry-level ( @michellesanver )-[:LOVES]->(Neo4j) It’s Open Source

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

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

  7. ( @michellesanver )-[:LOVES]->(Neo4j) I’m a data nerd Symfony is one

    of my main tools
  8. ( @michellesanver )-[:LOVES]->(Neo4j) YOU New to Graph DBs?

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

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

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

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

  13. ( @michellesanver )-[:LOVES]->(Neo4j) Graph theory has been studied since Leonard

    Euler’s Bridges 1736
  14. ( @michellesanver )-[:LOVES]->(Neo4j) Body Text

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

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

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

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

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

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

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

  22. ( @michellesanver )-[:LOVES]->(Neo4j) A graph is an easy way to

    visualise connected data. Michelle Graphs likes
  23. ( @michellesanver )-[:LOVES]->(Neo4j) ( Nodes )

  24. ( @michellesanver )-[:LOVES]->(Neo4j) (Node) { Properties } As many as

    you want Name: Michelle Nick: geekie Name: Cees-Jan Nick: WyriHaximus
  25. ( @michellesanver )-[:LOVES]->(Neo4j) Nick: geekie Nick: WyriHaximus knows (Node) [

    Relationships ] As many as you want
  26. ( @michellesanver )-[:LOVES]->(Neo4j) Labels: As many as you want Nick:

    geekie Label: Person
  27. ( @michellesanver )-[:LOVES]->(Neo4j) Indexes for easy lookup

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

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

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

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

  32. ( @michellesanver )-[:LOVES]->(Neo4j) You can make art out of your

    DB. (Don’t, or do?)
  33. ( @michellesanver )-[:LOVES]->(Neo4j) Graphs vs. Relational

  34. ( @michellesanver )-[:LOVES]->(Neo4j) Relational question: Get me all recipes for

    this user Graph question: What recipe should we recommend the user to cook next?
  35. ( @michellesanver )-[:LOVES]->(Neo4j) Relational databases have tables Recipes

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

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

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

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


    (recipe) Italian (group) is_in is_in
  40. ( @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
  41. ( @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
  42. ( @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)
  43. ( @michellesanver )-[:LOVES]->(Neo4j) Joins in relational Performance degrades exponentially Corresponding

    in Neo4j Linear performance
  44. ( @michellesanver )-[:LOVES]->(Neo4j) Up to four million hops per second

    and core
  45. ( @michellesanver )-[:LOVES]->(Neo4j) Whiteboard Friendly

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

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

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

  49. ( @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
  50. ( @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
  51. ( @michellesanver )-[:LOVES]->(Neo4j) Flexibility in a structured database is expensive.

  52. Choose the right tool for the right job.

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

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

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

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

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

  58. ( @michellesanver )-[:LOVES]->(Neo4j) Search similar recipes (This is a part

    where the graph structure shines)
  59. ( @michellesanver )-[:LOVES]->(Neo4j) Neo4j architecture

  60. ( @michellesanver )-[:LOVES]->(Neo4j) Disks File System Cache Record Files Object

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

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

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

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

    Cache Cypher Core API Traversal API Transaction Management Transaction Log
  65. ( @michellesanver )-[:LOVES]->(Neo4j) Cypher Like SQL for graph databases.

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

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

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

  69. ( @michellesanver )-[:LOVES]->(Neo4j) Makes it easy to visualise and query

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

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

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

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

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

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

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

  77. ( @michellesanver )-[:LOVES]->(Neo4j) Properties neostore.propertystore.db neostore.propertystore.db.index neostore.propertystore.db.strings neostore.propertystore.db.arrays

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

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

  80. ( @michellesanver )-[:LOVES]->(Neo4j) OmNomHub The future* * Maybe when my

    son moved away from home… In a land, far far away
  81. ( @michellesanver )-[:LOVES]->(Neo4j) Connecting users “You both like”

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

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

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

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

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

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

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

  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'));
 }

  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'));
 }

  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->value('name'));
 }

  92. ( @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'));
 }

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

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

  99. ( @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
  100. ( @michellesanver )-[:LOVES]->(Neo4j) Wrapup

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

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

    data
  103. ( @michellesanver )-[:LOVES]->(Neo4j) Graphs make it easier to… Be creepily

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

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

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

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