Slide 1

Slide 1 text

A presentation by @stuherbert
 for @GanbaroDigital DeCYPHERing Graph Databases With PHP And Neo4J

Slide 2

Slide 2 text

Industry veteran: architect, engineer, leader, manager, mentor F/OSS contributor since 1994 Talking and writing about PHP since 2004 Chief Software Archaeologist @GanbaroDigital About Stuart

Slide 3

Slide 3 text

Follow me I do tweet a lot about non-tech stuff though :) @stuherbert

Slide 4

Slide 4 text

@GanbaroDigital Why am I giving this talk?

Slide 5

Slide 5 text

@GanbaroDigital Story time with Stu!

Slide 6

Slide 6 text

@GanbaroDigital https://flic.kr/p/nUiUbU Canary Wharf

Slide 7

Slide 7 text

@GanbaroDigital https://flic.kr/p/Tjs2DM

Slide 8

Slide 8 text

@GanbaroDigital https://flic.kr/p/ciLwbo

Slide 9

Slide 9 text

@GanbaroDigital

Slide 10

Slide 10 text

@GanbaroDigital

Slide 11

Slide 11 text

@GanbaroDigital

Slide 12

Slide 12 text

@GanbaroDigital

Slide 13

Slide 13 text

@GanbaroDigital

Slide 14

Slide 14 text

@GanbaroDigital

Slide 15

Slide 15 text

@GanbaroDigital After describing their business, they said something profound:

Slide 16

Slide 16 text

@GanbaroDigital ... “we don’t know how to model that in a database.”

Slide 17

Slide 17 text

@GanbaroDigital

Slide 18

Slide 18 text

@GanbaroDigital https://flic.kr/p/ee8yvi

Slide 19

Slide 19 text

@GanbaroDigital A crucial part of building back-office systems is turning the business model into a RDBMS model.

Slide 20

Slide 20 text

@GanbaroDigital ?? ?? What if we could use the same model for the business and the database?

Slide 21

Slide 21 text

@GanbaroDigital ?? ?? What if we could use the same model for the business and the database?

Slide 22

Slide 22 text

@GanbaroDigital Same Model • Same entities • ... with the same names • ... and the same terms for connections

Slide 23

Slide 23 text

@GanbaroDigital I don’t mean use an ORM to pretend it’s the same. I mean the database itself uses the same model that the business does.

Slide 24

Slide 24 text

@GanbaroDigital I don’t mean use an ORM to pretend it’s the same. I mean the database itself uses the same model that the business does.

Slide 25

Slide 25 text

@GanbaroDigital https://flic.kr/p/5LsLri

Slide 26

Slide 26 text

@GanbaroDigital We can do that using a Graph database.

Slide 27

Slide 27 text

@GanbaroDigital We can do that using a Graph database.

Slide 28

Slide 28 text

@GanbaroDigital “ Graph DBs are the BDD of databases.

Slide 29

Slide 29 text

@GanbaroDigital This isn’t your typical graph database talk.

Slide 30

Slide 30 text

@GanbaroDigital This isn’t a talk about how to use graph algorithms.

Slide 31

Slide 31 text

@GanbaroDigital Other folks already have that covered.

Slide 32

Slide 32 text

@GanbaroDigital https://neo4j.com/graphgists/

Slide 33

Slide 33 text

@GanbaroDigital https://neo4j.com/graphgist/the-panamapapers-example-dataset-president-of-azerbaijan

Slide 34

Slide 34 text

@GanbaroDigital https://neo4j.com/webinars/

Slide 35

Slide 35 text

@GanbaroDigital https://www.youtube.com/watch?v=cvTFejfE1-k

Slide 36

Slide 36 text

@GanbaroDigital This is a talk about building (part of) a business on a Graph DB.

Slide 37

Slide 37 text

@GanbaroDigital ... and how to do this using PHP.

Slide 38

Slide 38 text

@GanbaroDigital This is my experience. This is how I did it, and why.

Slide 39

Slide 39 text

@GanbaroDigital Other approaches exist. I’m here to learn from you too!

Slide 40

Slide 40 text

@GanbaroDigital Please ask questions as we go.

Slide 41

Slide 41 text

@GanbaroDigital In This Talk 1. 100 Years Of History 2. Querying Graphs With CYPHER 3. BOLTing On PHP

Slide 42

Slide 42 text

@GanbaroDigital 100 Years Of History

Slide 43

Slide 43 text

@GanbaroDigital Let’s look at a real example. (Sorry if you don’t like football!)

Slide 44

Slide 44 text

@GanbaroDigital https://chelseafan12.com

Slide 45

Slide 45 text

@GanbaroDigital https://chelseafan12.com/fanzone/historical-fixtures/

Slide 46

Slide 46 text

@GanbaroDigital https://chelseafan12.com/fanzone/historical-fixtures/

Slide 47

Slide 47 text

@GanbaroDigital https://chelseafan12.com/fanzone/player-library/

Slide 48

Slide 48 text

@GanbaroDigital https://chelseafan12.com/fanzone/player-library/kerry-dixon/

Slide 49

Slide 49 text

@GanbaroDigital ChelseaFan12 Fanzone • Fixture list back to 1905 • Key events from each match • Squad data to match • Rich data on each squad member • + data on Chelsea opponents

Slide 50

Slide 50 text

@GanbaroDigital Underlying Dataset • 420,000+ records • 3.2 million+ foreign keys • average of nearly 8 foreign keys per record

Slide 51

Slide 51 text

@GanbaroDigital Person Spell Team Match Season Goal Media Biography Competition

Slide 52

Slide 52 text

@GanbaroDigital We’ll use this real dataset to learn how to write queries.

Slide 53

Slide 53 text

@GanbaroDigital Querying Graphs With CYPHER

Slide 54

Slide 54 text

@GanbaroDigital CYPHER is Neo4J’s query language

Slide 55

Slide 55 text

@GanbaroDigital CYPHER is awesome*

Slide 56

Slide 56 text

@GanbaroDigital What does CYPHER look like?

Slide 57

Slide 57 text

@GanbaroDigital With CYPHER your queries look like a graph diagram.

Slide 58

Slide 58 text

@GanbaroDigital MATCH (a)-[r]->(b)

Slide 59

Slide 59 text

@GanbaroDigital How To Read CYPHER • Anything in brackets is a record
 (a node in graph terms) • Anything in square brackets is a foreign key
 (a relationship in graph terms) • Relationships can have direction

Slide 60

Slide 60 text

@GanbaroDigital MATCH (a)-[r]->(b)

Slide 61

Slide 61 text

@GanbaroDigital How To Read CYPHER • Anything in brackets is a record
 (a node in graph terms) • Anything in square brackets is a foreign key
 (a relationship in graph terms) • Relationships can have direction

Slide 62

Slide 62 text

@GanbaroDigital MATCH (a)-[r]->(b)

Slide 63

Slide 63 text

@GanbaroDigital How To Read CYPHER • Anything in brackets is a record
 (a node in graph terms) • Anything in square brackets is a foreign key
 (a relationship in graph terms) • Relationships can have direction

Slide 64

Slide 64 text

@GanbaroDigital MATCH (a)-[r]->(b)

Slide 65

Slide 65 text

@GanbaroDigital How do we find records using CYPHER?

Slide 66

Slide 66 text

@GanbaroDigital MATCH (a:label)-[r:label]->(b:label)

Slide 67

Slide 67 text

@GanbaroDigital MATCH (a:label)-[r:label]->(b:label)

Slide 68

Slide 68 text

@GanbaroDigital MATCH (a:label)-[r:label]->(b:label) { from

Slide 69

Slide 69 text

@GanbaroDigital MATCH (a:label)-[r:label]->(b:label) { to

Slide 70

Slide 70 text

@GanbaroDigital MATCH (a:label)-[r:label]->(b:label) { connected by

Slide 71

Slide 71 text

@GanbaroDigital MATCH (a:label)-[r:label]->(b:label) { to { from { connected by

Slide 72

Slide 72 text

@GanbaroDigital MATCH (a:label)-[r:label]->(b:label)

Slide 73

Slide 73 text

@GanbaroDigital MATCH (a:label)-[r:label]->(b:label)

Slide 74

Slide 74 text

@GanbaroDigital MATCH (a:label)-[r:label]->(b:label)

Slide 75

Slide 75 text

@GanbaroDigital MATCH (a:label)-[r:label]->(b:label)

Slide 76

Slide 76 text

@GanbaroDigital CYPHER MATCH • ‘a’ and ‘b’ are named results • if you want to use it later in your query,
 give it a name • think of labels as record types / table names

Slide 77

Slide 77 text

@GanbaroDigital “ With CYPHER, you find records by relationships and filter them by content

Slide 78

Slide 78 text

@GanbaroDigital MATCH (a:label)-[r:label]->(b:label)
 WHERE a.field = value

Slide 79

Slide 79 text

@GanbaroDigital MATCH (a:label)-[r:label]->(b:label)
 WHERE a.field = value

Slide 80

Slide 80 text

@GanbaroDigital Tell CYPHER what data to return from the query.

Slide 81

Slide 81 text

@GanbaroDigital MATCH (a:label)-[r:label]->(b:label)
 WHERE a.field = value RETURN b

Slide 82

Slide 82 text

@GanbaroDigital MATCH (a:label)-[r:label]->(b:label)
 WHERE a.field = value RETURN b

Slide 83

Slide 83 text

@GanbaroDigital MATCH can take multiple
 relationships.

Slide 84

Slide 84 text

@GanbaroDigital MATCH (a:label)-[r:label]->(b:label)<-[r2:label]-[c:label]
 WHERE a.field = value AND c.field = value RETURN b

Slide 85

Slide 85 text

@GanbaroDigital MATCH (a:label)-[r:label]->(b:label)<-[r2:label]-[c:label]
 WHERE a.field = value AND c.field = value RETURN b

Slide 86

Slide 86 text

@GanbaroDigital CYPHER supports aggregate queries :)

Slide 87

Slide 87 text

@GanbaroDigital MATCH (a:label)-[r:label]->(b:label)<-[r2:label]-(c:label)
 WHERE a.field = value AND c.field = value WITH b, c MATCH (c)-[r3:label]->(d:label)<-[r4:label]-(b) RETURN c, d

Slide 88

Slide 88 text

@GanbaroDigital MATCH (a:label)-[r:label]->(b:label)<-[r2:label]-(c:label)
 WHERE a.field = value AND c.field = value WITH b, c MATCH (c)-[r3:label]->(d:label)<-[r4:label]-(b) RETURN c, d

Slide 89

Slide 89 text

@GanbaroDigital MATCH (a:label)-[r:label]->(b:label)<-[r2:label]-(c:label)
 WHERE a.field = value AND c.field = value WITH b, c MATCH (c)-[r3:label]->(d:label)<-[r4:label]-(b) RETURN c, d

Slide 90

Slide 90 text

@GanbaroDigital MATCH (a:label)-[r:label]->(b:label)<-[r2:label]-(c:label)
 WHERE a.field = value AND c.field = value WITH b, c MATCH (c)-[r3:label]->(d:label)<-[r4:label]-(b) RETURN c, d

Slide 91

Slide 91 text

@GanbaroDigital https://neo4j.com/docs/developer-manual/current/cypher/

Slide 92

Slide 92 text

@GanbaroDigital http://markhneedham.com/blog/tag/cypher/

Slide 93

Slide 93 text

@GanbaroDigital Querying The Dataset

Slide 94

Slide 94 text

@GanbaroDigital 3 Actual Queries • A-Z list • Player profile cards • Matches played in a season

Slide 95

Slide 95 text

@GanbaroDigital Query #1 The A-Z List

Slide 96

Slide 96 text

@GanbaroDigital https://chelseafan12.com/fanzone/player-library/

Slide 97

Slide 97 text

@GanbaroDigital Which players do we show on the A-Z page?

Slide 98

Slide 98 text

@GanbaroDigital “ To design a basic CYPHER query, simply say what you’re trying to find.

Slide 99

Slide 99 text

@GanbaroDigital Person Spell Team Match Season Goal Media Biography Competition

Slide 100

Slide 100 text

@GanbaroDigital Person Spell Team Match Season Goal Media Biography Competition

Slide 101

Slide 101 text

@GanbaroDigital “Find players who had a spell at ‘Chelsea’”

Slide 102

Slide 102 text

@GanbaroDigital MATCH (p:person)

Slide 103

Slide 103 text

@GanbaroDigital MATCH (p:person)-[:had_spell]->(:spell)

Slide 104

Slide 104 text

@GanbaroDigital MATCH (p:person)-[:had_spell]->(:spell)-[:at]->(t:team)
 WHERE t.name =~ “(?i)chelsea”

Slide 105

Slide 105 text

@GanbaroDigital MATCH (p:person)-[:had_spell]->(:spell)-[:at]->(t:team)
 WHERE t.name =~ “(?i)chelsea”
 RETURN DISTINCT(p)
 ORDER BY p.sortname ASC

Slide 106

Slide 106 text

@GanbaroDigital 3 Things About This Query • Text searching • Duplicate rows • Pre-calculated sort order

Slide 107

Slide 107 text

@GanbaroDigital MATCH (p:person)-[:had_spell]->(:spell)-[:at]->(t:team)
 WHERE t.name =~ “(?i)chelsea”
 RETURN DISTINCT(p)
 ORDER BY p.sortname ASC

Slide 108

Slide 108 text

@GanbaroDigital String-matching is case-sensitive in Neo4J. I end up using regexes for all my string matching.

Slide 109

Slide 109 text

@GanbaroDigital 3 Things About This Query • Text searching • Duplicate rows • Pre-calculated sort order

Slide 110

Slide 110 text

@GanbaroDigital MATCH (p:person)-[:had_spell]->(:spell)-[:at]->(t:team)
 WHERE t.name =~ “(?i)chelsea”
 RETURN DISTINCT(p)
 ORDER BY p.sortname ASC

Slide 111

Slide 111 text

@GanbaroDigital Players may have played for the club multiple times. Use DISTINCT() to avoid returning duplicate rows.

Slide 112

Slide 112 text

@GanbaroDigital 3 Things About This Query • Text searching • Duplicate rows • Pre-calculated sort order

Slide 113

Slide 113 text

@GanbaroDigital MATCH (p:person)-[:had_spell]->(:spell)-[:at]->(t:team)
 WHERE t.name =~ “(?i)chelsea”
 RETURN DISTINCT(p)
 ORDER BY p.sortname ASC

Slide 114

Slide 114 text

@GanbaroDigital Our pre-calculated ‘sortname’ saves any duplication in different client apps.

Slide 115

Slide 115 text

@GanbaroDigital “ CYPHER queries look like how we’d describe what we’re trying to find.

Slide 116

Slide 116 text

@GanbaroDigital The query that’s live today is a little different.

Slide 117

Slide 117 text

@GanbaroDigital Direct relationships often describe things clearer. Especially when it’s complicated.

Slide 118

Slide 118 text

@GanbaroDigital Person Spell Team Match Season Goal Media Biography Competition

Slide 119

Slide 119 text

@GanbaroDigital Person Spell Match Season Goal Media Biography Competition Team

Slide 120

Slide 120 text

@GanbaroDigital MATCH (p:person)-[:played_for]->(t:team)
 WHERE t.name =~ “(?i)chelsea”
 RETURN p
 ORDER BY p.sortname ASC

Slide 121

Slide 121 text

@GanbaroDigital MATCH (p:person)-[:played_for]->(t:team)
 WHERE t.name =~ “(?i)chelsea”
 RETURN p
 ORDER BY p.sortname ASC

Slide 122

Slide 122 text

@GanbaroDigital Just because you’re using a Graph DB, you don’t have to traverse longer paths.

Slide 123

Slide 123 text

@GanbaroDigital Person Spell Team Match Season Goal Media Biography Competition

Slide 124

Slide 124 text

@GanbaroDigital Person Spell Match Season Goal Media Biography Competition Team

Slide 125

Slide 125 text

@GanbaroDigital “ Create new, shorter paths for anything you use regularly.

Slide 126

Slide 126 text

@GanbaroDigital We can easily add different relationships between the same two records.

Slide 127

Slide 127 text

@GanbaroDigital This is very handy when new requirements arrive!

Slide 128

Slide 128 text

@GanbaroDigital https://chelseafan12.com/fanzone/player-library/

Slide 129

Slide 129 text

@GanbaroDigital https://chelseafan12.com/fanzone/player-library/

Slide 130

Slide 130 text

@GanbaroDigital MATCH (t:team)-[:current_squad]->(p:person)
 WHERE t.name =~ “(?i)chelsea”
 RETURN p
 ORDER BY p.sortname ASC

Slide 131

Slide 131 text

@GanbaroDigital MATCH (t:team)-[:current_squad]->(p:person)
 WHERE t.name =~ “(?i)chelsea”
 RETURN p
 ORDER BY p.sortname ASC

Slide 132

Slide 132 text

@GanbaroDigital We can do all this in an RDBMS. It’s just so much easier to do it in a Graph database.

Slide 133

Slide 133 text

@GanbaroDigital Graph Relationships • No schema changes / migrations • No extra columns for the foreign keys • No NULLs for empty foreign keys • Just get on and do it :)

Slide 134

Slide 134 text

@GanbaroDigital Shipped and deployed an average of 3 updated schemas* per month for 12 months. * equivalent to RDBMS

Slide 135

Slide 135 text

@GanbaroDigital Zero schema migrations required :-)

Slide 136

Slide 136 text

@GanbaroDigital Query #2 Profile Cards

Slide 137

Slide 137 text

@GanbaroDigital How do we build each player’s profile card for the A-Z page?

Slide 138

Slide 138 text

@GanbaroDigital

Slide 139

Slide 139 text

@GanbaroDigital Person Spell Team Match Season Goal Media Biography Competition

Slide 140

Slide 140 text

@GanbaroDigital Person Spell Match Season Goal Media Biography Competition Team

Slide 141

Slide 141 text

@GanbaroDigital MATCH (t:team)-[:played_for]->(p:person)
 WHERE t.name =~ “(?i)chelsea”
 WITH DISTINCT(p), t OPTIONAL MATCH (p)-[:profile_pic]->(m:media)-[:team]->(t)
 WHERE m.category = “thumbnail” RETURN p, COLLECT([m]) as profile_pics
 ORDER BY p.sortname ASC

Slide 142

Slide 142 text

@GanbaroDigital MATCH (t:team)-[:played_for]->(p:person)
 WHERE t.name =~ “(?i)chelsea”
 WITH DISTINCT(p), t OPTIONAL MATCH (p)-[:profile_pic]->(m:media)-[:team]->(t)
 WHERE m.category = “thumbnail” RETURN p, COLLECT([m]) as profile_pics
 ORDER BY p.sortname ASC

Slide 143

Slide 143 text

@GanbaroDigital Use WITH to feed the result rows into a second query.

Slide 144

Slide 144 text

@GanbaroDigital The second query can reduce the size of the final result set. It can also add new records to the final result set.

Slide 145

Slide 145 text

@GanbaroDigital Person Spell Team Match Season Goal Media Biography Competition

Slide 146

Slide 146 text

@GanbaroDigital MATCH (t:team)-[:played_for]->(p:person)
 WHERE t.name =~ “(?i)chelsea”
 WITH DISTINCT(p), t OPTIONAL MATCH (p)-[:profile_pic]->(m:media)-[:team]->(t)
 WHERE m.category = “thumbnail” RETURN p, COLLECT([m]) as profile_pics
 ORDER BY p.sortname ASC

Slide 147

Slide 147 text

@GanbaroDigital MATCH (t:team)-[:played_for]->(p:person)
 WHERE t.name =~ “(?i)chelsea”
 WITH DISTINCT(p), t OPTIONAL MATCH (p)-[:profile_pic]->(m:media)-[:team]->(t)
 WHERE m.category = “thumbnail” RETURN p, COLLECT([m]) as profile_pics
 ORDER BY p.sortname ASC

Slide 148

Slide 148 text

@GanbaroDigital Use OPTIONAL MATCH when querying incomplete datasets.

Slide 149

Slide 149 text

@GanbaroDigital In this case, my client doesn’t have a profile picture for every player who has played for a given team.

Slide 150

Slide 150 text

@GanbaroDigital MATCH (t:team)-[:played_for]->(p:person)
 WHERE t.name =~ “(?i)chelsea”
 WITH DISTINCT(p), t OPTIONAL MATCH (p)-[:profile_pic]->(m:media)-[:team]->(t)
 WHERE m.category = “thumbnail” RETURN p, COLLECT([m]) as profile_pics
 ORDER BY p.sortname ASC

Slide 151

Slide 151 text

@GanbaroDigital MATCH (t:team)-[:played_for]->(p:person)
 WHERE t.name =~ “(?i)chelsea”
 WITH DISTINCT(p), t OPTIONAL MATCH (p)-[:profile_pic]->(m:media)-[:team]->(t)
 WHERE m.category = “thumbnail” RETURN p, COLLECT([m]) as profile_pics
 ORDER BY p.sortname ASC

Slide 152

Slide 152 text

@GanbaroDigital Note how the name of the relationship tells us what kind of image the ‘media’ record is.

Slide 153

Slide 153 text

@GanbaroDigital “In real life, identity often depends on context. Graphs make it easy for us to model that.

Slide 154

Slide 154 text

@GanbaroDigital For example, UK addresses can be many different things all at once.

Slide 155

Slide 155 text

@GanbaroDigital The list of possible addresses would be the records. The type of address - the context - would be the relationship name.

Slide 156

Slide 156 text

@GanbaroDigital CYPHER returns result rows. Use COLLECT to put multiple records into a single result row.

Slide 157

Slide 157 text

@GanbaroDigital MATCH (t:team)-[:played_for]->(p:person)
 WHERE t.name =~ “(?i)chelsea”
 WITH DISTINCT(p), t OPTIONAL MATCH (p)-[:profile_pic]->(m:media)-[:team]->(t)
 WHERE m.category = “thumbnail” RETURN p, COLLECT([m]) as profile_pics
 ORDER BY p.sortname ASC

Slide 158

Slide 158 text

@GanbaroDigital Query #3: Matches In A Season

Slide 159

Slide 159 text

@GanbaroDigital Which matches were played in a season?

Slide 160

Slide 160 text

@GanbaroDigital Person Spell Team Match Season Goal Media Biography Competition

Slide 161

Slide 161 text

@GanbaroDigital Person Spell Team Match Season Goal Media Biography Competition

Slide 162

Slide 162 text

@GanbaroDigital “Find every league match played by Chelsea in the 2016/2017 season”

Slide 163

Slide 163 text

@GanbaroDigital “Find every league match played by Chelsea in the 2016/2017 season”

Slide 164

Slide 164 text

@GanbaroDigital MATCH (m:match)-[:competition]->(c:competition)
 WHERE c.name = “Premier League”

Slide 165

Slide 165 text

@GanbaroDigital “Find every league match played by Chelsea in the 2016/2017 season”

Slide 166

Slide 166 text

@GanbaroDigital MATCH (t:team)<-[:home_team|away_team]-(m:match) -[:competition]->(c:competition)
 WHERE c.name =~ “(?i)Premier League” AND t.name =~ “(?i)Chelsea”

Slide 167

Slide 167 text

@GanbaroDigital MATCH (t:team)<-[:home_team|away_team]-(m:match) -[:competition]->(c:competition)
 WHERE c.name =~ “(?i)Premier League” AND t.name =~ “(?i)Chelsea”

Slide 168

Slide 168 text

@GanbaroDigital “Find every league match played by Chelsea in the 2016/2017 season”

Slide 169

Slide 169 text

@GanbaroDigital ?? ?? How do we find the season?

Slide 170

Slide 170 text

@GanbaroDigital Football seasons start in one year and finish in the next.

Slide 171

Slide 171 text

@GanbaroDigital “ Design data sets to make records discoverable.

Slide 172

Slide 172 text

@GanbaroDigital Data dimensions are a classic solution to making records discoverable. And they are perfectly suited to graph databases :)

Slide 173

Slide 173 text

@GanbaroDigital https://www.kimballgroup.com

Slide 174

Slide 174 text

@GanbaroDigital Data dimensions are standardised data sets that you can connect different record types to.

Slide 175

Slide 175 text

@GanbaroDigital For example, the list of UK addresses could be a data dimension.

Slide 176

Slide 176 text

@GanbaroDigital Use relationships into the data dimension (e.g. “delivery address”, “billing address”) to provide identity.

Slide 177

Slide 177 text

@GanbaroDigital You can use whatever datasets you want as data dimensions. The point is to standardise them, and preload them in so that you can link to them.

Slide 178

Slide 178 text

@GanbaroDigital Different aspects of time are a classic set of data dimensions.

Slide 179

Slide 179 text

@GanbaroDigital We link each ‘season’ to a ‘start year’ and an ‘end year’ to make them easy to find.

Slide 180

Slide 180 text

@GanbaroDigital Person Spell Team Match Season Goal Media Year Competition

Slide 181

Slide 181 text

@GanbaroDigital MATCH (s:season)-[:started_on_year]->(y:year)
 WHERE y.year = 2016

Slide 182

Slide 182 text

@GanbaroDigital “Find every league match played by Chelsea in the 2016/2017 season”

Slide 183

Slide 183 text

@GanbaroDigital The query could search for things in the same order that we’d say it out loud.

Slide 184

Slide 184 text

@GanbaroDigital Sometimes, it’s better to change the query order to find the smaller result sets first.

Slide 185

Slide 185 text

@GanbaroDigital MATCH (s:season)-[:started_on_year]->(y:year)
 WHERE y.year = 2016
 WITH s
 MATCH (t:team)<-[:home_team|away_team]-(m:match) -[:competition]->(c:competition)
 WHERE c.name =~ “(?i)Premier League” AND t.name =~ “(?i)Chelsea”
 AND (m)-[:season]->(s)

Slide 186

Slide 186 text

@GanbaroDigital MATCH (s:season)-[:started_on_year]->(y:year)
 WHERE y.year = 2016
 WITH s
 MATCH (t:team)<-[:home_team|away_team]-(m:match) -[:competition]->(c:competition)
 WHERE c.name =~ “(?i)Premier League” AND t.name =~ “(?i)Chelsea”
 AND (m)-[:season]->(s)

Slide 187

Slide 187 text

@GanbaroDigital MATCH (s:season)-[:started_on_year]->(y:year)
 WHERE y.year = 2016
 WITH s
 MATCH (t:team)<-[:home_team|away_team]-(m:match) -[:competition]->(c:competition)
 WHERE c.name =~ “(?i)Premier League” AND t.name =~ “(?i)Chelsea”
 AND (m)-[:season]->(s)

Slide 188

Slide 188 text

@GanbaroDigital MATCH (s:season)-[:started_on_year]->(y:year)
 WHERE y.year = 2016
 WITH s
 MATCH (t:team)<-[:home_team|away_team]-(m:match) -[:competition]->(c:competition)
 WHERE c.name =~ “(?i)Premier League” AND t.name =~ “(?i)Chelsea”
 AND (m)-[:season]->(s)

Slide 189

Slide 189 text

@GanbaroDigital In CYPHER, we can use relationships between records in WHERE clauses too.

Slide 190

Slide 190 text

@GanbaroDigital We’ve Covered ... • Finding by relationships • Filtering by content & relationships • Aggregate queries • Identity by context • Discoverability by data dimensions

Slide 191

Slide 191 text

@GanbaroDigital BOLTing On PHP

Slide 192

Slide 192 text

@GanbaroDigital How do we get at all this graph goodness from the world’s best back-office programming language?

Slide 193

Slide 193 text

@GanbaroDigital composer require graphaware/neo4j-php-client

Slide 194

Slide 194 text

@GanbaroDigital // connect to Neo4J using the BOLT protocol // it’s a little faster than the HTTP API use GraphAware\Neo4j\Client\Client; use GraphAware\Neo4j\Client\ClientBuilder; $client = ClientBuilder::create() ->addConnection(‘default’, ‘bolt://neo4j:7687') ->build();

Slide 195

Slide 195 text

@GanbaroDigital // build the query using a HEREDOC // use { } as placeholders for query parameters
 $query = <<(p:person)
 WHERE t.name =~ {teamname}
 RETURN p
 ORDER BY p.sortname ASC EOS;

Slide 196

Slide 196 text

@GanbaroDigital // build the query parameters list $queryParams = [ ‘teamname’ => ‘(?i)chelsea’, ];

Slide 197

Slide 197 text

@GanbaroDigital // run the query $result = $client->run($query, $queryParams); // the records don’t come back as an assoc array :( $records = $result->getRecords();

Slide 198

Slide 198 text

@GanbaroDigital https://github.com/graphaware/neo4j-php-client

Slide 199

Slide 199 text

@GanbaroDigital Summing Up

Slide 200

Slide 200 text

@GanbaroDigital “ Graph DBs are the BDD of databases.

Slide 201

Slide 201 text

@GanbaroDigital “ CYPHER is easier to learn than SQL.

Slide 202

Slide 202 text

@GanbaroDigital “ The majority of queries are much easier to write in CYPHER than in SQL.

Slide 203

Slide 203 text

@GanbaroDigital “ To design a basic CYPHER query, simply say what you’re trying to find.

Slide 204

Slide 204 text

@GanbaroDigital “ Create new, shorter paths for anything you use regularly.

Slide 205

Slide 205 text

@GanbaroDigital “Graph DBs can take advantage of techniques developed for RDBMS like data dimensions!

Slide 206

Slide 206 text

Thank You Any Questions? A presentation by @stuherbert
 for @GanbaroDigital