SlideShare a Scribd company logo
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

More Related Content

What's hot

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
clintongormley
 
(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
 
Fantom and Tales
Fantom and TalesFantom and Tales
Fantom and Tales
kaushik_sathupadi
 
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...
Codemotion
 
Current state-of-php
Current state-of-phpCurrent state-of-php
Current state-of-php
Richard McIntyre
 
How to use MongoDB with CakePHP
How to use MongoDB with CakePHPHow to use MongoDB with CakePHP
How to use MongoDB with CakePHP
ichikaway
 
Fazendo mágica com ElasticSearch
Fazendo mágica com ElasticSearchFazendo mágica com ElasticSearch
Fazendo mágica com ElasticSearch
Pedro Franceschi
 
jQuery%20on%20Rails%20Presentation
jQuery%20on%20Rails%20PresentationjQuery%20on%20Rails%20Presentation
jQuery%20on%20Rails%20Presentationguestcf600a
 
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 ModelingDATAVERSITY
 
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
Daniel Yuschick
 
SPL - The Undiscovered Library - PHPBarcelona 2015
SPL - The Undiscovered Library - PHPBarcelona 2015SPL - The Undiscovered Library - PHPBarcelona 2015
SPL - The Undiscovered Library - PHPBarcelona 2015
Mark Baker
 
My First Ruby
My First RubyMy First Ruby
My First Ruby
Murray Steele
 
анатолий шарифулин Mojolicious
анатолий шарифулин Mojoliciousанатолий шарифулин Mojolicious
анатолий шарифулин Mojoliciousrit2010
 

What's hot (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
 

Viewers also liked

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
jfsheridan
 
A Brief Introduction to Zend_Form
A Brief Introduction to Zend_FormA Brief Introduction to Zend_Form
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 minutesJeremy 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-25Jeremy 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-05Jeremy Kendall
 
Tdd in php a brief example
Tdd in php   a brief exampleTdd in php   a brief example
Tdd in php a brief exampleJeremy Kendall
 
Didactica concepto, objeto y finalidades
Didactica   concepto, objeto y finalidadesDidactica   concepto, objeto y finalidades
Didactica concepto, objeto y finalidades
Alejandro De Greef
 

Viewers also liked (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
 

Similar to 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 PHPJeremy 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
 
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
Serendio Inc.
 
Eve - REST API for Humans™
Eve - REST API for Humans™Eve - REST API for Humans™
Eve - REST API for Humans™
Nicola Iarocci
 
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
Ruby Meditation
 
Structured Apps with Google Dart
Structured Apps with Google DartStructured Apps with Google Dart
Structured Apps with Google Dart
Jermaine Oppong
 
Neo4j Graph Database และการประยุกตร์ใช้
Neo4j Graph Database และการประยุกตร์ใช้Neo4j Graph Database และการประยุกตร์ใช้
Neo4j Graph Database และการประยุกตร์ใช้
Chakrit Phain
 
Application Modeling with Graph Databases
Application Modeling with Graph DatabasesApplication Modeling with Graph Databases
Application Modeling with Graph DatabasesJosh Adell
 
Visualizing Web Data Query Results
Visualizing Web Data Query ResultsVisualizing Web Data Query Results
Visualizing Web Data Query Results
Anja Jentzsch
 
WWW2012 Tutorial Visualizing SPARQL Queries
WWW2012 Tutorial Visualizing SPARQL QueriesWWW2012 Tutorial Visualizing SPARQL Queries
WWW2012 Tutorial Visualizing SPARQL Queries
Pablo Mendes
 
Clojure: Simple By Design
Clojure: Simple By DesignClojure: Simple By Design
Clojure: Simple By Design
All Things Open
 
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
andyseaborne
 
Better APIs with GraphQL
Better APIs with GraphQL Better APIs with GraphQL
Better APIs with GraphQL
Josh Price
 
Graph Analysis over JSON, Larus
Graph Analysis over JSON, LarusGraph Analysis over JSON, Larus
Graph Analysis over JSON, Larus
Neo4j
 
NUS iOS Swift Talk
NUS iOS Swift TalkNUS iOS Swift Talk
NUS iOS Swift Talk
Gabriel Lim
 
Your Database Cannot Do this (well)
Your Database Cannot Do this (well)Your Database Cannot Do this (well)
Your Database Cannot Do this (well)
javier ramirez
 
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...
Paul Leclercq
 
Overview of GraphQL & Clients
Overview of GraphQL & ClientsOverview of GraphQL & Clients
Overview of GraphQL & Clients
Pokai Chang
 
Jsonsaga 100605143125-phpapp02
Jsonsaga 100605143125-phpapp02Jsonsaga 100605143125-phpapp02
Jsonsaga 100605143125-phpapp02Ramamohan Chokkam
 

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

More from Jeremy Kendall

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
Jeremy Kendall
 
Game Changing Dependency Management
Game Changing Dependency ManagementGame Changing Dependency Management
Game Changing Dependency ManagementJeremy Kendall
 
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
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 FrameworkJeremy 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 frameworkJeremy Kendall
 
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
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_FormJeremy Kendall
 
Zero to ZF in 10 Minutes
Zero to ZF in 10 MinutesZero to ZF in 10 Minutes
Zero to ZF in 10 Minutes
Jeremy Kendall
 

More from 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
 

Recently uploaded

Accelerate your Kubernetes clusters with Varnish Caching
Accelerate your Kubernetes clusters with Varnish CachingAccelerate your Kubernetes clusters with Varnish Caching
Accelerate your Kubernetes clusters with Varnish Caching
Thijs Feryn
 
Slack (or Teams) Automation for Bonterra Impact Management (fka Social Soluti...
Slack (or Teams) Automation for Bonterra Impact Management (fka Social Soluti...Slack (or Teams) Automation for Bonterra Impact Management (fka Social Soluti...
Slack (or Teams) Automation for Bonterra Impact Management (fka Social Soluti...
Jeffrey Haguewood
 
Key Trends Shaping the Future of Infrastructure.pdf
Key Trends Shaping the Future of Infrastructure.pdfKey Trends Shaping the Future of Infrastructure.pdf
Key Trends Shaping the Future of Infrastructure.pdf
Cheryl Hung
 
Transcript: Selling digital books in 2024: Insights from industry leaders - T...
Transcript: Selling digital books in 2024: Insights from industry leaders - T...Transcript: Selling digital books in 2024: Insights from industry leaders - T...
Transcript: Selling digital books in 2024: Insights from industry leaders - T...
BookNet Canada
 
FIDO Alliance Osaka Seminar: Overview.pdf
FIDO Alliance Osaka Seminar: Overview.pdfFIDO Alliance Osaka Seminar: Overview.pdf
FIDO Alliance Osaka Seminar: Overview.pdf
FIDO Alliance
 
UiPath Test Automation using UiPath Test Suite series, part 3
UiPath Test Automation using UiPath Test Suite series, part 3UiPath Test Automation using UiPath Test Suite series, part 3
UiPath Test Automation using UiPath Test Suite series, part 3
DianaGray10
 
FIDO Alliance Osaka Seminar: The WebAuthn API and Discoverable Credentials.pdf
FIDO Alliance Osaka Seminar: The WebAuthn API and Discoverable Credentials.pdfFIDO Alliance Osaka Seminar: The WebAuthn API and Discoverable Credentials.pdf
FIDO Alliance Osaka Seminar: The WebAuthn API and Discoverable Credentials.pdf
FIDO Alliance
 
Connector Corner: Automate dynamic content and events by pushing a button
Connector Corner: Automate dynamic content and events by pushing a buttonConnector Corner: Automate dynamic content and events by pushing a button
Connector Corner: Automate dynamic content and events by pushing a button
DianaGray10
 
De-mystifying Zero to One: Design Informed Techniques for Greenfield Innovati...
De-mystifying Zero to One: Design Informed Techniques for Greenfield Innovati...De-mystifying Zero to One: Design Informed Techniques for Greenfield Innovati...
De-mystifying Zero to One: Design Informed Techniques for Greenfield Innovati...
Product School
 
When stars align: studies in data quality, knowledge graphs, and machine lear...
When stars align: studies in data quality, knowledge graphs, and machine lear...When stars align: studies in data quality, knowledge graphs, and machine lear...
When stars align: studies in data quality, knowledge graphs, and machine lear...
Elena Simperl
 
GDG Cloud Southlake #33: Boule & Rebala: Effective AppSec in SDLC using Deplo...
GDG Cloud Southlake #33: Boule & Rebala: Effective AppSec in SDLC using Deplo...GDG Cloud Southlake #33: Boule & Rebala: Effective AppSec in SDLC using Deplo...
GDG Cloud Southlake #33: Boule & Rebala: Effective AppSec in SDLC using Deplo...
James Anderson
 
State of ICS and IoT Cyber Threat Landscape Report 2024 preview
State of ICS and IoT Cyber Threat Landscape Report 2024 previewState of ICS and IoT Cyber Threat Landscape Report 2024 preview
State of ICS and IoT Cyber Threat Landscape Report 2024 preview
Prayukth K V
 
GraphRAG is All You need? LLM & Knowledge Graph
GraphRAG is All You need? LLM & Knowledge GraphGraphRAG is All You need? LLM & Knowledge Graph
GraphRAG is All You need? LLM & Knowledge Graph
Guy Korland
 
DevOps and Testing slides at DASA Connect
DevOps and Testing slides at DASA ConnectDevOps and Testing slides at DASA Connect
DevOps and Testing slides at DASA Connect
Kari Kakkonen
 
Neuro-symbolic is not enough, we need neuro-*semantic*
Neuro-symbolic is not enough, we need neuro-*semantic*Neuro-symbolic is not enough, we need neuro-*semantic*
Neuro-symbolic is not enough, we need neuro-*semantic*
Frank van Harmelen
 
Builder.ai Founder Sachin Dev Duggal's Strategic Approach to Create an Innova...
Builder.ai Founder Sachin Dev Duggal's Strategic Approach to Create an Innova...Builder.ai Founder Sachin Dev Duggal's Strategic Approach to Create an Innova...
Builder.ai Founder Sachin Dev Duggal's Strategic Approach to Create an Innova...
Ramesh Iyer
 
Smart TV Buyer Insights Survey 2024 by 91mobiles.pdf
Smart TV Buyer Insights Survey 2024 by 91mobiles.pdfSmart TV Buyer Insights Survey 2024 by 91mobiles.pdf
Smart TV Buyer Insights Survey 2024 by 91mobiles.pdf
91mobiles
 
Essentials of Automations: Optimizing FME Workflows with Parameters
Essentials of Automations: Optimizing FME Workflows with ParametersEssentials of Automations: Optimizing FME Workflows with Parameters
Essentials of Automations: Optimizing FME Workflows with Parameters
Safe Software
 
LF Energy Webinar: Electrical Grid Modelling and Simulation Through PowSyBl -...
LF Energy Webinar: Electrical Grid Modelling and Simulation Through PowSyBl -...LF Energy Webinar: Electrical Grid Modelling and Simulation Through PowSyBl -...
LF Energy Webinar: Electrical Grid Modelling and Simulation Through PowSyBl -...
DanBrown980551
 
Software Delivery At the Speed of AI: Inflectra Invests In AI-Powered Quality
Software Delivery At the Speed of AI: Inflectra Invests In AI-Powered QualitySoftware Delivery At the Speed of AI: Inflectra Invests In AI-Powered Quality
Software Delivery At the Speed of AI: Inflectra Invests In AI-Powered Quality
Inflectra
 

Recently uploaded (20)

Accelerate your Kubernetes clusters with Varnish Caching
Accelerate your Kubernetes clusters with Varnish CachingAccelerate your Kubernetes clusters with Varnish Caching
Accelerate your Kubernetes clusters with Varnish Caching
 
Slack (or Teams) Automation for Bonterra Impact Management (fka Social Soluti...
Slack (or Teams) Automation for Bonterra Impact Management (fka Social Soluti...Slack (or Teams) Automation for Bonterra Impact Management (fka Social Soluti...
Slack (or Teams) Automation for Bonterra Impact Management (fka Social Soluti...
 
Key Trends Shaping the Future of Infrastructure.pdf
Key Trends Shaping the Future of Infrastructure.pdfKey Trends Shaping the Future of Infrastructure.pdf
Key Trends Shaping the Future of Infrastructure.pdf
 
Transcript: Selling digital books in 2024: Insights from industry leaders - T...
Transcript: Selling digital books in 2024: Insights from industry leaders - T...Transcript: Selling digital books in 2024: Insights from industry leaders - T...
Transcript: Selling digital books in 2024: Insights from industry leaders - T...
 
FIDO Alliance Osaka Seminar: Overview.pdf
FIDO Alliance Osaka Seminar: Overview.pdfFIDO Alliance Osaka Seminar: Overview.pdf
FIDO Alliance Osaka Seminar: Overview.pdf
 
UiPath Test Automation using UiPath Test Suite series, part 3
UiPath Test Automation using UiPath Test Suite series, part 3UiPath Test Automation using UiPath Test Suite series, part 3
UiPath Test Automation using UiPath Test Suite series, part 3
 
FIDO Alliance Osaka Seminar: The WebAuthn API and Discoverable Credentials.pdf
FIDO Alliance Osaka Seminar: The WebAuthn API and Discoverable Credentials.pdfFIDO Alliance Osaka Seminar: The WebAuthn API and Discoverable Credentials.pdf
FIDO Alliance Osaka Seminar: The WebAuthn API and Discoverable Credentials.pdf
 
Connector Corner: Automate dynamic content and events by pushing a button
Connector Corner: Automate dynamic content and events by pushing a buttonConnector Corner: Automate dynamic content and events by pushing a button
Connector Corner: Automate dynamic content and events by pushing a button
 
De-mystifying Zero to One: Design Informed Techniques for Greenfield Innovati...
De-mystifying Zero to One: Design Informed Techniques for Greenfield Innovati...De-mystifying Zero to One: Design Informed Techniques for Greenfield Innovati...
De-mystifying Zero to One: Design Informed Techniques for Greenfield Innovati...
 
When stars align: studies in data quality, knowledge graphs, and machine lear...
When stars align: studies in data quality, knowledge graphs, and machine lear...When stars align: studies in data quality, knowledge graphs, and machine lear...
When stars align: studies in data quality, knowledge graphs, and machine lear...
 
GDG Cloud Southlake #33: Boule & Rebala: Effective AppSec in SDLC using Deplo...
GDG Cloud Southlake #33: Boule & Rebala: Effective AppSec in SDLC using Deplo...GDG Cloud Southlake #33: Boule & Rebala: Effective AppSec in SDLC using Deplo...
GDG Cloud Southlake #33: Boule & Rebala: Effective AppSec in SDLC using Deplo...
 
State of ICS and IoT Cyber Threat Landscape Report 2024 preview
State of ICS and IoT Cyber Threat Landscape Report 2024 previewState of ICS and IoT Cyber Threat Landscape Report 2024 preview
State of ICS and IoT Cyber Threat Landscape Report 2024 preview
 
GraphRAG is All You need? LLM & Knowledge Graph
GraphRAG is All You need? LLM & Knowledge GraphGraphRAG is All You need? LLM & Knowledge Graph
GraphRAG is All You need? LLM & Knowledge Graph
 
DevOps and Testing slides at DASA Connect
DevOps and Testing slides at DASA ConnectDevOps and Testing slides at DASA Connect
DevOps and Testing slides at DASA Connect
 
Neuro-symbolic is not enough, we need neuro-*semantic*
Neuro-symbolic is not enough, we need neuro-*semantic*Neuro-symbolic is not enough, we need neuro-*semantic*
Neuro-symbolic is not enough, we need neuro-*semantic*
 
Builder.ai Founder Sachin Dev Duggal's Strategic Approach to Create an Innova...
Builder.ai Founder Sachin Dev Duggal's Strategic Approach to Create an Innova...Builder.ai Founder Sachin Dev Duggal's Strategic Approach to Create an Innova...
Builder.ai Founder Sachin Dev Duggal's Strategic Approach to Create an Innova...
 
Smart TV Buyer Insights Survey 2024 by 91mobiles.pdf
Smart TV Buyer Insights Survey 2024 by 91mobiles.pdfSmart TV Buyer Insights Survey 2024 by 91mobiles.pdf
Smart TV Buyer Insights Survey 2024 by 91mobiles.pdf
 
Essentials of Automations: Optimizing FME Workflows with Parameters
Essentials of Automations: Optimizing FME Workflows with ParametersEssentials of Automations: Optimizing FME Workflows with Parameters
Essentials of Automations: Optimizing FME Workflows with Parameters
 
LF Energy Webinar: Electrical Grid Modelling and Simulation Through PowSyBl -...
LF Energy Webinar: Electrical Grid Modelling and Simulation Through PowSyBl -...LF Energy Webinar: Electrical Grid Modelling and Simulation Through PowSyBl -...
LF Energy Webinar: Electrical Grid Modelling and Simulation Through PowSyBl -...
 
Software Delivery At the Speed of AI: Inflectra Invests In AI-Powered Quality
Software Delivery At the Speed of AI: Inflectra Invests In AI-Powered QualitySoftware Delivery At the Speed of AI: Inflectra Invests In AI-Powered Quality
Software Delivery At the Speed of AI: Inflectra Invests In AI-Powered Quality
 

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