SlideShare a Scribd company logo
1 of 91
Download to read offline
Leveraging the Power of
Graph Databases
in PHP
Jeremy Kendall
Atlanta PHP
April 2015
Obligatory Intro Slide
Also - New Father
What Kind of
Database?
Graphs != Charts
https://www.flickr.com/photos/markgroves/3065192499/
Graphs != Charts
http://stephenwildish.tumblr.com/post/101408321763/friday-project-witch-moral-compass
Graph Databases
• Data Model
• Nodes with properties
• Typed relationships
• Strengths
• Highly connected data
• ACID
• Weaknesses
• Paradigm shift
• Examples
• Neo4j, Titan, OrientDB
Why Care?
• All the NoSQL Joy
• Schema-less
• Semi-structured data
• Escape from JOIN Hell
• Speed
Why Care?
• Relationships have 1st class status
• Just as important as the objects they connect
• You can have properties & labels
• Multiple relationships
Why Care?
Speed
Depth MySQL Query Time Neo4j Query Time Records Returned
2 0.028 (28 MS) 0.04 ~900
3 0.213 0.06 ~999
4 10.273 0.07 ~999
5 92.613 0.07 ~999
1,000 people with an average 50 friends each
Crazy Speed
Depth MySQL Query Time Neo4j Query Time Records Returned
2 0.016 (16 MS) 0.01 ~2500
3 30.27 0.168 ~125,000
4 1543.505 1.359 ~600,000
5 Stopped after 1 hour 2.132 ~800,000
1,000,000 people with an average 50 friends each
Neo4j + Cypher
Cypher
• Neo4j’s declarative query language
• Easy to pick up
• Some clauses and concepts familiar from SQL
Simple Example
Goal
Create Some Nodes
CREATE (jk:Person { name: "Jeremy Kendall" })
CREATE (gs:Company { name: "Graph Story" })
CREATE (tn:State { name: "Tennessee" })
CREATE (memphis:City { name: "Memphis" })
CREATE (nashville:City { name: "Nashville" })
CREATE (hotchicken:Food { name: "Hot Chicken" })
CREATE (bbq:Food { name: "Barbecue" })
CREATE (photography:Hobby { name: "Photography" })
CREATE (language:Language { name: "PHP" })
// . . . snip . . .
Create Some Relationships
// . . . snip . . .
CREATE (jk)-[:WORKS_AT { title: {"CTO"}]->(gs),
(jk)-[:LIVES_IN]->(memphis)-[:LIVED_IN]->(nashville),
(hotchicken)-[:ONLY_IN]->(nashville),
(bbq)-[:ONLY_IN]->(memphis),
(jk)-[:LOVES]->(hotchicken),
// . . . snip . . .
Create Some Relationships
// . . . snip . . .
CREATE (jk)-[:WORKS_AT { title: {"CTO"}]->(gs),
(jk)-[:LIVES_IN]->(memphis)-[:LIVED_IN]->(nashville),
(hotchicken)-[:ONLY_IN]->(nashville),
(bbq)-[:ONLY_IN]->(memphis),
(jk)-[:LOVES]->(hotchicken),
// . . . snip . . .
Create Some Relationships
// . . . snip . . .
CREATE (jk)-[:WORKS_AT { title: {"CTO"}]->(gs),
(jk)-[:LIVES_IN]->(memphis)-[:LIVED_IN]->(nashville),
(hotchicken)-[:ONLY_IN]->(nashville),
(bbq)-[:ONLY_IN]->(memphis),
(jk)-[:LOVES]->(hotchicken),
// . . . snip . . .
Create Some Relationships
// . . . snip . . .
CREATE (jk)-[:WORKS_AT { title: {"CTO"}]->(gs),
(jk)-[:LIVES_IN]->(memphis)-[:LIVED_IN]->(nashville),
(hotchicken)-[:ONLY_IN]->(nashville),
(bbq)-[:ONLY_IN]->(memphis),
(jk)-[:LOVES]->(hotchicken),
// . . . snip . . .
Example Cypher Query
MATCH (p:Person { name: "Jeremy Kendall" })-[:LOVES]->(l)
WITH p, l
MATCH (p)-[:WORKS_AT]->(j)
WITH p, l, j
MATCH (p)-[:LIVES_IN]->(c:City)-[:LIVED_IN*0..]->(o:City)
RETURN p, l, j, o
Example Cypher Query
MATCH (p:Person { name: "Jeremy Kendall" })-[:LOVES]->(l)
WITH p, l
MATCH (p)-[:WORKS_AT]->(j)
WITH p, l, j
MATCH (p)-[:LIVES_IN]->(c:City)-[:LIVED_IN*0..]->(o:City)
RETURN p, l, j, o
Example Cypher Query
MATCH (p:Person { name: "Jeremy Kendall" })-[:LOVES]->(l)
WITH p, l
MATCH (p)-[:WORKS_AT]->(j)
WITH p, l, j
MATCH (p)-[:LIVES_IN]->(c:City)-[:LIVED_IN*0..]->(o:City)
RETURN p, l, j, o
Example Cypher Query
MATCH (p:Person { name: "Jeremy Kendall" })-[:LOVES]->(l)
WITH p, l
MATCH (p)-[:WORKS_AT]->(j)
WITH p, l, j
MATCH (p)-[:LIVES_IN]->(c:City)-[:LIVED_IN*0..]->(o:City)
RETURN p, l, j, o
Example Cypher Query
MATCH (p:Person { name: "Jeremy Kendall" })-[:LOVES]->(l)
WITH p, l
MATCH (p)-[:WORKS_AT]->(j)
WITH p, l, j
MATCH (p)-[:LIVES_IN]->(c:City)-[:LIVED_IN*0..]->(o:City)
RETURN p, l, j, o
Example Cypher Query
MATCH (p:Person { name: "Jeremy Kendall" })-[:LOVES]->(l)
WITH p, l
MATCH (p)-[:WORKS_AT]->(j)
WITH p, l, j
MATCH (p)-[:LIVES_IN]->(c:City)-[:LIVED_IN*0..]->(o:City)
RETURN p, l, j, o
Example Cypher Query
MATCH (p:Person { name: "Jeremy Kendall" })-[:LOVES]->(l)
WITH p, l
MATCH (p)-[:WORKS_AT]->(j)
WITH p, l, j
MATCH (p)-[:LIVES_IN]->(c:City)-[:LIVED_IN*0..]->(o:City)
RETURN p, l, j, o
Query Result
However!
Easy to Model,
Challenging to Master
Subtle(-ish) Bug
Subtle(-ish) Bug
Neo4j + PHP
Neo4jPHP
• PHP wrapper for the Neo4j REST API
• Installable via Composer
• Used internally at Graph Story
• Used in this presentation
• Well tested
• https://packagist.org/packages/everyman/
neo4jphp
Also see: NeoClient
• Written by Neoxygen
• Alternative PHP wrapper for the Neo4j REST API
• Installable via Composer
• Accepted for internal use at Graph Story
• Well tested
• https://packagist.org/packages/neoxygen/neoclient
Connecting
$neo4jClient = new EverymanNeo4jClient(
‘yourgraph.example.com’,
7473
);
$neo4jClient->getTransport()
->setAuth('username', 'password')
->getTransport()->useHttps();
Creating a Node and Label
$node = new Node($neo4jClient);
$label = $neo4jClient->makeLabel('Person');
$node->setProperty('name', ‘Jeremy Kendall');
$node->save()->addLabels(array($label));
Searching
// Searching for a label by property
$label = $neo4jClient->makeLabel('Person');
$nodes = $label->getNodes('name', $name);
Querying (Cypher)
$queryString =
'MATCH (p:Person { name: { name }}) RETURN p';
$query = new EverymanNeo4jCypherQuery(
$neo4jClient,
$queryString,
['name' => ‘Jeremy Kendall']
);
$result = $query->getResultSet();
Named Parameters
Named Parameters
$queryString =
'MATCH (p:Person { name: { name }}) RETURN p';
$query = new EverymanNeo4jCypherQuery(
$neo4jClient,
$queryString,
['name' => ‘Jeremy Kendall']
);
$result = $query->getResultSet();
Named Parameters
$queryString =
'MATCH (p:Person { name: { name }}) RETURN p';
$query = new EverymanNeo4jCypherQuery(
$neo4jClient,
$queryString,
['name' => ‘Jeremy Kendall']
);
$result = $query->getResultSet();
Content Modeling:
News Feeds
Graph Kit for PHP
https://github.com/GraphStory/graph-kit-php
News Feed
• Modeled as a list of posts
• Newest post first
• All subsequent posts follow
• Relationships: LASTPOST and NEXTPOST
LASTPOST
NEXTPOST
The Content Model
class Content
{
public $node;
public $nodeId;
public $contentId;
public $title;
public $url;
public $tagstr;
public $timestamp;
public $userNameForPost;
public $owner = false;
}
Adding Content
public static function add($username, Content $content)
{
$queryString =<<<CYPHER
MATCH (user { username: {u}})
OPTIONAL MATCH (user)-[r:LASTPOST]->(lastpost)
DELETE r
CREATE (user)-[:LASTPOST]->(p:Content { title:{title}, url:{url}, tagstr:{tagstr}, timestamp:{timestamp}, contentId:
{contentId} })
WITH p, collect(lastpost) as lastposts
FOREACH (x IN lastposts | CREATE p-[:NEXTPOST]->x)
RETURN p, {u} as username, true as owner
CYPHER;
$query = new Query(
Neo4jClient::client(),
$queryString,
array(
'u' => $username,
'title' => $content->title,
'url' => $content->url,
'tagstr' => $content->tagstr,
'timestamp' => time(),
'contentId' => uniqid()
)
);
$result = $query->getResultSet();
return self::returnMappedContent($result);
}
Adding Content
public static function add($username, Content $content)
{
$queryString =<<<CYPHER
MATCH (user { username: {u}})
OPTIONAL MATCH (user)-[r:LASTPOST]->(lastpost)
DELETE r
CREATE (user)-[:LASTPOST]->(p:Content { title:{title}, url:{url}, tagstr:{tagstr}, timestamp:{timestamp}, contentId:
{contentId} })
WITH p, collect(lastpost) as lastposts
FOREACH (x IN lastposts | CREATE p-[:NEXTPOST]->x)
RETURN p, {u} as username, true as owner
CYPHER;
$query = new Query(
Neo4jClient::client(),
$queryString,
array(
'u' => $username,
'title' => $content->title,
'url' => $content->url,
'tagstr' => $content->tagstr,
'timestamp' => time(),
'contentId' => uniqid()
)
);
$result = $query->getResultSet();
return self::returnMappedContent($result);
}
Adding Content
public static function add($username, Content $content)
{
$queryString =<<<CYPHER
MATCH (user { username: {u}})
OPTIONAL MATCH (user)-[r:LASTPOST]->(lastpost)
DELETE r
CREATE (user)-[:LASTPOST]->(p:Content { title:{title}, url:{url}, tagstr:{tagstr}, timestamp:{timestamp}, contentId:
{contentId} })
WITH p, collect(lastpost) as lastposts
FOREACH (x IN lastposts | CREATE p-[:NEXTPOST]->x)
RETURN p, {u} as username, true as owner
CYPHER;
$query = new Query(
Neo4jClient::client(),
$queryString,
array(
'u' => $username,
'title' => $content->title,
'url' => $content->url,
'tagstr' => $content->tagstr,
'timestamp' => time(),
'contentId' => uniqid()
)
);
$result = $query->getResultSet();
return self::returnMappedContent($result);
}
Adding Content
MATCH (user { username: {u}})
OPTIONAL MATCH (user)-[r:LASTPOST]->(lastpost)
DELETE r
CREATE (user)-[:LASTPOST]->(p:Content { title:{title}, url:
{url}, tagstr:{tagstr}, timestamp:{timestamp}, contentId:
{contentId} })
WITH p, collect(lastpost) as lastposts
FOREACH (x IN lastposts | CREATE p-[:NEXTPOST]->x)
RETURN p, {u} as username, true as owner
Adding Content
MATCH (user { username: {u}})
OPTIONAL MATCH (user)-[r:LASTPOST]->(lastpost)
DELETE r
CREATE (user)-[:LASTPOST]->(p:Content { title:{title}, url:
{url}, tagstr:{tagstr}, timestamp:{timestamp}, contentId:
{contentId} })
WITH p, collect(lastpost) as lastposts
FOREACH (x IN lastposts | CREATE p-[:NEXTPOST]->x)
RETURN p, {u} as username, true as owner
Adding Content
MATCH (user { username: {u}})
OPTIONAL MATCH (user)-[r:LASTPOST]->(lastpost)
DELETE r
CREATE (user)-[:LASTPOST]->(p:Content { title:{title}, url:
{url}, tagstr:{tagstr}, timestamp:{timestamp}, contentId:
{contentId} })
WITH p, collect(lastpost) as lastposts
FOREACH (x IN lastposts | CREATE p-[:NEXTPOST]->x)
RETURN p, {u} as username, true as owner
Adding Content
MATCH (user { username: {u}})
OPTIONAL MATCH (user)-[r:LASTPOST]->(lastpost)
DELETE r
CREATE (user)-[:LASTPOST]->(p:Content { title:{title}, url:
{url}, tagstr:{tagstr}, timestamp:{timestamp}, contentId:
{contentId} })
WITH p, collect(lastpost) as lastposts
FOREACH (x IN lastposts | CREATE p-[:NEXTPOST]->x)
RETURN p, {u} as username, true as owner
Adding Content
MATCH (user { username: {u}})
OPTIONAL MATCH (user)-[r:LASTPOST]->(lastpost)
DELETE r
CREATE (user)-[:LASTPOST]->(p:Content { title:{title}, url:
{url}, tagstr:{tagstr}, timestamp:{timestamp}, contentId:
{contentId} })
WITH p, collect(lastpost) as lastposts
FOREACH (x IN lastposts | CREATE p-[:NEXTPOST]->x)
RETURN p, {u} as username, true as owner
Adding Content
MATCH (user { username: {u}})
OPTIONAL MATCH (user)-[r:LASTPOST]->(lastpost)
DELETE r
CREATE (user)-[:LASTPOST]->(p:Content { title:{title}, url:
{url}, tagstr:{tagstr}, timestamp:{timestamp}, contentId:
{contentId} })
WITH p, collect(lastpost) as lastposts
FOREACH (x IN lastposts | CREATE p-[:NEXTPOST]->x)
RETURN p, {u} as username, true as owner
Adding Content
$query = new Query(
$neo4jClient,
$queryString,
array(
'u' => $username,
'title' => $content->title,
'url' => $content->url,
'tagstr' => $content->tagstr,
'timestamp' => time(),
'contentId' => uniqid()
)
);
$result = $query->getResultSet();
Retrieving Content
public static function getContent($username, $skip)
{
$queryString = <<<CYPHER
MATCH (u:User { username: { u }})-[:FOLLOWS*0..1]->f
WITH DISTINCT f, u
MATCH f-[:LASTPOST]-lp-[:NEXTPOST*0..]-p
RETURN p, f.username as username, f = u as owner
ORDER BY p.timestamp desc SKIP { skip } LIMIT 4
CYPHER;
$query = new Query(
Neo4jClient::client(),
$queryString,
array(
'u' => $username,
'skip' => $skip,
)
);
$result = $query->getResultSet();
return self::returnMappedContent($result);
}
Retrieving Content
MATCH (u:User { username: { u }})-[:FOLLOWS*0..1]->f
WITH DISTINCT f, u
MATCH f-[:LASTPOST]-lp-[:NEXTPOST*0..]-p
RETURN p, f.username as username, f = u as owner
ORDER BY p.timestamp desc SKIP { skip } LIMIT 4
Retrieving Content
MATCH (u:User { username: { u }})-[:FOLLOWS*0..1]->f
WITH DISTINCT f, u
MATCH f-[:LASTPOST]-lp-[:NEXTPOST*0..]-p
RETURN p, f.username as username, f = u as owner
ORDER BY p.timestamp desc SKIP { skip } LIMIT 4
Retrieving Content
MATCH (u:User { username: { u }})-[:FOLLOWS*0..1]->f
WITH DISTINCT f, u
MATCH f-[:LASTPOST]-lp-[:NEXTPOST*0..]-p
RETURN p, f.username as username, f = u as owner
ORDER BY p.timestamp desc SKIP { skip } LIMIT 4
Retrieving Content
MATCH (u:User { username: { u }})-[:FOLLOWS*0..1]->f
WITH DISTINCT f, u
MATCH f-[:LASTPOST]-lp-[:NEXTPOST*0..]-p
RETURN p, f.username as username, f = u as owner
ORDER BY p.timestamp desc SKIP { skip } LIMIT 4
Retrieving Content
MATCH (u:User { username: { u }})-[:FOLLOWS*0..1]->f
WITH DISTINCT f, u
MATCH f-[:LASTPOST]-lp-[:NEXTPOST*0..]-p
RETURN p, f.username as username, f = u as owner
ORDER BY p.timestamp desc SKIP { skip } LIMIT 4
Retrieving Content
MATCH (u:User { username: { u }})-[:FOLLOWS*0..1]->f
WITH DISTINCT f, u
MATCH f-[:LASTPOST]-lp-[:NEXTPOST*0..]-p
RETURN p, f.username as username, f = u as owner
ORDER BY p.timestamp desc SKIP { skip } LIMIT 4
Editing Content
public static function edit(Content $content)
{
$updatedAt = time();
$node = $content->node;
$node->setProperty('title', $content->title);
$node->setProperty('url', $content->url);
$node->setProperty('tagstr', $content->tagstr);
$node->setProperty('updated', $updatedAt);
$node->save();
$content->updated = $updatedAt;
return $content;
}
Editing Content
public static function edit(Content $content)
{
$updatedAt = time();
$node = $content->node;
$node->setProperty('title', $content->title);
$node->setProperty('url', $content->url);
$node->setProperty('tagstr', $content->tagstr);
$node->setProperty('updated', $updatedAt);
$node->save();
$content->updated = $updatedAt;
return $content;
}
Editing Content
public static function edit(Content $content)
{
$updatedAt = time();
$node = $content->node;
$node->setProperty('title', $content->title);
$node->setProperty('url', $content->url);
$node->setProperty('tagstr', $content->tagstr);
$node->setProperty('updated', $updatedAt);
$node->save();
$content->updated = $updatedAt;
return $content;
}
Editing Content
public static function edit(Content $content)
{
$updatedAt = time();
$node = $content->node;
$node->setProperty('title', $content->title);
$node->setProperty('url', $content->url);
$node->setProperty('tagstr', $content->tagstr);
$node->setProperty('updated', $updatedAt);
$node->save();
$content->updated = $updatedAt;
return $content;
}
Editing Content
public static function edit(Content $content)
{
$updatedAt = time();
$node = $content->node;
$node->setProperty('title', $content->title);
$node->setProperty('url', $content->url);
$node->setProperty('tagstr', $content->tagstr);
$node->setProperty('updated', $updatedAt);
$node->save();
$content->updated = $updatedAt;
return $content;
}
Deleting Content
public static function delete($username, $contentId)
{
$queryString = self::getDeleteQueryString(
$username,
$contentId
);
$params = array(
'username' => $username,
'contentId' => $contentId,
);
$query = new Query(
$neo4jClient,
$queryString,
$params
);
$query->getResultSet();
}
Deleting Content
public static function delete($username, $contentId)
{
$queryString = self::getDeleteQueryString(
$username,
$contentId
);
$params = array(
'username' => $username,
'contentId' => $contentId,
);
$query = new Query(
$neo4jClient,
$queryString,
$params
);
$query->getResultSet();
}
Deleting Content
public static function delete($username, $contentId)
{
$queryString = self::getDeleteQueryString(
$username,
$contentId
);
$params = array(
'username' => $username,
'contentId' => $contentId,
);
$query = new Query(
$neo4jClient,
$queryString,
$params
);
$query->getResultSet();
}
Deleting Content
public static function delete($username, $contentId)
{
$queryString = self::getDeleteQueryString(
$username,
$contentId
);
$params = array(
'username' => $username,
'contentId' => $contentId,
);
$query = new Query(
$neo4jClient,
$queryString,
$params
);
$query->getResultSet();
}
Deleting Content: Leaf
// If leaf
MATCH (u:User { username: { username }})-[:LASTPOST|
NEXTPOST*0..]->(c:Content { contentId: { contentId }})
WITH c
MATCH (c)-[r]-()
DELETE c, r
Deleting Content: Leaf
// If leaf
MATCH (u:User { username: { username }})-[:LASTPOST|
NEXTPOST*0..]->(c:Content { contentId: { contentId }})
WITH c
MATCH (c)-[r]-()
DELETE c, r
Deleting Content: Leaf
// If leaf
MATCH (u:User { username: { username }})-[:LASTPOST|
NEXTPOST*0..]->(c:Content { contentId: { contentId }})
WITH c
MATCH (c)-[r]-()
DELETE c, r
Deleting Content: Leaf
// If leaf
MATCH (u:User { username: { username }})-[:LASTPOST|
NEXTPOST*0..]->(c:Content { contentId: { contentId }})
WITH c
MATCH (c)-[r]-()
DELETE c, r
Deleting Content: Leaf
// If leaf
MATCH (u:User { username: { username }})-[:LASTPOST|
NEXTPOST*0..]->(c:Content { contentId: { contentId }})
WITH c
MATCH (c)-[r]-()
DELETE c, r
Deleting Content: LASTPOST
// If last
MATCH (u:User { username: { username }})-[lp:LASTPOST]-
>(del:Content { contentId: { contentId }})-[np:NEXTPOST]-
>(nextPost)
CREATE UNIQUE (u)-[:LASTPOST]->(nextPost)
DELETE lp, del, np
Deleting Content: LASTPOST
// If last
MATCH (u:User { username: { username }})-[lp:LASTPOST]-
>(del:Content { contentId: { contentId }})-[np:NEXTPOST]-
>(nextPost)
CREATE UNIQUE (u)-[:LASTPOST]->(nextPost)
DELETE lp, del, np
Deleting Content: LASTPOST
// If last
MATCH (u:User { username: { username }})-[lp:LASTPOST]-
>(del:Content { contentId: { contentId }})-[np:NEXTPOST]-
>(nextPost)
CREATE UNIQUE (u)-[:LASTPOST]->(nextPost)
DELETE lp, del, np
Deleting Content: LASTPOST
// If last
MATCH (u:User { username: { username }})-[lp:LASTPOST]-
>(del:Content { contentId: { contentId }})-[np:NEXTPOST]-
>(nextPost)
CREATE UNIQUE (u)-[:LASTPOST]->(nextPost)
DELETE lp, del, np
Deleting Content: Other
// All other
MATCH (u:User { username: { username }})-[:LASTPOST|
NEXTPOST*0..]->(before),
(before)-[delBefore]->(del:Content { contentId:
{ contentId }})-[delAfter]->(after)
CREATE UNIQUE (before)-[:NEXTPOST]->(after)
DELETE del, delBefore, delAfter
Deleting Content: Other
// All other
MATCH (u:User { username: { username }})-[:LASTPOST|
NEXTPOST*0..]->(before),
(before)-[delBefore]->(del:Content { contentId:
{ contentId }})-[delAfter]->(after)
CREATE UNIQUE (before)-[:NEXTPOST]->(after)
DELETE del, delBefore, delAfter
Deleting Content: Other
// All other
MATCH (u:User { username: { username }})-[:LASTPOST|
NEXTPOST*0..]->(before),
(before)-[delBefore]->(del:Content { contentId:
{ contentId }})-[delAfter]->(after)
CREATE UNIQUE (before)-[:NEXTPOST]->(after)
DELETE del, delBefore, delAfter
Deleting Content: Other
// All other
MATCH (u:User { username: { username }})-[:LASTPOST|
NEXTPOST*0..]->(before),
(before)-[delBefore]->(del:Content { contentId:
{ contentId }})-[delAfter]->(after)
CREATE UNIQUE (before)-[:NEXTPOST]->(after)
DELETE del, delBefore, delAfter
Deleting Content: Other
// All other
MATCH (u:User { username: { username }})-[:LASTPOST|
NEXTPOST*0..]->(before),
(before)-[delBefore]->(del:Content { contentId:
{ contentId }})-[delAfter]->(after)
CREATE UNIQUE (before)-[:NEXTPOST]->(after)
DELETE del, delBefore, delAfter
Questions?
Thanks!
jeremy.kendall@graphstory.com
@JeremyKendall
http://www.graphstory.com

More Related Content

What's hot

(Ab)Using the MetaCPAN API for Fun and Profit
(Ab)Using the MetaCPAN API for Fun and Profit(Ab)Using the MetaCPAN API for Fun and Profit
(Ab)Using the MetaCPAN API for Fun and Profit
Olaf Alders
 
Tips of CakePHP and MongoDB - Cakefest2011 ichikaway
Tips of CakePHP and MongoDB - Cakefest2011 ichikaway Tips of CakePHP and MongoDB - Cakefest2011 ichikaway
Tips of CakePHP and MongoDB - Cakefest2011 ichikaway
ichikaway
 
jQuery%20on%20Rails%20Presentation
jQuery%20on%20Rails%20PresentationjQuery%20on%20Rails%20Presentation
jQuery%20on%20Rails%20Presentation
guestcf600a
 
10gen Presents Schema Design and Data Modeling
10gen Presents Schema Design and Data Modeling10gen Presents Schema Design and Data Modeling
10gen Presents Schema Design and Data Modeling
DATAVERSITY
 

What's hot (16)

(Ab)Using the MetaCPAN API for Fun and Profit
(Ab)Using the MetaCPAN API for Fun and Profit(Ab)Using the MetaCPAN API for Fun and Profit
(Ab)Using the MetaCPAN API for Fun and Profit
 
Tips of CakePHP and MongoDB - Cakefest2011 ichikaway
Tips of CakePHP and MongoDB - Cakefest2011 ichikaway Tips of CakePHP and MongoDB - Cakefest2011 ichikaway
Tips of CakePHP and MongoDB - Cakefest2011 ichikaway
 
Terms of endearment - the ElasticSearch Query DSL explained
Terms of endearment - the ElasticSearch Query DSL explainedTerms of endearment - the ElasticSearch Query DSL explained
Terms of endearment - the ElasticSearch Query DSL explained
 
Fantom and Tales
Fantom and TalesFantom and Tales
Fantom and Tales
 
RESTFUL SERVICES MADE EASY: THE EVE REST API FRAMEWORK - Nicola Iarocci - Co...
RESTFUL SERVICES MADE EASY: THE EVE REST API FRAMEWORK -  Nicola Iarocci - Co...RESTFUL SERVICES MADE EASY: THE EVE REST API FRAMEWORK -  Nicola Iarocci - Co...
RESTFUL SERVICES MADE EASY: THE EVE REST API FRAMEWORK - Nicola Iarocci - Co...
 
PHP Tutorial (funtion)
PHP Tutorial (funtion)PHP Tutorial (funtion)
PHP Tutorial (funtion)
 
Current state-of-php
Current state-of-phpCurrent state-of-php
Current state-of-php
 
Fazendo mágica com ElasticSearch
Fazendo mágica com ElasticSearchFazendo mágica com ElasticSearch
Fazendo mágica com ElasticSearch
 
How to use MongoDB with CakePHP
How to use MongoDB with CakePHPHow to use MongoDB with CakePHP
How to use MongoDB with CakePHP
 
jQuery%20on%20Rails%20Presentation
jQuery%20on%20Rails%20PresentationjQuery%20on%20Rails%20Presentation
jQuery%20on%20Rails%20Presentation
 
10gen Presents Schema Design and Data Modeling
10gen Presents Schema Design and Data Modeling10gen Presents Schema Design and Data Modeling
10gen Presents Schema Design and Data Modeling
 
[WLDN] Supercharging word press development in 2018
[WLDN] Supercharging word press development in 2018[WLDN] Supercharging word press development in 2018
[WLDN] Supercharging word press development in 2018
 
Raleigh Web Design Meetup Group - Sass Presentation
Raleigh Web Design Meetup Group - Sass PresentationRaleigh Web Design Meetup Group - Sass Presentation
Raleigh Web Design Meetup Group - Sass Presentation
 
My First Ruby
My First RubyMy First Ruby
My First Ruby
 
SPL - The Undiscovered Library - PHPBarcelona 2015
SPL - The Undiscovered Library - PHPBarcelona 2015SPL - The Undiscovered Library - PHPBarcelona 2015
SPL - The Undiscovered Library - PHPBarcelona 2015
 
How else can you write the code in PHP?
How else can you write the code in PHP?How else can you write the code in PHP?
How else can you write the code in PHP?
 

Viewers also liked

Tdd in php a brief example
Tdd in php   a brief exampleTdd in php   a brief example
Tdd in php a brief example
Jeremy Kendall
 

Viewers also liked (8)

Django a whirlwind tour
Django   a whirlwind tourDjango   a whirlwind tour
Django a whirlwind tour
 
PHP 102: Out with the Bad, In with the Good
PHP 102: Out with the Bad, In with the GoodPHP 102: Out with the Bad, In with the Good
PHP 102: Out with the Bad, In with the Good
 
Tdd in php a brief example
Tdd in php   a brief exampleTdd in php   a brief example
Tdd in php a brief example
 
Slim RedBeanPHP and Knockout
Slim RedBeanPHP and KnockoutSlim RedBeanPHP and Knockout
Slim RedBeanPHP and Knockout
 
Zero to SOLID
Zero to SOLIDZero to SOLID
Zero to SOLID
 
Php 101: PDO
Php 101: PDOPhp 101: PDO
Php 101: PDO
 
PHP Data Objects
PHP Data ObjectsPHP Data Objects
PHP Data Objects
 
TDC SP 2015 - PHP7: melhor e mais rápido
TDC SP 2015 - PHP7: melhor e mais rápidoTDC SP 2015 - PHP7: melhor e mais rápido
TDC SP 2015 - PHP7: melhor e mais rápido
 

Similar to Leveraging the Power of Graph Databases in PHP

Zendcon 2007 Api Design
Zendcon 2007 Api DesignZendcon 2007 Api Design
Zendcon 2007 Api Design
unodelostrece
 
Remedie: Building a desktop app with HTTP::Engine, SQLite and jQuery
Remedie: Building a desktop app with HTTP::Engine, SQLite and jQueryRemedie: Building a desktop app with HTTP::Engine, SQLite and jQuery
Remedie: Building a desktop app with HTTP::Engine, SQLite and jQuery
Tatsuhiko Miyagawa
 

Similar to Leveraging the Power of Graph Databases in PHP (20)

Leveraging the Power of Graph Databases in PHP
Leveraging the Power of Graph Databases in PHPLeveraging the Power of Graph Databases in PHP
Leveraging the Power of Graph Databases in PHP
 
DBIx::Skinnyと仲間たち
DBIx::Skinnyと仲間たちDBIx::Skinnyと仲間たち
DBIx::Skinnyと仲間たち
 
Hands On Spring Data
Hands On Spring DataHands On Spring Data
Hands On Spring Data
 
Great Developers Steal
Great Developers StealGreat Developers Steal
Great Developers Steal
 
PHP API
PHP APIPHP API
PHP API
 
JSON and the APInauts
JSON and the APInautsJSON and the APInauts
JSON and the APInauts
 
Building Go Web Apps
Building Go Web AppsBuilding Go Web Apps
Building Go Web Apps
 
pyscript_django.pdf
pyscript_django.pdfpyscript_django.pdf
pyscript_django.pdf
 
Zendcon 2007 Api Design
Zendcon 2007 Api DesignZendcon 2007 Api Design
Zendcon 2007 Api Design
 
CouchDB Mobile - From Couch to 5K in 1 Hour
CouchDB Mobile - From Couch to 5K in 1 HourCouchDB Mobile - From Couch to 5K in 1 Hour
CouchDB Mobile - From Couch to 5K in 1 Hour
 
Django
DjangoDjango
Django
 
Remedie: Building a desktop app with HTTP::Engine, SQLite and jQuery
Remedie: Building a desktop app with HTTP::Engine, SQLite and jQueryRemedie: Building a desktop app with HTTP::Engine, SQLite and jQuery
Remedie: Building a desktop app with HTTP::Engine, SQLite and jQuery
 
Eve - REST API for Humans™
Eve - REST API for Humans™Eve - REST API for Humans™
Eve - REST API for Humans™
 
jQuery Makes Writing JavaScript Fun Again (for HTML5 User Group)
jQuery Makes Writing JavaScript Fun Again (for HTML5 User Group)jQuery Makes Writing JavaScript Fun Again (for HTML5 User Group)
jQuery Makes Writing JavaScript Fun Again (for HTML5 User Group)
 
Tomer Elmalem - GraphQL APIs: REST in Peace - Codemotion Milan 2017
Tomer Elmalem - GraphQL APIs: REST in Peace - Codemotion Milan 2017Tomer Elmalem - GraphQL APIs: REST in Peace - Codemotion Milan 2017
Tomer Elmalem - GraphQL APIs: REST in Peace - Codemotion Milan 2017
 
Express Presentation
Express PresentationExpress Presentation
Express Presentation
 
Jersey
JerseyJersey
Jersey
 
PostgreSQL Open SV 2018
PostgreSQL Open SV 2018PostgreSQL Open SV 2018
PostgreSQL Open SV 2018
 
Schema Design with MongoDB
Schema Design with MongoDBSchema Design with MongoDB
Schema Design with MongoDB
 
Routing @ Scuk.cz
Routing @ Scuk.czRouting @ Scuk.cz
Routing @ Scuk.cz
 

More from Jeremy Kendall

Game Changing Dependency Management
Game Changing Dependency ManagementGame Changing Dependency Management
Game Changing Dependency Management
Jeremy Kendall
 
Keeping it Small: Getting to know the Slim Micro Framework
Keeping it Small: Getting to know the Slim Micro FrameworkKeeping it Small: Getting to know the Slim Micro Framework
Keeping it Small: Getting to know the Slim Micro Framework
Jeremy Kendall
 
Keeping it small: Getting to know the Slim micro framework
Keeping it small: Getting to know the Slim micro frameworkKeeping it small: Getting to know the Slim micro framework
Keeping it small: Getting to know the Slim micro framework
Jeremy Kendall
 
Intro to #memtech PHP 2011-12-05
Intro to #memtech PHP   2011-12-05Intro to #memtech PHP   2011-12-05
Intro to #memtech PHP 2011-12-05
Jeremy Kendall
 
TDD in PHP - Memphis PHP 2011-08-25
TDD in PHP - Memphis PHP 2011-08-25TDD in PHP - Memphis PHP 2011-08-25
TDD in PHP - Memphis PHP 2011-08-25
Jeremy Kendall
 
Zend_Form to the Rescue - A Brief Introduction to Zend_Form
Zend_Form to the Rescue - A Brief Introduction to Zend_FormZend_Form to the Rescue - A Brief Introduction to Zend_Form
Zend_Form to the Rescue - A Brief Introduction to Zend_Form
Jeremy Kendall
 
Zero to Zend Framework in 10 minutes
Zero to Zend Framework in 10 minutesZero to Zend Framework in 10 minutes
Zero to Zend Framework in 10 minutes
Jeremy Kendall
 

More from Jeremy Kendall (11)

5 Ways to Awesome-ize Your (PHP) Code
5 Ways to Awesome-ize Your (PHP) Code5 Ways to Awesome-ize Your (PHP) Code
5 Ways to Awesome-ize Your (PHP) Code
 
Game Changing Dependency Management
Game Changing Dependency ManagementGame Changing Dependency Management
Game Changing Dependency Management
 
Keeping it small - Getting to know the Slim PHP micro framework
Keeping it small - Getting to know the Slim PHP micro frameworkKeeping it small - Getting to know the Slim PHP micro framework
Keeping it small - Getting to know the Slim PHP micro framework
 
Keeping it Small: Getting to know the Slim Micro Framework
Keeping it Small: Getting to know the Slim Micro FrameworkKeeping it Small: Getting to know the Slim Micro Framework
Keeping it Small: Getting to know the Slim Micro Framework
 
Keeping it small: Getting to know the Slim micro framework
Keeping it small: Getting to know the Slim micro frameworkKeeping it small: Getting to know the Slim micro framework
Keeping it small: Getting to know the Slim micro framework
 
Intro to #memtech PHP 2011-12-05
Intro to #memtech PHP   2011-12-05Intro to #memtech PHP   2011-12-05
Intro to #memtech PHP 2011-12-05
 
TDD in PHP - Memphis PHP 2011-08-25
TDD in PHP - Memphis PHP 2011-08-25TDD in PHP - Memphis PHP 2011-08-25
TDD in PHP - Memphis PHP 2011-08-25
 
Zend_Form to the Rescue - A Brief Introduction to Zend_Form
Zend_Form to the Rescue - A Brief Introduction to Zend_FormZend_Form to the Rescue - A Brief Introduction to Zend_Form
Zend_Form to the Rescue - A Brief Introduction to Zend_Form
 
Zero to ZF in 10 Minutes
Zero to ZF in 10 MinutesZero to ZF in 10 Minutes
Zero to ZF in 10 Minutes
 
A Brief Introduction to Zend_Form
A Brief Introduction to Zend_FormA Brief Introduction to Zend_Form
A Brief Introduction to Zend_Form
 
Zero to Zend Framework in 10 minutes
Zero to Zend Framework in 10 minutesZero to Zend Framework in 10 minutes
Zero to Zend Framework in 10 minutes
 

Recently uploaded

Recently uploaded (20)

08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
 
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
 
What Are The Drone Anti-jamming Systems Technology?
What Are The Drone Anti-jamming Systems Technology?What Are The Drone Anti-jamming Systems Technology?
What Are The Drone Anti-jamming Systems Technology?
 
Partners Life - Insurer Innovation Award 2024
Partners Life - Insurer Innovation Award 2024Partners Life - Insurer Innovation Award 2024
Partners Life - Insurer Innovation Award 2024
 
How to convert PDF to text with Nanonets
How to convert PDF to text with NanonetsHow to convert PDF to text with Nanonets
How to convert PDF to text with Nanonets
 
Driving Behavioral Change for Information Management through Data-Driven Gree...
Driving Behavioral Change for Information Management through Data-Driven Gree...Driving Behavioral Change for Information Management through Data-Driven Gree...
Driving Behavioral Change for Information Management through Data-Driven Gree...
 
Tech Trends Report 2024 Future Today Institute.pdf
Tech Trends Report 2024 Future Today Institute.pdfTech Trends Report 2024 Future Today Institute.pdf
Tech Trends Report 2024 Future Today Institute.pdf
 
🐬 The future of MySQL is Postgres 🐘
🐬  The future of MySQL is Postgres   🐘🐬  The future of MySQL is Postgres   🐘
🐬 The future of MySQL is Postgres 🐘
 
08448380779 Call Girls In Greater Kailash - I Women Seeking Men
08448380779 Call Girls In Greater Kailash - I Women Seeking Men08448380779 Call Girls In Greater Kailash - I Women Seeking Men
08448380779 Call Girls In Greater Kailash - I Women Seeking Men
 
Strategies for Landing an Oracle DBA Job as a Fresher
Strategies for Landing an Oracle DBA Job as a FresherStrategies for Landing an Oracle DBA Job as a Fresher
Strategies for Landing an Oracle DBA Job as a Fresher
 
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
 
08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking Men08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking Men
 
Boost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdfBoost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdf
 
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemkeProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
 
Data Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt RobisonData Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt Robison
 
GenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day PresentationGenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day Presentation
 
TrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law DevelopmentsTrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
 
Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot TakeoffStrategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected Worker
 
Scaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organizationScaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organization
 

Leveraging the Power of Graph Databases in PHP

  • 1. Leveraging the Power of Graph Databases in PHP Jeremy Kendall Atlanta PHP April 2015
  • 3. Also - New Father
  • 7. Graph Databases • Data Model • Nodes with properties • Typed relationships • Strengths • Highly connected data • ACID • Weaknesses • Paradigm shift • Examples • Neo4j, Titan, OrientDB
  • 8. Why Care? • All the NoSQL Joy • Schema-less • Semi-structured data • Escape from JOIN Hell • Speed
  • 9. Why Care? • Relationships have 1st class status • Just as important as the objects they connect • You can have properties & labels • Multiple relationships
  • 11. Speed Depth MySQL Query Time Neo4j Query Time Records Returned 2 0.028 (28 MS) 0.04 ~900 3 0.213 0.06 ~999 4 10.273 0.07 ~999 5 92.613 0.07 ~999 1,000 people with an average 50 friends each
  • 12. Crazy Speed Depth MySQL Query Time Neo4j Query Time Records Returned 2 0.016 (16 MS) 0.01 ~2500 3 30.27 0.168 ~125,000 4 1543.505 1.359 ~600,000 5 Stopped after 1 hour 2.132 ~800,000 1,000,000 people with an average 50 friends each
  • 13.
  • 15. Cypher • Neo4j’s declarative query language • Easy to pick up • Some clauses and concepts familiar from SQL
  • 17. Goal
  • 18. Create Some Nodes CREATE (jk:Person { name: "Jeremy Kendall" }) CREATE (gs:Company { name: "Graph Story" }) CREATE (tn:State { name: "Tennessee" }) CREATE (memphis:City { name: "Memphis" }) CREATE (nashville:City { name: "Nashville" }) CREATE (hotchicken:Food { name: "Hot Chicken" }) CREATE (bbq:Food { name: "Barbecue" }) CREATE (photography:Hobby { name: "Photography" }) CREATE (language:Language { name: "PHP" }) // . . . snip . . .
  • 19. Create Some Relationships // . . . snip . . . CREATE (jk)-[:WORKS_AT { title: {"CTO"}]->(gs), (jk)-[:LIVES_IN]->(memphis)-[:LIVED_IN]->(nashville), (hotchicken)-[:ONLY_IN]->(nashville), (bbq)-[:ONLY_IN]->(memphis), (jk)-[:LOVES]->(hotchicken), // . . . snip . . .
  • 20. Create Some Relationships // . . . snip . . . CREATE (jk)-[:WORKS_AT { title: {"CTO"}]->(gs), (jk)-[:LIVES_IN]->(memphis)-[:LIVED_IN]->(nashville), (hotchicken)-[:ONLY_IN]->(nashville), (bbq)-[:ONLY_IN]->(memphis), (jk)-[:LOVES]->(hotchicken), // . . . snip . . .
  • 21. Create Some Relationships // . . . snip . . . CREATE (jk)-[:WORKS_AT { title: {"CTO"}]->(gs), (jk)-[:LIVES_IN]->(memphis)-[:LIVED_IN]->(nashville), (hotchicken)-[:ONLY_IN]->(nashville), (bbq)-[:ONLY_IN]->(memphis), (jk)-[:LOVES]->(hotchicken), // . . . snip . . .
  • 22. Create Some Relationships // . . . snip . . . CREATE (jk)-[:WORKS_AT { title: {"CTO"}]->(gs), (jk)-[:LIVES_IN]->(memphis)-[:LIVED_IN]->(nashville), (hotchicken)-[:ONLY_IN]->(nashville), (bbq)-[:ONLY_IN]->(memphis), (jk)-[:LOVES]->(hotchicken), // . . . snip . . .
  • 23. Example Cypher Query MATCH (p:Person { name: "Jeremy Kendall" })-[:LOVES]->(l) WITH p, l MATCH (p)-[:WORKS_AT]->(j) WITH p, l, j MATCH (p)-[:LIVES_IN]->(c:City)-[:LIVED_IN*0..]->(o:City) RETURN p, l, j, o
  • 24. Example Cypher Query MATCH (p:Person { name: "Jeremy Kendall" })-[:LOVES]->(l) WITH p, l MATCH (p)-[:WORKS_AT]->(j) WITH p, l, j MATCH (p)-[:LIVES_IN]->(c:City)-[:LIVED_IN*0..]->(o:City) RETURN p, l, j, o
  • 25. Example Cypher Query MATCH (p:Person { name: "Jeremy Kendall" })-[:LOVES]->(l) WITH p, l MATCH (p)-[:WORKS_AT]->(j) WITH p, l, j MATCH (p)-[:LIVES_IN]->(c:City)-[:LIVED_IN*0..]->(o:City) RETURN p, l, j, o
  • 26. Example Cypher Query MATCH (p:Person { name: "Jeremy Kendall" })-[:LOVES]->(l) WITH p, l MATCH (p)-[:WORKS_AT]->(j) WITH p, l, j MATCH (p)-[:LIVES_IN]->(c:City)-[:LIVED_IN*0..]->(o:City) RETURN p, l, j, o
  • 27. Example Cypher Query MATCH (p:Person { name: "Jeremy Kendall" })-[:LOVES]->(l) WITH p, l MATCH (p)-[:WORKS_AT]->(j) WITH p, l, j MATCH (p)-[:LIVES_IN]->(c:City)-[:LIVED_IN*0..]->(o:City) RETURN p, l, j, o
  • 28. Example Cypher Query MATCH (p:Person { name: "Jeremy Kendall" })-[:LOVES]->(l) WITH p, l MATCH (p)-[:WORKS_AT]->(j) WITH p, l, j MATCH (p)-[:LIVES_IN]->(c:City)-[:LIVED_IN*0..]->(o:City) RETURN p, l, j, o
  • 29. Example Cypher Query MATCH (p:Person { name: "Jeremy Kendall" })-[:LOVES]->(l) WITH p, l MATCH (p)-[:WORKS_AT]->(j) WITH p, l, j MATCH (p)-[:LIVES_IN]->(c:City)-[:LIVED_IN*0..]->(o:City) RETURN p, l, j, o
  • 36. Neo4jPHP • PHP wrapper for the Neo4j REST API • Installable via Composer • Used internally at Graph Story • Used in this presentation • Well tested • https://packagist.org/packages/everyman/ neo4jphp
  • 37. Also see: NeoClient • Written by Neoxygen • Alternative PHP wrapper for the Neo4j REST API • Installable via Composer • Accepted for internal use at Graph Story • Well tested • https://packagist.org/packages/neoxygen/neoclient
  • 38. Connecting $neo4jClient = new EverymanNeo4jClient( ‘yourgraph.example.com’, 7473 ); $neo4jClient->getTransport() ->setAuth('username', 'password') ->getTransport()->useHttps();
  • 39. Creating a Node and Label $node = new Node($neo4jClient); $label = $neo4jClient->makeLabel('Person'); $node->setProperty('name', ‘Jeremy Kendall'); $node->save()->addLabels(array($label));
  • 40. Searching // Searching for a label by property $label = $neo4jClient->makeLabel('Person'); $nodes = $label->getNodes('name', $name);
  • 41. Querying (Cypher) $queryString = 'MATCH (p:Person { name: { name }}) RETURN p'; $query = new EverymanNeo4jCypherQuery( $neo4jClient, $queryString, ['name' => ‘Jeremy Kendall'] ); $result = $query->getResultSet();
  • 43. Named Parameters $queryString = 'MATCH (p:Person { name: { name }}) RETURN p'; $query = new EverymanNeo4jCypherQuery( $neo4jClient, $queryString, ['name' => ‘Jeremy Kendall'] ); $result = $query->getResultSet();
  • 44. Named Parameters $queryString = 'MATCH (p:Person { name: { name }}) RETURN p'; $query = new EverymanNeo4jCypherQuery( $neo4jClient, $queryString, ['name' => ‘Jeremy Kendall'] ); $result = $query->getResultSet();
  • 45. Content Modeling: News Feeds Graph Kit for PHP https://github.com/GraphStory/graph-kit-php
  • 46. News Feed • Modeled as a list of posts • Newest post first • All subsequent posts follow • Relationships: LASTPOST and NEXTPOST
  • 49. The Content Model class Content { public $node; public $nodeId; public $contentId; public $title; public $url; public $tagstr; public $timestamp; public $userNameForPost; public $owner = false; }
  • 50. Adding Content public static function add($username, Content $content) { $queryString =<<<CYPHER MATCH (user { username: {u}}) OPTIONAL MATCH (user)-[r:LASTPOST]->(lastpost) DELETE r CREATE (user)-[:LASTPOST]->(p:Content { title:{title}, url:{url}, tagstr:{tagstr}, timestamp:{timestamp}, contentId: {contentId} }) WITH p, collect(lastpost) as lastposts FOREACH (x IN lastposts | CREATE p-[:NEXTPOST]->x) RETURN p, {u} as username, true as owner CYPHER; $query = new Query( Neo4jClient::client(), $queryString, array( 'u' => $username, 'title' => $content->title, 'url' => $content->url, 'tagstr' => $content->tagstr, 'timestamp' => time(), 'contentId' => uniqid() ) ); $result = $query->getResultSet(); return self::returnMappedContent($result); }
  • 51. Adding Content public static function add($username, Content $content) { $queryString =<<<CYPHER MATCH (user { username: {u}}) OPTIONAL MATCH (user)-[r:LASTPOST]->(lastpost) DELETE r CREATE (user)-[:LASTPOST]->(p:Content { title:{title}, url:{url}, tagstr:{tagstr}, timestamp:{timestamp}, contentId: {contentId} }) WITH p, collect(lastpost) as lastposts FOREACH (x IN lastposts | CREATE p-[:NEXTPOST]->x) RETURN p, {u} as username, true as owner CYPHER; $query = new Query( Neo4jClient::client(), $queryString, array( 'u' => $username, 'title' => $content->title, 'url' => $content->url, 'tagstr' => $content->tagstr, 'timestamp' => time(), 'contentId' => uniqid() ) ); $result = $query->getResultSet(); return self::returnMappedContent($result); }
  • 52. Adding Content public static function add($username, Content $content) { $queryString =<<<CYPHER MATCH (user { username: {u}}) OPTIONAL MATCH (user)-[r:LASTPOST]->(lastpost) DELETE r CREATE (user)-[:LASTPOST]->(p:Content { title:{title}, url:{url}, tagstr:{tagstr}, timestamp:{timestamp}, contentId: {contentId} }) WITH p, collect(lastpost) as lastposts FOREACH (x IN lastposts | CREATE p-[:NEXTPOST]->x) RETURN p, {u} as username, true as owner CYPHER; $query = new Query( Neo4jClient::client(), $queryString, array( 'u' => $username, 'title' => $content->title, 'url' => $content->url, 'tagstr' => $content->tagstr, 'timestamp' => time(), 'contentId' => uniqid() ) ); $result = $query->getResultSet(); return self::returnMappedContent($result); }
  • 53. Adding Content MATCH (user { username: {u}}) OPTIONAL MATCH (user)-[r:LASTPOST]->(lastpost) DELETE r CREATE (user)-[:LASTPOST]->(p:Content { title:{title}, url: {url}, tagstr:{tagstr}, timestamp:{timestamp}, contentId: {contentId} }) WITH p, collect(lastpost) as lastposts FOREACH (x IN lastposts | CREATE p-[:NEXTPOST]->x) RETURN p, {u} as username, true as owner
  • 54. Adding Content MATCH (user { username: {u}}) OPTIONAL MATCH (user)-[r:LASTPOST]->(lastpost) DELETE r CREATE (user)-[:LASTPOST]->(p:Content { title:{title}, url: {url}, tagstr:{tagstr}, timestamp:{timestamp}, contentId: {contentId} }) WITH p, collect(lastpost) as lastposts FOREACH (x IN lastposts | CREATE p-[:NEXTPOST]->x) RETURN p, {u} as username, true as owner
  • 55. Adding Content MATCH (user { username: {u}}) OPTIONAL MATCH (user)-[r:LASTPOST]->(lastpost) DELETE r CREATE (user)-[:LASTPOST]->(p:Content { title:{title}, url: {url}, tagstr:{tagstr}, timestamp:{timestamp}, contentId: {contentId} }) WITH p, collect(lastpost) as lastposts FOREACH (x IN lastposts | CREATE p-[:NEXTPOST]->x) RETURN p, {u} as username, true as owner
  • 56. Adding Content MATCH (user { username: {u}}) OPTIONAL MATCH (user)-[r:LASTPOST]->(lastpost) DELETE r CREATE (user)-[:LASTPOST]->(p:Content { title:{title}, url: {url}, tagstr:{tagstr}, timestamp:{timestamp}, contentId: {contentId} }) WITH p, collect(lastpost) as lastposts FOREACH (x IN lastposts | CREATE p-[:NEXTPOST]->x) RETURN p, {u} as username, true as owner
  • 57. Adding Content MATCH (user { username: {u}}) OPTIONAL MATCH (user)-[r:LASTPOST]->(lastpost) DELETE r CREATE (user)-[:LASTPOST]->(p:Content { title:{title}, url: {url}, tagstr:{tagstr}, timestamp:{timestamp}, contentId: {contentId} }) WITH p, collect(lastpost) as lastposts FOREACH (x IN lastposts | CREATE p-[:NEXTPOST]->x) RETURN p, {u} as username, true as owner
  • 58. Adding Content MATCH (user { username: {u}}) OPTIONAL MATCH (user)-[r:LASTPOST]->(lastpost) DELETE r CREATE (user)-[:LASTPOST]->(p:Content { title:{title}, url: {url}, tagstr:{tagstr}, timestamp:{timestamp}, contentId: {contentId} }) WITH p, collect(lastpost) as lastposts FOREACH (x IN lastposts | CREATE p-[:NEXTPOST]->x) RETURN p, {u} as username, true as owner
  • 59. Adding Content $query = new Query( $neo4jClient, $queryString, array( 'u' => $username, 'title' => $content->title, 'url' => $content->url, 'tagstr' => $content->tagstr, 'timestamp' => time(), 'contentId' => uniqid() ) ); $result = $query->getResultSet();
  • 60. Retrieving Content public static function getContent($username, $skip) { $queryString = <<<CYPHER MATCH (u:User { username: { u }})-[:FOLLOWS*0..1]->f WITH DISTINCT f, u MATCH f-[:LASTPOST]-lp-[:NEXTPOST*0..]-p RETURN p, f.username as username, f = u as owner ORDER BY p.timestamp desc SKIP { skip } LIMIT 4 CYPHER; $query = new Query( Neo4jClient::client(), $queryString, array( 'u' => $username, 'skip' => $skip, ) ); $result = $query->getResultSet(); return self::returnMappedContent($result); }
  • 61. Retrieving Content MATCH (u:User { username: { u }})-[:FOLLOWS*0..1]->f WITH DISTINCT f, u MATCH f-[:LASTPOST]-lp-[:NEXTPOST*0..]-p RETURN p, f.username as username, f = u as owner ORDER BY p.timestamp desc SKIP { skip } LIMIT 4
  • 62. Retrieving Content MATCH (u:User { username: { u }})-[:FOLLOWS*0..1]->f WITH DISTINCT f, u MATCH f-[:LASTPOST]-lp-[:NEXTPOST*0..]-p RETURN p, f.username as username, f = u as owner ORDER BY p.timestamp desc SKIP { skip } LIMIT 4
  • 63. Retrieving Content MATCH (u:User { username: { u }})-[:FOLLOWS*0..1]->f WITH DISTINCT f, u MATCH f-[:LASTPOST]-lp-[:NEXTPOST*0..]-p RETURN p, f.username as username, f = u as owner ORDER BY p.timestamp desc SKIP { skip } LIMIT 4
  • 64. Retrieving Content MATCH (u:User { username: { u }})-[:FOLLOWS*0..1]->f WITH DISTINCT f, u MATCH f-[:LASTPOST]-lp-[:NEXTPOST*0..]-p RETURN p, f.username as username, f = u as owner ORDER BY p.timestamp desc SKIP { skip } LIMIT 4
  • 65. Retrieving Content MATCH (u:User { username: { u }})-[:FOLLOWS*0..1]->f WITH DISTINCT f, u MATCH f-[:LASTPOST]-lp-[:NEXTPOST*0..]-p RETURN p, f.username as username, f = u as owner ORDER BY p.timestamp desc SKIP { skip } LIMIT 4
  • 66. Retrieving Content MATCH (u:User { username: { u }})-[:FOLLOWS*0..1]->f WITH DISTINCT f, u MATCH f-[:LASTPOST]-lp-[:NEXTPOST*0..]-p RETURN p, f.username as username, f = u as owner ORDER BY p.timestamp desc SKIP { skip } LIMIT 4
  • 67. Editing Content public static function edit(Content $content) { $updatedAt = time(); $node = $content->node; $node->setProperty('title', $content->title); $node->setProperty('url', $content->url); $node->setProperty('tagstr', $content->tagstr); $node->setProperty('updated', $updatedAt); $node->save(); $content->updated = $updatedAt; return $content; }
  • 68. Editing Content public static function edit(Content $content) { $updatedAt = time(); $node = $content->node; $node->setProperty('title', $content->title); $node->setProperty('url', $content->url); $node->setProperty('tagstr', $content->tagstr); $node->setProperty('updated', $updatedAt); $node->save(); $content->updated = $updatedAt; return $content; }
  • 69. Editing Content public static function edit(Content $content) { $updatedAt = time(); $node = $content->node; $node->setProperty('title', $content->title); $node->setProperty('url', $content->url); $node->setProperty('tagstr', $content->tagstr); $node->setProperty('updated', $updatedAt); $node->save(); $content->updated = $updatedAt; return $content; }
  • 70. Editing Content public static function edit(Content $content) { $updatedAt = time(); $node = $content->node; $node->setProperty('title', $content->title); $node->setProperty('url', $content->url); $node->setProperty('tagstr', $content->tagstr); $node->setProperty('updated', $updatedAt); $node->save(); $content->updated = $updatedAt; return $content; }
  • 71. Editing Content public static function edit(Content $content) { $updatedAt = time(); $node = $content->node; $node->setProperty('title', $content->title); $node->setProperty('url', $content->url); $node->setProperty('tagstr', $content->tagstr); $node->setProperty('updated', $updatedAt); $node->save(); $content->updated = $updatedAt; return $content; }
  • 72. Deleting Content public static function delete($username, $contentId) { $queryString = self::getDeleteQueryString( $username, $contentId ); $params = array( 'username' => $username, 'contentId' => $contentId, ); $query = new Query( $neo4jClient, $queryString, $params ); $query->getResultSet(); }
  • 73. Deleting Content public static function delete($username, $contentId) { $queryString = self::getDeleteQueryString( $username, $contentId ); $params = array( 'username' => $username, 'contentId' => $contentId, ); $query = new Query( $neo4jClient, $queryString, $params ); $query->getResultSet(); }
  • 74. Deleting Content public static function delete($username, $contentId) { $queryString = self::getDeleteQueryString( $username, $contentId ); $params = array( 'username' => $username, 'contentId' => $contentId, ); $query = new Query( $neo4jClient, $queryString, $params ); $query->getResultSet(); }
  • 75. Deleting Content public static function delete($username, $contentId) { $queryString = self::getDeleteQueryString( $username, $contentId ); $params = array( 'username' => $username, 'contentId' => $contentId, ); $query = new Query( $neo4jClient, $queryString, $params ); $query->getResultSet(); }
  • 76. Deleting Content: Leaf // If leaf MATCH (u:User { username: { username }})-[:LASTPOST| NEXTPOST*0..]->(c:Content { contentId: { contentId }}) WITH c MATCH (c)-[r]-() DELETE c, r
  • 77. Deleting Content: Leaf // If leaf MATCH (u:User { username: { username }})-[:LASTPOST| NEXTPOST*0..]->(c:Content { contentId: { contentId }}) WITH c MATCH (c)-[r]-() DELETE c, r
  • 78. Deleting Content: Leaf // If leaf MATCH (u:User { username: { username }})-[:LASTPOST| NEXTPOST*0..]->(c:Content { contentId: { contentId }}) WITH c MATCH (c)-[r]-() DELETE c, r
  • 79. Deleting Content: Leaf // If leaf MATCH (u:User { username: { username }})-[:LASTPOST| NEXTPOST*0..]->(c:Content { contentId: { contentId }}) WITH c MATCH (c)-[r]-() DELETE c, r
  • 80. Deleting Content: Leaf // If leaf MATCH (u:User { username: { username }})-[:LASTPOST| NEXTPOST*0..]->(c:Content { contentId: { contentId }}) WITH c MATCH (c)-[r]-() DELETE c, r
  • 81. Deleting Content: LASTPOST // If last MATCH (u:User { username: { username }})-[lp:LASTPOST]- >(del:Content { contentId: { contentId }})-[np:NEXTPOST]- >(nextPost) CREATE UNIQUE (u)-[:LASTPOST]->(nextPost) DELETE lp, del, np
  • 82. Deleting Content: LASTPOST // If last MATCH (u:User { username: { username }})-[lp:LASTPOST]- >(del:Content { contentId: { contentId }})-[np:NEXTPOST]- >(nextPost) CREATE UNIQUE (u)-[:LASTPOST]->(nextPost) DELETE lp, del, np
  • 83. Deleting Content: LASTPOST // If last MATCH (u:User { username: { username }})-[lp:LASTPOST]- >(del:Content { contentId: { contentId }})-[np:NEXTPOST]- >(nextPost) CREATE UNIQUE (u)-[:LASTPOST]->(nextPost) DELETE lp, del, np
  • 84. Deleting Content: LASTPOST // If last MATCH (u:User { username: { username }})-[lp:LASTPOST]- >(del:Content { contentId: { contentId }})-[np:NEXTPOST]- >(nextPost) CREATE UNIQUE (u)-[:LASTPOST]->(nextPost) DELETE lp, del, np
  • 85. Deleting Content: Other // All other MATCH (u:User { username: { username }})-[:LASTPOST| NEXTPOST*0..]->(before), (before)-[delBefore]->(del:Content { contentId: { contentId }})-[delAfter]->(after) CREATE UNIQUE (before)-[:NEXTPOST]->(after) DELETE del, delBefore, delAfter
  • 86. Deleting Content: Other // All other MATCH (u:User { username: { username }})-[:LASTPOST| NEXTPOST*0..]->(before), (before)-[delBefore]->(del:Content { contentId: { contentId }})-[delAfter]->(after) CREATE UNIQUE (before)-[:NEXTPOST]->(after) DELETE del, delBefore, delAfter
  • 87. Deleting Content: Other // All other MATCH (u:User { username: { username }})-[:LASTPOST| NEXTPOST*0..]->(before), (before)-[delBefore]->(del:Content { contentId: { contentId }})-[delAfter]->(after) CREATE UNIQUE (before)-[:NEXTPOST]->(after) DELETE del, delBefore, delAfter
  • 88. Deleting Content: Other // All other MATCH (u:User { username: { username }})-[:LASTPOST| NEXTPOST*0..]->(before), (before)-[delBefore]->(del:Content { contentId: { contentId }})-[delAfter]->(after) CREATE UNIQUE (before)-[:NEXTPOST]->(after) DELETE del, delBefore, delAfter
  • 89. Deleting Content: Other // All other MATCH (u:User { username: { username }})-[:LASTPOST| NEXTPOST*0..]->(before), (before)-[delBefore]->(del:Content { contentId: { contentId }})-[delAfter]->(after) CREATE UNIQUE (before)-[:NEXTPOST]->(after) DELETE del, delBefore, delAfter