SlideShare une entreprise Scribd logo
1  sur  97
Télécharger pour lire hors ligne
Leveraging the Power of
Graph Databases
in PHP
Jeremy Kendall
Nashville PHP
November 2014
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
Graph Databases
• Data Model!
• Nodes with properties
• Typed relationships
Graph Databases
• Data Model!
• Nodes with properties
• Typed relationships
• Strengths!
• Highly connected data
• ACID
Graph Databases
• Data Model!
• Nodes with properties
• Typed relationships
• Strengths!
• Highly connected data
• ACID
• Weaknesses!
• Paradigm shift
Graph Databases
• Data Model!
• Nodes with properties
• Typed relationships
• Strengths!
• Highly connected data
• ACID
• Weaknesses!
• Paradigm shift
• Examples!
• Neo4j, Titan, OrientDB
Why Care?
Why Care?
• All the NoSQL Joy
• Schema-less
• Semi-structured data
Why Care?
• All the NoSQL Joy
• Schema-less
• Semi-structured data
• Escape from JOIN Hell
Why Care?
• All the NoSQL Joy
• Schema-less
• Semi-structured data
• Escape from JOIN Hell
• Speed
Why Care?
Why Care?
• Relationships have 1st class status
Why Care?
• Relationships have 1st class status
• Just as important as the objects connecting them
Why Care?
• Relationships have 1st class status
• Just as important as the objects connecting them
• You can have properties & labels
Why Care?
• Relationships have 1st class status
• Just as important as the objects connecting them
• 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
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
• Under review 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
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

Contenu connexe

Tendances

(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
 
анатолий шарифулин Mojolicious
анатолий шарифулин Mojoliciousанатолий шарифулин Mojolicious
анатолий шарифулин Mojolicious
rit2010
 

Tendances (15)

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
 
(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
 
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
 
How to use MongoDB with CakePHP
How to use MongoDB with CakePHPHow to use MongoDB with CakePHP
How to use MongoDB with CakePHP
 
Fazendo mágica com ElasticSearch
Fazendo mágica com ElasticSearchFazendo mágica com ElasticSearch
Fazendo mágica com ElasticSearch
 
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
 
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
 
SPL - The Undiscovered Library - PHPBarcelona 2015
SPL - The Undiscovered Library - PHPBarcelona 2015SPL - The Undiscovered Library - PHPBarcelona 2015
SPL - The Undiscovered Library - PHPBarcelona 2015
 
My First Ruby
My First RubyMy First Ruby
My First Ruby
 
анатолий шарифулин Mojolicious
анатолий шарифулин Mojoliciousанатолий шарифулин Mojolicious
анатолий шарифулин Mojolicious
 

En vedette (7)

Illinois Health Care Spring It Technology Conference
Illinois Health Care Spring It Technology ConferenceIllinois Health Care Spring It Technology Conference
Illinois Health Care Spring It Technology Conference
 
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
 
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
 
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 a brief example
Tdd in php   a brief exampleTdd in php   a brief example
Tdd in php a brief example
 
Didactica concepto, objeto y finalidades
Didactica   concepto, objeto y finalidadesDidactica   concepto, objeto y finalidades
Didactica concepto, objeto y finalidades
 

Similaire à Leveraging the Power of Graph Databases in PHP

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
Jeremy Kendall
 
The openCypher Project - An Open Graph Query Language
The openCypher Project - An Open Graph Query LanguageThe openCypher Project - An Open Graph Query Language
The openCypher Project - An Open Graph Query Language
Neo4j
 
Application Modeling with Graph Databases
Application Modeling with Graph DatabasesApplication Modeling with Graph Databases
Application Modeling with Graph Databases
Josh Adell
 
Jsonsaga 100605143125-phpapp02
Jsonsaga 100605143125-phpapp02Jsonsaga 100605143125-phpapp02
Jsonsaga 100605143125-phpapp02
Ramamohan Chokkam
 

Similaire à 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
 
The openCypher Project - An Open Graph Query Language
The openCypher Project - An Open Graph Query LanguageThe openCypher Project - An Open Graph Query Language
The openCypher Project - An Open Graph Query Language
 
Hands on Training – Graph Database with Neo4j
Hands on Training – Graph Database with Neo4jHands on Training – Graph Database with Neo4j
Hands on Training – Graph Database with Neo4j
 
Eve - REST API for Humans™
Eve - REST API for Humans™Eve - REST API for Humans™
Eve - REST API for Humans™
 
What/How to do with GraphQL? - Valentyn Ostakh (ENG) | Ruby Meditation 27
What/How to do with GraphQL? - Valentyn Ostakh (ENG) | Ruby Meditation 27What/How to do with GraphQL? - Valentyn Ostakh (ENG) | Ruby Meditation 27
What/How to do with GraphQL? - Valentyn Ostakh (ENG) | Ruby Meditation 27
 
Structured Apps with Google Dart
Structured Apps with Google DartStructured Apps with Google Dart
Structured Apps with Google Dart
 
Neo4j Graph Database และการประยุกตร์ใช้
Neo4j Graph Database และการประยุกตร์ใช้Neo4j Graph Database และการประยุกตร์ใช้
Neo4j Graph Database และการประยุกตร์ใช้
 
Application Modeling with Graph Databases
Application Modeling with Graph DatabasesApplication Modeling with Graph Databases
Application Modeling with Graph Databases
 
Neo4j
Neo4jNeo4j
Neo4j
 
Visualizing Web Data Query Results
Visualizing Web Data Query ResultsVisualizing Web Data Query Results
Visualizing Web Data Query Results
 
WWW2012 Tutorial Visualizing SPARQL Queries
WWW2012 Tutorial Visualizing SPARQL QueriesWWW2012 Tutorial Visualizing SPARQL Queries
WWW2012 Tutorial Visualizing SPARQL Queries
 
Clojure: Simple By Design
Clojure: Simple By DesignClojure: Simple By Design
Clojure: Simple By Design
 
Two graph data models : RDF and Property Graphs
Two graph data models : RDF and Property GraphsTwo graph data models : RDF and Property Graphs
Two graph data models : RDF and Property Graphs
 
Better APIs with GraphQL
Better APIs with GraphQL Better APIs with GraphQL
Better APIs with GraphQL
 
Graph Analysis over JSON, Larus
Graph Analysis over JSON, LarusGraph Analysis over JSON, Larus
Graph Analysis over JSON, Larus
 
NUS iOS Swift Talk
NUS iOS Swift TalkNUS iOS Swift Talk
NUS iOS Swift Talk
 
Your Database Cannot Do this (well)
Your Database Cannot Do this (well)Your Database Cannot Do this (well)
Your Database Cannot Do this (well)
 
Analyze one year of radio station songs aired with Spark SQL, Spotify, and Da...
Analyze one year of radio station songs aired with Spark SQL, Spotify, and Da...Analyze one year of radio station songs aired with Spark SQL, Spotify, and Da...
Analyze one year of radio station songs aired with Spark SQL, Spotify, and Da...
 
Overview of GraphQL & Clients
Overview of GraphQL & ClientsOverview of GraphQL & Clients
Overview of GraphQL & Clients
 
Jsonsaga 100605143125-phpapp02
Jsonsaga 100605143125-phpapp02Jsonsaga 100605143125-phpapp02
Jsonsaga 100605143125-phpapp02
 

Plus de 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
 
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
 

Plus de Jeremy Kendall (9)

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
 
Php 101: PDO
Php 101: PDOPhp 101: PDO
Php 101: PDO
 
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
 
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
 

Dernier

Artificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and MythsArtificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and Myths
Joaquim Jorge
 
EIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptx
EIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptxEIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptx
EIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptx
Earley Information Science
 
IAC 2024 - IA Fast Track to Search Focused AI Solutions
IAC 2024 - IA Fast Track to Search Focused AI SolutionsIAC 2024 - IA Fast Track to Search Focused AI Solutions
IAC 2024 - IA Fast Track to Search Focused AI Solutions
Enterprise Knowledge
 

Dernier (20)

Exploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone ProcessorsExploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone Processors
 
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
 
Artificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and MythsArtificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and Myths
 
EIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptx
EIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptxEIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptx
EIS-Webinar-Prompt-Knowledge-Eng-2024-04-08.pptx
 
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
 
The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024
 
Presentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreterPresentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreter
 
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
 
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
 
GenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day PresentationGenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day Presentation
 
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
 
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...
 
Understanding Discord NSFW Servers A Guide for Responsible Users.pdf
Understanding Discord NSFW Servers A Guide for Responsible Users.pdfUnderstanding Discord NSFW Servers A Guide for Responsible Users.pdf
Understanding Discord NSFW Servers A Guide for Responsible Users.pdf
 
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdfThe Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
 
presentation ICT roal in 21st century education
presentation ICT roal in 21st century educationpresentation ICT roal in 21st century education
presentation ICT roal in 21st century education
 
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
 
IAC 2024 - IA Fast Track to Search Focused AI Solutions
IAC 2024 - IA Fast Track to Search Focused AI SolutionsIAC 2024 - IA Fast Track to Search Focused AI Solutions
IAC 2024 - IA Fast Track to Search Focused AI Solutions
 
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
 
Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)
 
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
 

Leveraging the Power of Graph Databases in PHP

  • 1. Leveraging the Power of Graph Databases in PHP Jeremy Kendall Nashville PHP November 2014
  • 3. Also - New Father
  • 8. Graph Databases • Data Model! • Nodes with properties • Typed relationships
  • 9. Graph Databases • Data Model! • Nodes with properties • Typed relationships • Strengths! • Highly connected data • ACID
  • 10. Graph Databases • Data Model! • Nodes with properties • Typed relationships • Strengths! • Highly connected data • ACID • Weaknesses! • Paradigm shift
  • 11. Graph Databases • Data Model! • Nodes with properties • Typed relationships • Strengths! • Highly connected data • ACID • Weaknesses! • Paradigm shift • Examples! • Neo4j, Titan, OrientDB
  • 13. Why Care? • All the NoSQL Joy • Schema-less • Semi-structured data
  • 14. Why Care? • All the NoSQL Joy • Schema-less • Semi-structured data • Escape from JOIN Hell
  • 15. Why Care? • All the NoSQL Joy • Schema-less • Semi-structured data • Escape from JOIN Hell • Speed
  • 17. Why Care? • Relationships have 1st class status
  • 18. Why Care? • Relationships have 1st class status • Just as important as the objects connecting them
  • 19. Why Care? • Relationships have 1st class status • Just as important as the objects connecting them • You can have properties & labels
  • 20. Why Care? • Relationships have 1st class status • Just as important as the objects connecting them • You can have properties & labels • Multiple relationships
  • 22. 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
  • 23. 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
  • 25. Cypher • Neo4j’s declarative query language • Easy to pick up • Some clauses and concepts familiar from SQL
  • 27. Goal
  • 28. 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 . . .!
  • 29. 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 . . .!
  • 30. 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 . . .!
  • 31. 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 . . .!
  • 32. 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 . . .!
  • 33. 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
  • 34. 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
  • 35. 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. 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
  • 37. 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
  • 38. 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
  • 39. 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
  • 42. 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
  • 43. Also see: NeoClient • Written by Neoxygen • Alternative PHP wrapper for the Neo4j REST API • Installable via Composer • Under review for internal use at Graph Story • Well tested • https://packagist.org/packages/neoxygen/neoclient
  • 44. Connecting $neo4jClient = new EverymanNeo4jClient(! ‘yourgraph.example.com’, ! 7473! );! ! $neo4jClient->getTransport()! ->setAuth('username', 'password')! ->getTransport()->useHttps();
  • 45. Creating a Node and Label $node = new Node($neo4jClient);! ! $label = $neo4jClient->makeLabel('Person');! ! $node->setProperty('name', ‘Jeremy Kendall');! ! $node->save()->addLabels(array($label));
  • 46. Searching // Searching for a label by property! $label = $neo4jClient->makeLabel('Person');! $nodes = $label->getNodes('name', $name);
  • 47. Querying (Cypher) $queryString = ! 'MATCH (p:Person { name: { name }}) RETURN p';! ! $query = new EverymanNeo4jCypherQuery(! $neo4jClient,! $queryString,! ['name' => ‘Jeremy Kendall']! );! ! $result = $query->getResultSet();
  • 49. Named Parameters $queryString = ! 'MATCH (p:Person { name: { name }}) RETURN p';! ! $query = new EverymanNeo4jCypherQuery(! $neo4jClient,! $queryString,! ['name' => ‘Jeremy Kendall']! );! ! $result = $query->getResultSet();
  • 50. Named Parameters $queryString = ! 'MATCH (p:Person { name: { name }}) RETURN p';! ! $query = new EverymanNeo4jCypherQuery(! $neo4jClient,! $queryString,! ['name' => ‘Jeremy Kendall']! );! ! $result = $query->getResultSet();
  • 52. News Feed • Modeled as a list of posts • Newest post first • All subsequent posts follow • Relationships: LASTPOST and NEXTPOST
  • 55. The Content Model class Content! {! public $node;! public $nodeId;! public $contentId;! public $title;! public $url;! public $tagstr;! public $timestamp;! public $userNameForPost;! public $owner = false;! }
  • 56. 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);! }
  • 57. 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);! }
  • 58. 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);! }
  • 59. 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
  • 60. 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
  • 61. 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
  • 62. 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
  • 63. 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
  • 64. 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
  • 65. 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();
  • 66. 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);! }
  • 67. 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
  • 68. 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
  • 69. 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
  • 70. 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
  • 71. 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
  • 72. 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
  • 73. 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;! }
  • 74. 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;! }
  • 75. 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;! }
  • 76. 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;! }
  • 77. 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;! }
  • 78. 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();! }
  • 79. 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();! }
  • 80. 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();! }
  • 81. 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();! }
  • 82. 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
  • 83. 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
  • 84. 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
  • 85. 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
  • 86. 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
  • 87. 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
  • 88. 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
  • 89. 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
  • 90. 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
  • 91. 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
  • 92. 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
  • 93. 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
  • 94. 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
  • 95. 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