SlideShare a Scribd company logo
Twi$er:	
  @ianSrobinson	
  #neo4j	
  

Designing	
  and	
  Building	
  a	
  Graph	
  
Database	
  Applica5on	
  
Roadmap	
  
•  Complex	
  data	
  
•  Designing	
  a	
  graph	
  data	
  model	
  and	
  queries	
  
•  Tes@ng	
  
Data	
  Complexity	
  

complexity = f(size

, semi-structure, connectedness)
Social	
  Network	
  
Network	
  Impact	
  Analysis	
  
Route	
  Finding	
  
Recommenda@ons	
  
Logis@cs	
  
Access	
  Control	
  
Fraud	
  Analysis	
  
Securi@es	
  and	
  Debt	
  

Image:	
  orgnet.com	
  
Graphs	
  Are	
  Everywhere	
  
Graph	
  Databases	
  
•  Store	
  
•  Manage	
  
•  Query	
  

data	
  
Neo4j	
  is	
  a	
  Graph	
  Database	
  
Labeled	
  Property	
  Graph	
  
Designing	
  a	
  Graph	
  Model	
  
Models	
  
Purposeful	
  abstrac@on	
  of	
  a	
  domain	
  designed	
  to	
  
sa@sfy	
  par@cular	
  applica@on/end-­‐user	
  goals	
  

Images:	
  en.wikipedia.org	
  
Applica@on/End-­‐User	
  Goals	
  
As	
  an	
  emp

	
  

loyee	
  

I	
  want	
  to	
  k
now	
  who	
  i
n	
  the	
  comp
has	
  similar
any	
  
	
  skills	
  to	
  m
	
  
e	
  
So	
  that	
  we
	
  can	
  excha
nge	
  knowl
edge	
  
Ques@ons	
  To	
  Ask	
  of	
  the	
  Domain	
  
As	
  an	
  emp
loyee	
  
	
  
I	
  want	
  to	
  k
now	
  who	
  i
n	
  the	
  co
has	
  similar	
  
skills	
  to	
  me mpany	
  
	
  
	
  
So	
  that	
  we
	
  can	
  excha
nge	
  knowle
dge	
  

Which	
  people,	
  who	
  work	
  for	
  the	
  same	
  company	
  
as	
  me,	
  have	
  similar	
  skills	
  to	
  me?	
  
Iden@fy	
  En@@es	
  
Which	
  people,	
  who	
  work	
  for	
  the	
  same	
  company	
  
as	
  me,	
  have	
  similar	
  skills	
  to	
  me?	
  
	
  
Person	
  
Company	
  
Skill	
  
Iden@fy	
  Rela@onships	
  Between	
  En@@es	
  
Which	
  people,	
  who	
  work	
  for	
  the	
  same	
  company	
  
as	
  me,	
  have	
  similar	
  skills	
  to	
  me?	
  
	
  
Person	
  WORKS_FOR	
  Company	
  
Person	
  HAS_SKILL	
  Skill	
  
Convert	
  to	
  Cypher	
  Paths	
  
Rela@onship	
  

Person	
  WORKS_FOR	
  Company	
  
Person	
  HAS_SKILL	
  Skill	
  
Label	
  

(:Person)-[:WORKS_FOR]->(:Company),	
(:Person)-[:HAS_SKILL]->(:Skill)
Consolidate	
  Paths	
  
(:Person)-[:WORKS_FOR]->(:Company),	
(:Person)-[:HAS_SKILL]->(:Skill)	

(:Company)<-[:WORKS_FOR]-(:Person)-[:HAS_SKILL]->(:Skill)
Candidate	
  Data	
  Model	
  
(:Company)<-[:WORKS_FOR]-(:Person)-[:HAS_SKILL]->(:Skill)
Express	
  Ques@on	
  as	
  Graph	
  Pa$ern	
  
Which	
  people,	
  who	
  work	
  for	
  the	
  same	
  company	
  
as	
  me,	
  have	
  similar	
  skills	
  to	
  me?	
  
Cypher	
  Query	
  
Which	
  people,	
  who	
  work	
  for	
  the	
  same	
  company	
  
as	
  me,	
  have	
  similar	
  skills	
  to	
  me?	
  
	
MATCH (company)<-[:WORKS_FOR]-(me:Person)-[:HAS_SKILL]->(skill),	
(company)<-[:WORKS_FOR]-(colleague)-[:HAS_SKILL]->(skill)	
WHERE me.name = {name}	
RETURN colleague.name AS name,	
count(skill) AS score,	
collect(skill.name) AS skills	
ORDER BY score DESC
Graph	
  Pa$ern	
  
Which	
  people,	
  who	
  work	
  for	
  the	
  same	
  company	
  
as	
  me,	
  have	
  similar	
  skills	
  to	
  me?	
  
	
MATCH (company)<-[:WORKS_FOR]-(me:Person)-[:HAS_SKILL]->(skill),	
(company)<-[:WORKS_FOR]-(colleague)-[:HAS_SKILL]->(skill)	
WHERE me.name = {name}	
RETURN colleague.name AS name,	
count(skill) AS score,	
collect(skill.name) AS skills	
ORDER BY score DESC
Anchor	
  Pa$ern	
  in	
  Graph	
  
Which	
  people,	
  who	
  work	
  for	
  the	
  same	
  company	
  
as	
  me,	
  have	
  similar	
  skills	
  to	
  me?	
  
	
MATCH (company)<-[:WORKS_FOR]-(me:Person)-[:HAS_SKILL]->(skill),	
(company)<-[:WORKS_FOR]-(colleague)-[:HAS_SKILL]->(skill)	
WHERE me.name = {name}	
RETURN colleague.name AS name,	
count(skill) AS score,	
collect(skill.name) AS skills	
ORDER BY score DESC	

Search	
  nodes	
  labeled	
  
‘Person’,	
  matching	
  on	
  
‘name’	
  property	
  
Create	
  Projec@on	
  of	
  Results	
  
Which	
  people,	
  who	
  work	
  for	
  the	
  same	
  company	
  
as	
  me,	
  have	
  similar	
  skills	
  to	
  me?	
  
	
MATCH (company)<-[:WORKS_FOR]-(me:Person)-[:HAS_SKILL]->(skill),	
(company)<-[:WORKS_FOR]-(colleague)-[:HAS_SKILL]->(skill)	
WHERE me.name = {name}	
RETURN colleague.name AS name,	
count(skill) AS score,	
collect(skill.name) AS skills	
ORDER BY score DESC
First	
  Match	
  
Second	
  Match	
  
Third	
  Match	
  
Running	
  the	
  Query	
  
+-----------------------------------+	
| name
| score | skills
|	
+-----------------------------------+	
| "Lucy" | 2
| ["Java","Neo4j"] |	
| "Bill" | 1
| ["Neo4j"]
|	
+-----------------------------------+	
2 rows
From	
  User	
  Story	
  to	
  Model	
  and	
  Query	
  
As	
  an	
  emp
loyee	
  
	
  
I	
  want	
  to	
  k
now	
  who	
  i
n	
  the	
  co
has	
  similar	
  
skills	
  to	
  me mpany	
  
	
  
	
  
So	
  that	
  we
	
  can	
  excha
nge	
  knowle
dge	
  

MATCH (company)<-[:WORKS_FOR]-(me:Person)-[:HAS_SKILL]->(skill),	
(company)<-[:WORKS_FOR]-(colleague)-[:HAS_SKILL]->(skill)	
WHERE me.name = {name}	
RETURN colleague.name AS name,	
count(skill) AS score,	
collect(skill.name) AS skills	
ORDER BY score DESC	

?

Which	
  people,	
  who	
  work	
  for	
  the	
  same	
  
company	
  as	
  me,	
  have	
  similar	
  skills	
  to	
  me?	

Person	
  WORKS_FOR	
  Company	
  
Person	
  HAS_SKILL	
  Skill	

(:Company)<-[:WORKS_FOR]-(:Person)-[:HAS_SKILL]->(:Skill)
Tes@ng	
  
Why	
  Test?	
  
•  Ensure	
  model	
  is	
  fit	
  for	
  queries	
  
–  Rapid	
  feedback	
  

•  Ensure	
  correctness	
  of	
  queries	
  
•  Document	
  your	
  understanding	
  of	
  your	
  domain	
  
–  Including	
  corner	
  cases	
  and	
  excep@ons	
  

•  Provide	
  a	
  regression	
  test	
  suite	
  
–  Allows	
  you	
  to	
  change	
  and	
  evolve	
  model	
  and	
  
queries	
  
Method	
  
•  Develop	
  queries,	
  or	
  classes	
  that	
  encapsulate	
  
queries,	
  using	
  unit	
  tests	
  
•  Use	
  small,	
  well-­‐understood	
  datasets	
  in	
  each	
  test	
  
–  Create	
  data	
  in	
  test	
  setup	
  
–  Test	
  dataset	
  expresses	
  your	
  understanding	
  of	
  (part	
  of)	
  
the	
  domain	
  

•  Inject	
  in-­‐memory	
  graph	
  database	
  (or	
  Cypher	
  
engine)	
  into	
  object	
  under	
  test	
  
•  The	
  exact	
  strategy	
  you	
  use	
  depends	
  on	
  your	
  
applica@on	
  architecture…	
  
Applica@on	
  Architectures	
  
•  Embedded	
  
•  Server	
  
•  Server	
  with	
  Extensions	
  
Applica@on	
  Architectures	
  
•  Embedded	
  
–  Host	
  in	
  Java	
  process	
  
–  Access	
  to	
  Java	
  APIs	
  

•  Server	
  
•  Server	
  with	
  Extensions	
  

Applica@on	
  
Java	
  APIs	
  
Applica@on	
  Architectures	
  
•  Embedded	
  
•  Server	
  
–  HTTP/JSON	
  interface	
  
–  Server	
  wraps	
  embedded	
  
instance	
  

•  Server	
  with	
  Extensions	
  

Applica@on	
  
REST	
  Client	
  

Write	
  LB	
  
REST	
  API	
  

REST	
  API	
  

Read	
  LB	
  
REST	
  API	
  
Applica@on	
  Architectures	
  
•  Embedded	
  
•  Server	
  
•  Server	
  with	
  Extensions	
  

REST	
  API	
  

–  Execute	
  complex	
  logic	
  on	
  server	
  
–  Control	
  HTTP	
  request/response	
  format	
  

Extensions	
  
Embedded	
  Example	
  
•  Company	
  social	
  network	
  
•  Find	
  colleagues	
  with	
  similar	
  skills	
  
•  Encapsulate	
  query	
  in	
  a	
  ColleagueFinder
Unit	
  Test	
  Fixture	
  
public class ColleagueFinderTest {	
	
private GraphDatabaseService db;	
private ColleagueFinder finder;	
	
@Before	
public void init() {	
db = new TestGraphDatabaseFactory().newImpermanentDatabase();	
ExampleGraph.populate( db );	
finder = new ColleagueFinder( new ExecutionEngine( db ) );	
}	
	
@After	
public void shutdown() {	
db.shutdown();	
}	
}
Create	
  Database	
  
public class ColleagueFinderTest {	
	
private GraphDatabaseService db;	
private ColleagueFinder finder;	
	
@Before	
public void init() {	
db = new TestGraphDatabaseFactory().newImpermanentDatabase();	
ExampleGraph.populate( db );	
finder = new ColleagueFinder( new ExecutionEngine( db ) );	
}	
	
@After	
public void shutdown() {	
db.shutdown();	
}	
}
Populate	
  Graph	
  
public class ColleagueFinderTest {	
	
private GraphDatabaseService db;	
private ColleagueFinder finder;	
	
@Before	
public void init() {	
db = new TestGraphDatabaseFactory().newImpermanentDatabase();	
ExampleGraph.populate( db );	
finder = new ColleagueFinder( new ExecutionEngine( db ) );	
}	
	
@After	
public void shutdown() {	
db.shutdown();	
}	
}
Create	
  Object	
  Under	
  Test	
  
public class ColleagueFinderTest {	
	
private GraphDatabaseService db;	
private ColleagueFinder finder;	
	
@Before	
public void init() {	
db = new TestGraphDatabaseFactory().newImpermanentDatabase();	
ExampleGraph.populate( db );	
finder = new ColleagueFinder( new ExecutionEngine( db ) );	
}	
	
@After	
public void shutdown() {	
db.shutdown();	
}	
}	

Inject	
  	
  
Execu@onEngine	
  
ImpermanentGraphDatabase	
  
•  In-­‐memory	
  
•  For	
  tes@ng	
  only,	
  not	
  produc@on!	
  
	
	
<dependency>	
<groupId>org.neo4j</groupId>	
<artifactId>neo4j-kernel</artifactId>	
<version>${project.version}</version>	
<type>test-jar</type>	
<scope>test</scope>	
</dependency>
Create	
  Sample	
  Data	
  
public static void populate( GraphDatabaseService db ) {	
	
ExecutionEngine engine = new ExecutionEngine( db );	
	
String cypher = 	
"CREATE ian:Person VALUES {name:'Ian'},n" +	
"
bill:Person VALUES {name:'Bill'},n" +	
"
lucy:Person VALUES {name:'Lucy'},n" +	
"
acme:Company VALUES {name:'Acme'},n" +	
	
// Cypher continues...	
	
"
"
"
"

(bill)-[:HAS_SKILL]->(neo4j),n" +	
(bill)-[:HAS_SKILL]->(ruby),n" +	
(lucy)-[:HAS_SKILL]->(java),n" +	
(lucy)-[:HAS_SKILL]->(neo4j)";	

	
engine.execute( cypher );	
}
Unit	
  Test	
  
@Test	
public void shouldFindColleaguesWithSimilarSkills() throws Exception {	
	
// when	
Iterator<Map<String, Object>> results = finder.findColleaguesFor( "Ian" );	
	
// then	
assertEquals( "Lucy", results.next().get( "name" ) );	
assertEquals( "Bill", results.next().get( "name" ) );	
	
assertFalse( results.hasNext() );	
}
Execute	
  Object	
  Under	
  Test	
  
@Test	
public void shouldFindColleaguesWithSimilarSkills() throws Exception {	
	
// when	
Iterator<Map<String, Object>> results = finder.findColleaguesFor( "Ian" );	
	
// then	
assertEquals( "Lucy", results.next().get( "name" ) );	
assertEquals( "Bill", results.next().get( "name" ) );	
	
assertFalse( results.hasNext() );	
}
Assert	
  Results	
  
@Test	
public void shouldFindColleaguesWithSimilarSkills() throws Exception {	
	
// when	
Iterator<Map<String, Object>> results = finder.findColleaguesFor( "Ian" );	
	
// then	
assertEquals( "Lucy", results.next().get( "name" ) );	
assertEquals( "Bill", results.next().get( "name" ) );	
	
assertFalse( results.hasNext() );	
}
Ensure	
  No	
  More	
  Results	
  
@Test	
public void shouldFindColleaguesWithSimilarSkills() throws Exception {	
	
// when	
Iterator<Map<String, Object>> results = finder.findColleaguesFor( "Ian" );	
	
// then	
assertEquals( "Lucy", results.next().get( "name" ) );	
assertEquals( "Bill", results.next().get( "name" ) );	
	
assertFalse( results.hasNext() );	
}
ColleagueFinder	
  
public class ColleagueFinder {	
	
private final ExecutionEngine executionEngine;	
	
public ColleagueFinder( ExecutionEngine executionEngine ) {	
this.executionEngine = executionEngine;	
}	
	
public Iterator<Map<String, Object>> findColleaguesFor( String name ) {	
...	
}	
}
Inject	
  Execu@onEngine	
  
public class ColleagueFinder {	
	
private final ExecutionEngine executionEngine;	
	
public ColleagueFinder( ExecutionEngine executionEngine ) {	
this.executionEngine = executionEngine;	
}	
	
public Iterator<Map<String, Object>> findColleaguesFor( String name ) {	
...	
}	
}
findColleaguesFor()	
  Method	
  
public Iterator<Map<String, Object>> findColleaguesFor( String name ) {	
	
String cypher =	
"MATCH (me:Person)-[:WORKS_FOR]->(company),n" +	
"
(me)-[:HAS_SKILL]->(skill),n" +	
"
(colleague)-[:WORKS_FOR]->(company),n" +	
"
(colleague)-[:HAS_SKILL]->(skill)n" +	
"WHERE me.name = {name}n" +	
"RETURN colleague.name AS name,n" +	
"
count(skill) AS score,n" +	
"
collect(skill.name) AS skillsn" +	
"ORDER BY score DESC";	
	
Map<String, Object> params = new HashMap<String, Object>();	
params.put( "name", name );	
	
return executionEngine.execute( cypher, params ).iterator();	
}
Cypher	
  Query	
  
public Iterator<Map<String, Object>> findColleaguesFor( String name ) {	
	
String cypher =	
"MATCH (me:Person)-[:WORKS_FOR]->(company),n" +	
"
(me)-[:HAS_SKILL]->(skill),n" +	
"
(colleague)-[:WORKS_FOR]->(company),n" +	
"
(colleague)-[:HAS_SKILL]->(skill)n" +	
"WHERE me.name = {name}n" +	
"RETURN colleague.name AS name,n" +	
"
count(skill) AS score,n" +	
"
collect(skill.name) AS skillsn" +	
"ORDER BY score DESC";	
	
Map<String, Object> params = new HashMap<String, Object>();	
params.put( "name", name );	
	
return executionEngine.execute( cypher, params ).iterator();	
}
Parameterized	
  Query	
  
public Iterator<Map<String, Object>> findColleaguesFor( String name ) {	
	
String cypher =	
"MATCH (me:Person)-[:WORKS_FOR]->(company),n" +	
"
(me)-[:HAS_SKILL]->(skill),n" +	
"
(colleague)-[:WORKS_FOR]->(company),n" +	
"
(colleague)-[:HAS_SKILL]->(skill)n" +	
"WHERE me.name = {name}n" +	
"RETURN colleague.name AS name,n" +	
"
count(skill) AS score,n" +	
"
collect(skill.name) AS skillsn" +	
"ORDER BY score DESC";	
	
Map<String, Object> params = new HashMap<String, Object>();	
params.put( "name", name );	
	
return executionEngine.execute( cypher, params ).iterator();	
}
Execute	
  Query	
  
public Iterator<Map<String, Object>> findColleaguesFor( String name ) {	
	
String cypher =	
"MATCH (me:Person)-[:WORKS_FOR]->(company),n" +	
"
(me)-[:HAS_SKILL]->(skill),n" +	
"
(colleague)-[:WORKS_FOR]->(company),n" +	
"
(colleague)-[:HAS_SKILL]->(skill)n" +	
"WHERE me.name = {name}n" +	
"RETURN colleague.name AS name,n" +	
"
count(skill) AS score,n" +	
"
collect(skill.name) AS skillsn" +	
"ORDER BY score DESC";	
	
Map<String, Object> params = new HashMap<String, Object>();	
params.put( "name", name );	
	
return executionEngine.execute( cypher, params ).iterator();	
}
Server	
  Extension	
  Example	
  
•  Same	
  data	
  mode	
  and	
  query	
  as	
  before	
  
•  This	
  @me,	
  we’ll	
  host	
  ColleagueFinder	
  in	
  a	
  
server	
  extension	
  
Server	
  Extension	
  
@Path("/similar-skills")	
public class ColleagueFinderExtension {	
private static final ObjectMapper MAPPER = new ObjectMapper();	
private final ColleagueFinder colleagueFinder;	
	
public ColleagueFinderExtension( @Context CypherExecutor cypherExecutor ) {	
this.colleagueFinder = new ColleagueFinder( cypherExecutor.getExecutionEngine() );	
}	
	
@GET	
@Produces(MediaType.APPLICATION_JSON)	
@Path("/{name}")	
public Response getColleagues( @PathParam("name") String name ) 	
throws IOException {	
	
String json = MAPPER	
.writeValueAsString( colleagueFinder.findColleaguesFor( name ) );	
return Response.ok().entity( json ).build();	
}	
}
JAX-­‐RS	
  Annota@ons	
  
@Path("/similar-skills")	
public class ColleagueFinderExtension {	
private static final ObjectMapper MAPPER = new ObjectMapper();	
private final ColleagueFinder colleagueFinder;	
	
public ColleagueFinderExtension( @Context CypherExecutor cypherExecutor ) {	
this.colleagueFinder = new ColleagueFinder( cypherExecutor.getExecutionEngine() );	
}	
	
@GET	
@Produces(MediaType.APPLICATION_JSON)	
@Path("/{name}")	
public Response getColleagues( @PathParam("name") String name ) 	
throws IOException {	
	
String json = MAPPER	
.writeValueAsString( colleagueFinder.findColleaguesFor( name ) );	
return Response.ok().entity( json ).build();	
}	
}
Map	
  HTTP	
  Request	
  to	
  Object	
  +	
  Method	
  
@Path("/similar-skills")	
public class ColleagueFinderExtension {	
private static final ObjectMapper MAPPER = new ObjectMapper();	
private final ColleagueFinder colleagueFinder;	
	
public ColleagueFinderExtension( @Context CypherExecutor cypherExecutor ) {	
this.colleagueFinder = new ColleagueFinder( cypherExecutor.getExecutionEngine() );	
}	
	
@GET	
@Produces(MediaType.APPLICATION_JSON)	
@Path("/{name}")	
public Response getColleagues( @PathParam("name") String name ) 	
throws IOException {	
	
String json = MAPPER	
.writeValueAsString( colleagueFinder.findColleaguesFor( name ) );	
return Response.ok().entity( json ).build();	
}	
}	

GET	
  

/similar-­‐skills	
   /Ian	
  
CypherExecutor	
  Injected	
  by	
  Server	
  
Ensures	
  
Execu@onEngine	
  
reused	
  across	
  
resource	
  instances	
  

@Path("/similar-skills")	
public class ColleagueFinderExtension {	
private static final ObjectMapper MAPPER = new ObjectMapper();	
private final ColleagueFinder colleagueFinder;	
	
public ColleagueFinderExtension( @Context CypherExecutor cypherExecutor ) {	
this.colleagueFinder = new ColleagueFinder( cypherExecutor.getExecutionEngine() );	
}	
	
@GET	
@Produces(MediaType.APPLICATION_JSON)	
@Path("/{name}")	
public Response getColleagues( @PathParam("name") String name ) 	
throws IOException {	
	
String json = MAPPER	
.writeValueAsString( colleagueFinder.findColleaguesFor( name ) );	
return Response.ok().entity( json ).build();	
}	
}
Generate	
  and	
  Format	
  Response	
  
@Path("/similar-skills")	
public class ColleagueFinderExtension {	
private static final ObjectMapper MAPPER = new ObjectMapper();	
private final ColleagueFinder colleagueFinder;	
	
public ColleagueFinderExtension( @Context CypherExecutor cypherExecutor ) {	
this.colleagueFinder = new ColleagueFinder( cypherExecutor.getExecutionEngine() );	
}	
	
@GET	
@Produces(MediaType.APPLICATION_JSON)	
@Path("/{name}")	
public Response getColleagues( @PathParam("name") String name ) 	
throws IOException {	
	
String json = MAPPER	
.writeValueAsString( colleagueFinder.findColleaguesFor( name ) );	
return Response.ok().entity( json ).build();	
}	
}
Extension	
  Test	
  Fixture	
  
public class ColleagueFinderExtensionTest {	
private CommunityNeoServer server;	
	
@Before	
public void startServer() throws IOException	
{	
server = CommunityServerBuilder.server()	
.withThirdPartyJaxRsPackage(	
"org.neo4j.good_practices", "/colleagues" )	
.build();	
server.start();	
	
ExampleGraph.populate( server.getDatabase().getGraph() );	
}	
	
@After	
public void stopServer() {	
server.stop();	
}	
}
Build	
  and	
  Configure	
  Server	
  
public class ColleagueFinderExtensionTest {	
private CommunityNeoServer server;	
	
@Before	
public void startServer() throws IOException	
{	
server = CommunityServerBuilder.server()	
.withThirdPartyJaxRsPackage(	
"org.neo4j.good_practices", "/colleagues" )	
.build();	
server.start();	
	
ExampleGraph.populate( server.getDatabase().getGraph() );	
}	
	
@After	
public void stopServer() {	
server.stop();	
}	
}
Start	
  Server	
  
public class ColleagueFinderExtensionTest {	
private CommunityNeoServer server;	
	
@Before	
public void startServer() throws IOException	
{	
server = CommunityServerBuilder.server()	
.withThirdPartyJaxRsPackage(	
"org.neo4j.good_practices", "/colleagues" )	
.build();	
server.start();	
	
ExampleGraph.populate( server.getDatabase().getGraph() );	
}	
	
@After	
public void stopServer() {	
server.stop();	
}	
}
Populate	
  Database	
  
public class ColleagueFinderExtensionTest {	
private CommunityNeoServer server;	
	
@Before	
public void startServer() throws IOException	
{	
server = CommunityServerBuilder.server()	
.withThirdPartyJaxRsPackage(	
"org.neo4j.good_practices", "/colleagues" )	
.build();	
server.start();	
	
ExampleGraph.populate( server.getDatabase().getGraph() );	
}	
	
@After	
public void stopServer() {	
server.stop();	
}	
}
CommunityServerBuilder	
  
•  Programma@c	
  configura@on	
  
	
	
<dependency>	
<groupId>org.neo4j.app</groupId>	
<artifactId>neo4j-server</artifactId>	
<version>${project.version}</version>	
<type>test-jar</type>	
</dependency>
Tes@ng	
  Extension	
  Using	
  HTTP	
  Client	
  
@Test	
public void shouldReturnColleaguesWithSimilarSkills() throws Exception {	
	
Client client = Client.create( new DefaultClientConfig() );	
	
WebResource resource = client	
.resource( "http://localhost:7474/colleagues/similar-skills/Ian" );	
	
ClientResponse response = resource	
.accept( MediaType.APPLICATION_JSON )	
.get( ClientResponse.class );	
	
List<Map<String, Object>> results = new ObjectMapper()	
.readValue(response.getEntity( String.class ), List.class );	
	
// Assertions	
	
...
Create	
  HTTP	
  Client	
  
@Test	
public void shouldReturnColleaguesWithSimilarSkills() throws Exception {	
	
Client client = Client.create( new DefaultClientConfig() );	
	
WebResource resource = client	
.resource( "http://localhost:7474/colleagues/similar-skills/Ian" );	
	
ClientResponse response = resource	
.accept( MediaType.APPLICATION_JSON )	
.get( ClientResponse.class );	
	
List<Map<String, Object>> results = new ObjectMapper()	
.readValue(response.getEntity( String.class ), List.class );	
	
// Assertions	
	
...
Issue	
  Request	
  
@Test	
public void shouldReturnColleaguesWithSimilarSkills() throws Exception {	
	
Client client = Client.create( new DefaultClientConfig() );	
	
WebResource resource = client	
.resource( "http://localhost:7474/colleagues/similar-skills/Ian" );	
	
ClientResponse response = resource	
.accept( MediaType.APPLICATION_JSON )	
.get( ClientResponse.class );	
	
List<Map<String, Object>> results = new ObjectMapper()	
.readValue(response.getEntity( String.class ), List.class );	
	
// Assertions	
	
...
Parse	
  Response	
  
@Test	
public void shouldReturnColleaguesWithSimilarSkills() throws Exception {	
	
Client client = Client.create( new DefaultClientConfig() );	
	
WebResource resource = client	
.resource( "http://localhost:7474/colleagues/similar-skills/Ian" );	
	
ClientResponse response = resource	
.accept( MediaType.APPLICATION_JSON )	
.get( ClientResponse.class );	
	
List<Map<String, Object>> results = new ObjectMapper()	
.readValue(response.getEntity( String.class ), List.class );	
	
// Assertions	
	
...
Assert	
  Results	
  
	
...	
	
assertEquals( 200, response.getStatus() );	
assertEquals( MediaType.APPLICATION_JSON, 	
response.getHeaders().get( "Content-Type" ).get( 0 ) );	
	
assertEquals( "Lucy", results.get( 0 ).get( "name" ) );	
assertThat( (Iterable<String>) results.get( 0 ).get( "skills" ), 	
hasItems( "Java", "Neo4j" ) );	
}
ts gy
en lo
i m no
pl h
m ec
Co eo T
N
of

Graph
h
Databases

Thank	
  you	
  
Twi$er:	
  @ianSrobinson	
  
#neo4j	
  
	
  
Neo4j	
  User	
  Group	
  
30th	
  Oct	
  
Skillsma$er	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  
	
  

Ian Robinson,
Jim Webber & Emil Eifrem

github.com/iansrobinson/neo4j-­‐good-­‐prac@ces	
  
	
  

More Related Content

What's hot (20)

PDF
Large-Scale Text Processing Pipeline with Spark ML and GraphFrames: Spark Sum...
Spark Summit
 
PPTX
Spark & Cassandra at DataStax Meetup on Jan 29, 2015
Sameer Farooqui
 
PDF
Optimizing Delta/Parquet Data Lakes for Apache Spark
Databricks
 
PDF
Lessons from Running Large Scale Spark Workloads
Databricks
 
PDF
Fast Data Analytics with Spark and Python
Benjamin Bengfort
 
PDF
Adding Complex Data to Spark Stack by Tug Grall
Spark Summit
 
PDF
Spark what's new what's coming
Databricks
 
PDF
Spark streaming State of the Union - Strata San Jose 2015
Databricks
 
PDF
Spark Application Carousel: Highlights of Several Applications Built with Spark
Databricks
 
PDF
Practical Large Scale Experiences with Spark 2.0 Machine Learning: Spark Summ...
Spark Summit
 
PDF
New Developments in Spark
Databricks
 
PDF
Unified Big Data Processing with Apache Spark (QCON 2014)
Databricks
 
PDF
Performance Optimization Case Study: Shattering Hadoop's Sort Record with Spa...
Databricks
 
PDF
Spark Meetup at Uber
Databricks
 
PDF
A look ahead at spark 2.0
Databricks
 
PPTX
Spark - Philly JUG
Brian O'Neill
 
PDF
Building a Dataset Search Engine with Spark and Elasticsearch: Spark Summit E...
Spark Summit
 
PDF
Spark Summit EU 2015: Spark DataFrames: Simple and Fast Analysis of Structure...
Databricks
 
PDF
ETL to ML: Use Apache Spark as an end to end tool for Advanced Analytics
Miklos Christine
 
PDF
Spark Under the Hood - Meetup @ Data Science London
Databricks
 
Large-Scale Text Processing Pipeline with Spark ML and GraphFrames: Spark Sum...
Spark Summit
 
Spark & Cassandra at DataStax Meetup on Jan 29, 2015
Sameer Farooqui
 
Optimizing Delta/Parquet Data Lakes for Apache Spark
Databricks
 
Lessons from Running Large Scale Spark Workloads
Databricks
 
Fast Data Analytics with Spark and Python
Benjamin Bengfort
 
Adding Complex Data to Spark Stack by Tug Grall
Spark Summit
 
Spark what's new what's coming
Databricks
 
Spark streaming State of the Union - Strata San Jose 2015
Databricks
 
Spark Application Carousel: Highlights of Several Applications Built with Spark
Databricks
 
Practical Large Scale Experiences with Spark 2.0 Machine Learning: Spark Summ...
Spark Summit
 
New Developments in Spark
Databricks
 
Unified Big Data Processing with Apache Spark (QCON 2014)
Databricks
 
Performance Optimization Case Study: Shattering Hadoop's Sort Record with Spa...
Databricks
 
Spark Meetup at Uber
Databricks
 
A look ahead at spark 2.0
Databricks
 
Spark - Philly JUG
Brian O'Neill
 
Building a Dataset Search Engine with Spark and Elasticsearch: Spark Summit E...
Spark Summit
 
Spark Summit EU 2015: Spark DataFrames: Simple and Fast Analysis of Structure...
Databricks
 
ETL to ML: Use Apache Spark as an end to end tool for Advanced Analytics
Miklos Christine
 
Spark Under the Hood - Meetup @ Data Science London
Databricks
 

Viewers also liked (20)

PPTX
Bringing your app to the web with Dart - Chris Buckett (Entity Group)
jaxLondonConference
 
PDF
What You Need to Know About Lambdas - Jamie Allen (Typesafe)
jaxLondonConference
 
PDF
Are Hypermedia APIs Just Hype? - Aaron Phethean (Temenos) & Daniel Feist (Mul...
jaxLondonConference
 
PDF
Real-world polyglot programming on the JVM - Ben Summers (ONEIS)
jaxLondonConference
 
PDF
Lambda Expressions: Myths and Mistakes - Richard Warburton (jClarity)
jaxLondonConference
 
PDF
Big data from the LHC commissioning: practical lessons from big science - Sim...
jaxLondonConference
 
PPTX
Practical Performance: Understand the Performance of Your Application - Chris...
jaxLondonConference
 
PDF
What makes Groovy Groovy - Guillaume Laforge (Pivotal)
jaxLondonConference
 
PPTX
Interactive media applications
Nicole174
 
PDF
Scaling Scala to the database - Stefan Zeiger (Typesafe)
jaxLondonConference
 
PDF
How Hailo fuels its growth using NoSQL storage and analytics - Dave Gardner (...
jaxLondonConference
 
PDF
Are you better than a coin toss? - Richard Warbuton & John Oliver (jClarity)
jaxLondonConference
 
PDF
Little words of wisdom for the developer - Guillaume Laforge (Pivotal)
jaxLondonConference
 
PDF
Introducing Vert.x 2.0 - Taking polyglot application development to the next ...
jaxLondonConference
 
PDF
Packed Objects: Fast Talking Java Meets Native Code - Steve Poole (IBM)
jaxLondonConference
 
PDF
Databases and agile development - Dwight Merriman (MongoDB)
jaxLondonConference
 
PDF
Streams and Things - Darach Ennis (Ubiquiti Networks)
jaxLondonConference
 
PPT
How Java got its Mojo Back - James Governor (Redmonk)
jaxLondonConference
 
PPTX
45 second video proposal
Nicole174
 
PDF
The state of the art biorepository at ILRI
Absolomon Kihara
 
Bringing your app to the web with Dart - Chris Buckett (Entity Group)
jaxLondonConference
 
What You Need to Know About Lambdas - Jamie Allen (Typesafe)
jaxLondonConference
 
Are Hypermedia APIs Just Hype? - Aaron Phethean (Temenos) & Daniel Feist (Mul...
jaxLondonConference
 
Real-world polyglot programming on the JVM - Ben Summers (ONEIS)
jaxLondonConference
 
Lambda Expressions: Myths and Mistakes - Richard Warburton (jClarity)
jaxLondonConference
 
Big data from the LHC commissioning: practical lessons from big science - Sim...
jaxLondonConference
 
Practical Performance: Understand the Performance of Your Application - Chris...
jaxLondonConference
 
What makes Groovy Groovy - Guillaume Laforge (Pivotal)
jaxLondonConference
 
Interactive media applications
Nicole174
 
Scaling Scala to the database - Stefan Zeiger (Typesafe)
jaxLondonConference
 
How Hailo fuels its growth using NoSQL storage and analytics - Dave Gardner (...
jaxLondonConference
 
Are you better than a coin toss? - Richard Warbuton & John Oliver (jClarity)
jaxLondonConference
 
Little words of wisdom for the developer - Guillaume Laforge (Pivotal)
jaxLondonConference
 
Introducing Vert.x 2.0 - Taking polyglot application development to the next ...
jaxLondonConference
 
Packed Objects: Fast Talking Java Meets Native Code - Steve Poole (IBM)
jaxLondonConference
 
Databases and agile development - Dwight Merriman (MongoDB)
jaxLondonConference
 
Streams and Things - Darach Ennis (Ubiquiti Networks)
jaxLondonConference
 
How Java got its Mojo Back - James Governor (Redmonk)
jaxLondonConference
 
45 second video proposal
Nicole174
 
The state of the art biorepository at ILRI
Absolomon Kihara
 
Ad

Similar to Designing and Building a Graph Database Application - Ian Robinson (Neo Technology) (20)

PDF
Designing and Building a Graph Database Application – Architectural Choices, ...
Neo4j
 
PDF
Data modeling with neo4j tutorial
Max De Marzi
 
PDF
20141216 graph database prototyping ams meetup
Rik Van Bruggen
 
PDF
Building Applications with a Graph Database
Tobias Lindaaker
 
PDF
3rd Athens Big Data Meetup - 2nd Talk - Neo4j: The World's Leading Graph DB
Athens Big Data
 
PPTX
Graph Database workshop
Jeremy Deane
 
PDF
Graph Search: The Power of Connected Data
Codemotion
 
PPT
Processing Large Graphs
Nishant Gandhi
 
PDF
Neo4j Data Science Presentation
Max De Marzi
 
PDF
Data Modeling with Neo4j
Neo4j
 
PPTX
Graph Database Query Languages
Jay Coskey
 
PPTX
Neo4j 20 minutes introduction
András Fehér
 
PDF
New opportunities for connected data
Neo4j
 
PPT
Hands on Training – Graph Database with Neo4j
Serendio Inc.
 
PPTX
Introduction to Neo4j and .Net
Neo4j
 
PDF
Hr analytics and graphs : job recommendations
Linkurious
 
PDF
Neo4j Introduction (Basics, Cypher, RDBMS to GRAPH)
David Fombella Pombal
 
PDF
managing big data
Suveeksha
 
PDF
Open data with Neo4j and Kotlin
Neo4j
 
Designing and Building a Graph Database Application – Architectural Choices, ...
Neo4j
 
Data modeling with neo4j tutorial
Max De Marzi
 
20141216 graph database prototyping ams meetup
Rik Van Bruggen
 
Building Applications with a Graph Database
Tobias Lindaaker
 
3rd Athens Big Data Meetup - 2nd Talk - Neo4j: The World's Leading Graph DB
Athens Big Data
 
Graph Database workshop
Jeremy Deane
 
Graph Search: The Power of Connected Data
Codemotion
 
Processing Large Graphs
Nishant Gandhi
 
Neo4j Data Science Presentation
Max De Marzi
 
Data Modeling with Neo4j
Neo4j
 
Graph Database Query Languages
Jay Coskey
 
Neo4j 20 minutes introduction
András Fehér
 
New opportunities for connected data
Neo4j
 
Hands on Training – Graph Database with Neo4j
Serendio Inc.
 
Introduction to Neo4j and .Net
Neo4j
 
Hr analytics and graphs : job recommendations
Linkurious
 
Neo4j Introduction (Basics, Cypher, RDBMS to GRAPH)
David Fombella Pombal
 
managing big data
Suveeksha
 
Open data with Neo4j and Kotlin
Neo4j
 
Ad

More from jaxLondonConference (17)

PDF
Garbage Collection: the Useful Parts - Martijn Verburg & Dr John Oliver (jCla...
jaxLondonConference
 
PDF
Conflict Free Replicated Data-types in Eventually Consistent Systems - Joel J...
jaxLondonConference
 
PDF
JVM Support for Multitenant Applications - Steve Poole (IBM)
jaxLondonConference
 
PPTX
Why other ppl_dont_get_it
jaxLondonConference
 
PDF
Java Testing With Spock - Ken Sipe (Trexin Consulting)
jaxLondonConference
 
PDF
Big Events, Mob Scale - Darach Ennis (Push Technology)
jaxLondonConference
 
PDF
The Java Virtual Machine is Over - The Polyglot VM is here - Marcus Lagergren...
jaxLondonConference
 
PDF
Java EE 7 Platform: Boosting Productivity and Embracing HTML5 - Arun Gupta (R...
jaxLondonConference
 
PPT
Exploring the Talend unified Big Data toolset for sentiment analysis - Ben Br...
jaxLondonConference
 
PDF
The Curious Clojurist - Neal Ford (Thoughtworks)
jaxLondonConference
 
PPTX
TDD at scale - Mash Badar (UBS)
jaxLondonConference
 
PDF
Run Your Java Code on Cloud Foundry - Andy Piper (Pivotal)
jaxLondonConference
 
PDF
Put your Java apps to sleep? Find out how - John Matthew Holt (Waratek)
jaxLondonConference
 
PPTX
Project Lambda: Functional Programming Constructs in Java - Simon Ritter (Ora...
jaxLondonConference
 
PPTX
Do You Like Coffee with Your dessert? Java and the Raspberry Pi - Simon Ritte...
jaxLondonConference
 
PPTX
Large scale, interactive ad-hoc queries over different datastores with Apache...
jaxLondonConference
 
PDF
Designing Resilient Application Platforms with Apache Cassandra - Hayato Shim...
jaxLondonConference
 
Garbage Collection: the Useful Parts - Martijn Verburg & Dr John Oliver (jCla...
jaxLondonConference
 
Conflict Free Replicated Data-types in Eventually Consistent Systems - Joel J...
jaxLondonConference
 
JVM Support for Multitenant Applications - Steve Poole (IBM)
jaxLondonConference
 
Why other ppl_dont_get_it
jaxLondonConference
 
Java Testing With Spock - Ken Sipe (Trexin Consulting)
jaxLondonConference
 
Big Events, Mob Scale - Darach Ennis (Push Technology)
jaxLondonConference
 
The Java Virtual Machine is Over - The Polyglot VM is here - Marcus Lagergren...
jaxLondonConference
 
Java EE 7 Platform: Boosting Productivity and Embracing HTML5 - Arun Gupta (R...
jaxLondonConference
 
Exploring the Talend unified Big Data toolset for sentiment analysis - Ben Br...
jaxLondonConference
 
The Curious Clojurist - Neal Ford (Thoughtworks)
jaxLondonConference
 
TDD at scale - Mash Badar (UBS)
jaxLondonConference
 
Run Your Java Code on Cloud Foundry - Andy Piper (Pivotal)
jaxLondonConference
 
Put your Java apps to sleep? Find out how - John Matthew Holt (Waratek)
jaxLondonConference
 
Project Lambda: Functional Programming Constructs in Java - Simon Ritter (Ora...
jaxLondonConference
 
Do You Like Coffee with Your dessert? Java and the Raspberry Pi - Simon Ritte...
jaxLondonConference
 
Large scale, interactive ad-hoc queries over different datastores with Apache...
jaxLondonConference
 
Designing Resilient Application Platforms with Apache Cassandra - Hayato Shim...
jaxLondonConference
 

Recently uploaded (20)

PDF
Transforming Utility Networks: Large-scale Data Migrations with FME
Safe Software
 
PPTX
COMPARISON OF RASTER ANALYSIS TOOLS OF QGIS AND ARCGIS
Sharanya Sarkar
 
PDF
How do you fast track Agentic automation use cases discovery?
DianaGray10
 
PDF
NLJUG Speaker academy 2025 - first session
Bert Jan Schrijver
 
PDF
Kit-Works Team Study_20250627_한달만에만든사내서비스키링(양다윗).pdf
Wonjun Hwang
 
PDF
Reverse Engineering of Security Products: Developing an Advanced Microsoft De...
nwbxhhcyjv
 
PDF
The Rise of AI and IoT in Mobile App Tech.pdf
IMG Global Infotech
 
PDF
Mastering Financial Management in Direct Selling
Epixel MLM Software
 
PDF
LOOPS in C Programming Language - Technology
RishabhDwivedi43
 
PDF
“NPU IP Hardware Shaped Through Software and Use-case Analysis,” a Presentati...
Edge AI and Vision Alliance
 
PDF
Transcript: Book industry state of the nation 2025 - Tech Forum 2025
BookNet Canada
 
PDF
“Voice Interfaces on a Budget: Building Real-time Speech Recognition on Low-c...
Edge AI and Vision Alliance
 
PDF
Bitcoin for Millennials podcast with Bram, Power Laws of Bitcoin
Stephen Perrenod
 
PDF
NASA A Researcher’s Guide to International Space Station : Physical Sciences ...
Dr. PANKAJ DHUSSA
 
PPTX
Agentforce World Tour Toronto '25 - MCP with MuleSoft
Alexandra N. Martinez
 
PDF
SIZING YOUR AIR CONDITIONER---A PRACTICAL GUIDE.pdf
Muhammad Rizwan Akram
 
PDF
Future-Proof or Fall Behind? 10 Tech Trends You Can’t Afford to Ignore in 2025
DIGITALCONFEX
 
PPT
Ericsson LTE presentation SEMINAR 2010.ppt
npat3
 
PDF
Automating Feature Enrichment and Station Creation in Natural Gas Utility Net...
Safe Software
 
PPTX
Q2 FY26 Tableau User Group Leader Quarterly Call
lward7
 
Transforming Utility Networks: Large-scale Data Migrations with FME
Safe Software
 
COMPARISON OF RASTER ANALYSIS TOOLS OF QGIS AND ARCGIS
Sharanya Sarkar
 
How do you fast track Agentic automation use cases discovery?
DianaGray10
 
NLJUG Speaker academy 2025 - first session
Bert Jan Schrijver
 
Kit-Works Team Study_20250627_한달만에만든사내서비스키링(양다윗).pdf
Wonjun Hwang
 
Reverse Engineering of Security Products: Developing an Advanced Microsoft De...
nwbxhhcyjv
 
The Rise of AI and IoT in Mobile App Tech.pdf
IMG Global Infotech
 
Mastering Financial Management in Direct Selling
Epixel MLM Software
 
LOOPS in C Programming Language - Technology
RishabhDwivedi43
 
“NPU IP Hardware Shaped Through Software and Use-case Analysis,” a Presentati...
Edge AI and Vision Alliance
 
Transcript: Book industry state of the nation 2025 - Tech Forum 2025
BookNet Canada
 
“Voice Interfaces on a Budget: Building Real-time Speech Recognition on Low-c...
Edge AI and Vision Alliance
 
Bitcoin for Millennials podcast with Bram, Power Laws of Bitcoin
Stephen Perrenod
 
NASA A Researcher’s Guide to International Space Station : Physical Sciences ...
Dr. PANKAJ DHUSSA
 
Agentforce World Tour Toronto '25 - MCP with MuleSoft
Alexandra N. Martinez
 
SIZING YOUR AIR CONDITIONER---A PRACTICAL GUIDE.pdf
Muhammad Rizwan Akram
 
Future-Proof or Fall Behind? 10 Tech Trends You Can’t Afford to Ignore in 2025
DIGITALCONFEX
 
Ericsson LTE presentation SEMINAR 2010.ppt
npat3
 
Automating Feature Enrichment and Station Creation in Natural Gas Utility Net...
Safe Software
 
Q2 FY26 Tableau User Group Leader Quarterly Call
lward7
 

Designing and Building a Graph Database Application - Ian Robinson (Neo Technology)

  • 1. Twi$er:  @ianSrobinson  #neo4j   Designing  and  Building  a  Graph   Database  Applica5on  
  • 2. Roadmap   •  Complex  data   •  Designing  a  graph  data  model  and  queries   •  Tes@ng  
  • 3. Data  Complexity   complexity = f(size , semi-structure, connectedness)
  • 11. Securi@es  and  Debt   Image:  orgnet.com  
  • 13. Graph  Databases   •  Store   •  Manage   •  Query   data  
  • 14. Neo4j  is  a  Graph  Database  
  • 16. Designing  a  Graph  Model  
  • 17. Models   Purposeful  abstrac@on  of  a  domain  designed  to   sa@sfy  par@cular  applica@on/end-­‐user  goals   Images:  en.wikipedia.org  
  • 18. Applica@on/End-­‐User  Goals   As  an  emp   loyee   I  want  to  k now  who  i n  the  comp has  similar any    skills  to  m   e   So  that  we  can  excha nge  knowl edge  
  • 19. Ques@ons  To  Ask  of  the  Domain   As  an  emp loyee     I  want  to  k now  who  i n  the  co has  similar   skills  to  me mpany       So  that  we  can  excha nge  knowle dge   Which  people,  who  work  for  the  same  company   as  me,  have  similar  skills  to  me?  
  • 20. Iden@fy  En@@es   Which  people,  who  work  for  the  same  company   as  me,  have  similar  skills  to  me?     Person   Company   Skill  
  • 21. Iden@fy  Rela@onships  Between  En@@es   Which  people,  who  work  for  the  same  company   as  me,  have  similar  skills  to  me?     Person  WORKS_FOR  Company   Person  HAS_SKILL  Skill  
  • 22. Convert  to  Cypher  Paths   Rela@onship   Person  WORKS_FOR  Company   Person  HAS_SKILL  Skill   Label   (:Person)-[:WORKS_FOR]->(:Company), (:Person)-[:HAS_SKILL]->(:Skill)
  • 24. Candidate  Data  Model   (:Company)<-[:WORKS_FOR]-(:Person)-[:HAS_SKILL]->(:Skill)
  • 25. Express  Ques@on  as  Graph  Pa$ern   Which  people,  who  work  for  the  same  company   as  me,  have  similar  skills  to  me?  
  • 26. Cypher  Query   Which  people,  who  work  for  the  same  company   as  me,  have  similar  skills  to  me?   MATCH (company)<-[:WORKS_FOR]-(me:Person)-[:HAS_SKILL]->(skill), (company)<-[:WORKS_FOR]-(colleague)-[:HAS_SKILL]->(skill) WHERE me.name = {name} RETURN colleague.name AS name, count(skill) AS score, collect(skill.name) AS skills ORDER BY score DESC
  • 27. Graph  Pa$ern   Which  people,  who  work  for  the  same  company   as  me,  have  similar  skills  to  me?   MATCH (company)<-[:WORKS_FOR]-(me:Person)-[:HAS_SKILL]->(skill), (company)<-[:WORKS_FOR]-(colleague)-[:HAS_SKILL]->(skill) WHERE me.name = {name} RETURN colleague.name AS name, count(skill) AS score, collect(skill.name) AS skills ORDER BY score DESC
  • 28. Anchor  Pa$ern  in  Graph   Which  people,  who  work  for  the  same  company   as  me,  have  similar  skills  to  me?   MATCH (company)<-[:WORKS_FOR]-(me:Person)-[:HAS_SKILL]->(skill), (company)<-[:WORKS_FOR]-(colleague)-[:HAS_SKILL]->(skill) WHERE me.name = {name} RETURN colleague.name AS name, count(skill) AS score, collect(skill.name) AS skills ORDER BY score DESC Search  nodes  labeled   ‘Person’,  matching  on   ‘name’  property  
  • 29. Create  Projec@on  of  Results   Which  people,  who  work  for  the  same  company   as  me,  have  similar  skills  to  me?   MATCH (company)<-[:WORKS_FOR]-(me:Person)-[:HAS_SKILL]->(skill), (company)<-[:WORKS_FOR]-(colleague)-[:HAS_SKILL]->(skill) WHERE me.name = {name} RETURN colleague.name AS name, count(skill) AS score, collect(skill.name) AS skills ORDER BY score DESC
  • 33. Running  the  Query   +-----------------------------------+ | name | score | skills | +-----------------------------------+ | "Lucy" | 2 | ["Java","Neo4j"] | | "Bill" | 1 | ["Neo4j"] | +-----------------------------------+ 2 rows
  • 34. From  User  Story  to  Model  and  Query   As  an  emp loyee     I  want  to  k now  who  i n  the  co has  similar   skills  to  me mpany       So  that  we  can  excha nge  knowle dge   MATCH (company)<-[:WORKS_FOR]-(me:Person)-[:HAS_SKILL]->(skill), (company)<-[:WORKS_FOR]-(colleague)-[:HAS_SKILL]->(skill) WHERE me.name = {name} RETURN colleague.name AS name, count(skill) AS score, collect(skill.name) AS skills ORDER BY score DESC ? Which  people,  who  work  for  the  same   company  as  me,  have  similar  skills  to  me? Person  WORKS_FOR  Company   Person  HAS_SKILL  Skill (:Company)<-[:WORKS_FOR]-(:Person)-[:HAS_SKILL]->(:Skill)
  • 36. Why  Test?   •  Ensure  model  is  fit  for  queries   –  Rapid  feedback   •  Ensure  correctness  of  queries   •  Document  your  understanding  of  your  domain   –  Including  corner  cases  and  excep@ons   •  Provide  a  regression  test  suite   –  Allows  you  to  change  and  evolve  model  and   queries  
  • 37. Method   •  Develop  queries,  or  classes  that  encapsulate   queries,  using  unit  tests   •  Use  small,  well-­‐understood  datasets  in  each  test   –  Create  data  in  test  setup   –  Test  dataset  expresses  your  understanding  of  (part  of)   the  domain   •  Inject  in-­‐memory  graph  database  (or  Cypher   engine)  into  object  under  test   •  The  exact  strategy  you  use  depends  on  your   applica@on  architecture…  
  • 38. Applica@on  Architectures   •  Embedded   •  Server   •  Server  with  Extensions  
  • 39. Applica@on  Architectures   •  Embedded   –  Host  in  Java  process   –  Access  to  Java  APIs   •  Server   •  Server  with  Extensions   Applica@on   Java  APIs  
  • 40. Applica@on  Architectures   •  Embedded   •  Server   –  HTTP/JSON  interface   –  Server  wraps  embedded   instance   •  Server  with  Extensions   Applica@on   REST  Client   Write  LB   REST  API   REST  API   Read  LB   REST  API  
  • 41. Applica@on  Architectures   •  Embedded   •  Server   •  Server  with  Extensions   REST  API   –  Execute  complex  logic  on  server   –  Control  HTTP  request/response  format   Extensions  
  • 42. Embedded  Example   •  Company  social  network   •  Find  colleagues  with  similar  skills   •  Encapsulate  query  in  a  ColleagueFinder
  • 43. Unit  Test  Fixture   public class ColleagueFinderTest { private GraphDatabaseService db; private ColleagueFinder finder; @Before public void init() { db = new TestGraphDatabaseFactory().newImpermanentDatabase(); ExampleGraph.populate( db ); finder = new ColleagueFinder( new ExecutionEngine( db ) ); } @After public void shutdown() { db.shutdown(); } }
  • 44. Create  Database   public class ColleagueFinderTest { private GraphDatabaseService db; private ColleagueFinder finder; @Before public void init() { db = new TestGraphDatabaseFactory().newImpermanentDatabase(); ExampleGraph.populate( db ); finder = new ColleagueFinder( new ExecutionEngine( db ) ); } @After public void shutdown() { db.shutdown(); } }
  • 45. Populate  Graph   public class ColleagueFinderTest { private GraphDatabaseService db; private ColleagueFinder finder; @Before public void init() { db = new TestGraphDatabaseFactory().newImpermanentDatabase(); ExampleGraph.populate( db ); finder = new ColleagueFinder( new ExecutionEngine( db ) ); } @After public void shutdown() { db.shutdown(); } }
  • 46. Create  Object  Under  Test   public class ColleagueFinderTest { private GraphDatabaseService db; private ColleagueFinder finder; @Before public void init() { db = new TestGraphDatabaseFactory().newImpermanentDatabase(); ExampleGraph.populate( db ); finder = new ColleagueFinder( new ExecutionEngine( db ) ); } @After public void shutdown() { db.shutdown(); } } Inject     Execu@onEngine  
  • 47. ImpermanentGraphDatabase   •  In-­‐memory   •  For  tes@ng  only,  not  produc@on!   <dependency> <groupId>org.neo4j</groupId> <artifactId>neo4j-kernel</artifactId> <version>${project.version}</version> <type>test-jar</type> <scope>test</scope> </dependency>
  • 48. Create  Sample  Data   public static void populate( GraphDatabaseService db ) { ExecutionEngine engine = new ExecutionEngine( db ); String cypher = "CREATE ian:Person VALUES {name:'Ian'},n" + " bill:Person VALUES {name:'Bill'},n" + " lucy:Person VALUES {name:'Lucy'},n" + " acme:Company VALUES {name:'Acme'},n" + // Cypher continues... " " " " (bill)-[:HAS_SKILL]->(neo4j),n" + (bill)-[:HAS_SKILL]->(ruby),n" + (lucy)-[:HAS_SKILL]->(java),n" + (lucy)-[:HAS_SKILL]->(neo4j)"; engine.execute( cypher ); }
  • 49. Unit  Test   @Test public void shouldFindColleaguesWithSimilarSkills() throws Exception { // when Iterator<Map<String, Object>> results = finder.findColleaguesFor( "Ian" ); // then assertEquals( "Lucy", results.next().get( "name" ) ); assertEquals( "Bill", results.next().get( "name" ) ); assertFalse( results.hasNext() ); }
  • 50. Execute  Object  Under  Test   @Test public void shouldFindColleaguesWithSimilarSkills() throws Exception { // when Iterator<Map<String, Object>> results = finder.findColleaguesFor( "Ian" ); // then assertEquals( "Lucy", results.next().get( "name" ) ); assertEquals( "Bill", results.next().get( "name" ) ); assertFalse( results.hasNext() ); }
  • 51. Assert  Results   @Test public void shouldFindColleaguesWithSimilarSkills() throws Exception { // when Iterator<Map<String, Object>> results = finder.findColleaguesFor( "Ian" ); // then assertEquals( "Lucy", results.next().get( "name" ) ); assertEquals( "Bill", results.next().get( "name" ) ); assertFalse( results.hasNext() ); }
  • 52. Ensure  No  More  Results   @Test public void shouldFindColleaguesWithSimilarSkills() throws Exception { // when Iterator<Map<String, Object>> results = finder.findColleaguesFor( "Ian" ); // then assertEquals( "Lucy", results.next().get( "name" ) ); assertEquals( "Bill", results.next().get( "name" ) ); assertFalse( results.hasNext() ); }
  • 53. ColleagueFinder   public class ColleagueFinder { private final ExecutionEngine executionEngine; public ColleagueFinder( ExecutionEngine executionEngine ) { this.executionEngine = executionEngine; } public Iterator<Map<String, Object>> findColleaguesFor( String name ) { ... } }
  • 54. Inject  Execu@onEngine   public class ColleagueFinder { private final ExecutionEngine executionEngine; public ColleagueFinder( ExecutionEngine executionEngine ) { this.executionEngine = executionEngine; } public Iterator<Map<String, Object>> findColleaguesFor( String name ) { ... } }
  • 55. findColleaguesFor()  Method   public Iterator<Map<String, Object>> findColleaguesFor( String name ) { String cypher = "MATCH (me:Person)-[:WORKS_FOR]->(company),n" + " (me)-[:HAS_SKILL]->(skill),n" + " (colleague)-[:WORKS_FOR]->(company),n" + " (colleague)-[:HAS_SKILL]->(skill)n" + "WHERE me.name = {name}n" + "RETURN colleague.name AS name,n" + " count(skill) AS score,n" + " collect(skill.name) AS skillsn" + "ORDER BY score DESC"; Map<String, Object> params = new HashMap<String, Object>(); params.put( "name", name ); return executionEngine.execute( cypher, params ).iterator(); }
  • 56. Cypher  Query   public Iterator<Map<String, Object>> findColleaguesFor( String name ) { String cypher = "MATCH (me:Person)-[:WORKS_FOR]->(company),n" + " (me)-[:HAS_SKILL]->(skill),n" + " (colleague)-[:WORKS_FOR]->(company),n" + " (colleague)-[:HAS_SKILL]->(skill)n" + "WHERE me.name = {name}n" + "RETURN colleague.name AS name,n" + " count(skill) AS score,n" + " collect(skill.name) AS skillsn" + "ORDER BY score DESC"; Map<String, Object> params = new HashMap<String, Object>(); params.put( "name", name ); return executionEngine.execute( cypher, params ).iterator(); }
  • 57. Parameterized  Query   public Iterator<Map<String, Object>> findColleaguesFor( String name ) { String cypher = "MATCH (me:Person)-[:WORKS_FOR]->(company),n" + " (me)-[:HAS_SKILL]->(skill),n" + " (colleague)-[:WORKS_FOR]->(company),n" + " (colleague)-[:HAS_SKILL]->(skill)n" + "WHERE me.name = {name}n" + "RETURN colleague.name AS name,n" + " count(skill) AS score,n" + " collect(skill.name) AS skillsn" + "ORDER BY score DESC"; Map<String, Object> params = new HashMap<String, Object>(); params.put( "name", name ); return executionEngine.execute( cypher, params ).iterator(); }
  • 58. Execute  Query   public Iterator<Map<String, Object>> findColleaguesFor( String name ) { String cypher = "MATCH (me:Person)-[:WORKS_FOR]->(company),n" + " (me)-[:HAS_SKILL]->(skill),n" + " (colleague)-[:WORKS_FOR]->(company),n" + " (colleague)-[:HAS_SKILL]->(skill)n" + "WHERE me.name = {name}n" + "RETURN colleague.name AS name,n" + " count(skill) AS score,n" + " collect(skill.name) AS skillsn" + "ORDER BY score DESC"; Map<String, Object> params = new HashMap<String, Object>(); params.put( "name", name ); return executionEngine.execute( cypher, params ).iterator(); }
  • 59. Server  Extension  Example   •  Same  data  mode  and  query  as  before   •  This  @me,  we’ll  host  ColleagueFinder  in  a   server  extension  
  • 60. Server  Extension   @Path("/similar-skills") public class ColleagueFinderExtension { private static final ObjectMapper MAPPER = new ObjectMapper(); private final ColleagueFinder colleagueFinder; public ColleagueFinderExtension( @Context CypherExecutor cypherExecutor ) { this.colleagueFinder = new ColleagueFinder( cypherExecutor.getExecutionEngine() ); } @GET @Produces(MediaType.APPLICATION_JSON) @Path("/{name}") public Response getColleagues( @PathParam("name") String name ) throws IOException { String json = MAPPER .writeValueAsString( colleagueFinder.findColleaguesFor( name ) ); return Response.ok().entity( json ).build(); } }
  • 61. JAX-­‐RS  Annota@ons   @Path("/similar-skills") public class ColleagueFinderExtension { private static final ObjectMapper MAPPER = new ObjectMapper(); private final ColleagueFinder colleagueFinder; public ColleagueFinderExtension( @Context CypherExecutor cypherExecutor ) { this.colleagueFinder = new ColleagueFinder( cypherExecutor.getExecutionEngine() ); } @GET @Produces(MediaType.APPLICATION_JSON) @Path("/{name}") public Response getColleagues( @PathParam("name") String name ) throws IOException { String json = MAPPER .writeValueAsString( colleagueFinder.findColleaguesFor( name ) ); return Response.ok().entity( json ).build(); } }
  • 62. Map  HTTP  Request  to  Object  +  Method   @Path("/similar-skills") public class ColleagueFinderExtension { private static final ObjectMapper MAPPER = new ObjectMapper(); private final ColleagueFinder colleagueFinder; public ColleagueFinderExtension( @Context CypherExecutor cypherExecutor ) { this.colleagueFinder = new ColleagueFinder( cypherExecutor.getExecutionEngine() ); } @GET @Produces(MediaType.APPLICATION_JSON) @Path("/{name}") public Response getColleagues( @PathParam("name") String name ) throws IOException { String json = MAPPER .writeValueAsString( colleagueFinder.findColleaguesFor( name ) ); return Response.ok().entity( json ).build(); } } GET   /similar-­‐skills   /Ian  
  • 63. CypherExecutor  Injected  by  Server   Ensures   Execu@onEngine   reused  across   resource  instances   @Path("/similar-skills") public class ColleagueFinderExtension { private static final ObjectMapper MAPPER = new ObjectMapper(); private final ColleagueFinder colleagueFinder; public ColleagueFinderExtension( @Context CypherExecutor cypherExecutor ) { this.colleagueFinder = new ColleagueFinder( cypherExecutor.getExecutionEngine() ); } @GET @Produces(MediaType.APPLICATION_JSON) @Path("/{name}") public Response getColleagues( @PathParam("name") String name ) throws IOException { String json = MAPPER .writeValueAsString( colleagueFinder.findColleaguesFor( name ) ); return Response.ok().entity( json ).build(); } }
  • 64. Generate  and  Format  Response   @Path("/similar-skills") public class ColleagueFinderExtension { private static final ObjectMapper MAPPER = new ObjectMapper(); private final ColleagueFinder colleagueFinder; public ColleagueFinderExtension( @Context CypherExecutor cypherExecutor ) { this.colleagueFinder = new ColleagueFinder( cypherExecutor.getExecutionEngine() ); } @GET @Produces(MediaType.APPLICATION_JSON) @Path("/{name}") public Response getColleagues( @PathParam("name") String name ) throws IOException { String json = MAPPER .writeValueAsString( colleagueFinder.findColleaguesFor( name ) ); return Response.ok().entity( json ).build(); } }
  • 65. Extension  Test  Fixture   public class ColleagueFinderExtensionTest { private CommunityNeoServer server; @Before public void startServer() throws IOException { server = CommunityServerBuilder.server() .withThirdPartyJaxRsPackage( "org.neo4j.good_practices", "/colleagues" ) .build(); server.start(); ExampleGraph.populate( server.getDatabase().getGraph() ); } @After public void stopServer() { server.stop(); } }
  • 66. Build  and  Configure  Server   public class ColleagueFinderExtensionTest { private CommunityNeoServer server; @Before public void startServer() throws IOException { server = CommunityServerBuilder.server() .withThirdPartyJaxRsPackage( "org.neo4j.good_practices", "/colleagues" ) .build(); server.start(); ExampleGraph.populate( server.getDatabase().getGraph() ); } @After public void stopServer() { server.stop(); } }
  • 67. Start  Server   public class ColleagueFinderExtensionTest { private CommunityNeoServer server; @Before public void startServer() throws IOException { server = CommunityServerBuilder.server() .withThirdPartyJaxRsPackage( "org.neo4j.good_practices", "/colleagues" ) .build(); server.start(); ExampleGraph.populate( server.getDatabase().getGraph() ); } @After public void stopServer() { server.stop(); } }
  • 68. Populate  Database   public class ColleagueFinderExtensionTest { private CommunityNeoServer server; @Before public void startServer() throws IOException { server = CommunityServerBuilder.server() .withThirdPartyJaxRsPackage( "org.neo4j.good_practices", "/colleagues" ) .build(); server.start(); ExampleGraph.populate( server.getDatabase().getGraph() ); } @After public void stopServer() { server.stop(); } }
  • 69. CommunityServerBuilder   •  Programma@c  configura@on   <dependency> <groupId>org.neo4j.app</groupId> <artifactId>neo4j-server</artifactId> <version>${project.version}</version> <type>test-jar</type> </dependency>
  • 70. Tes@ng  Extension  Using  HTTP  Client   @Test public void shouldReturnColleaguesWithSimilarSkills() throws Exception { Client client = Client.create( new DefaultClientConfig() ); WebResource resource = client .resource( "http://localhost:7474/colleagues/similar-skills/Ian" ); ClientResponse response = resource .accept( MediaType.APPLICATION_JSON ) .get( ClientResponse.class ); List<Map<String, Object>> results = new ObjectMapper() .readValue(response.getEntity( String.class ), List.class ); // Assertions ...
  • 71. Create  HTTP  Client   @Test public void shouldReturnColleaguesWithSimilarSkills() throws Exception { Client client = Client.create( new DefaultClientConfig() ); WebResource resource = client .resource( "http://localhost:7474/colleagues/similar-skills/Ian" ); ClientResponse response = resource .accept( MediaType.APPLICATION_JSON ) .get( ClientResponse.class ); List<Map<String, Object>> results = new ObjectMapper() .readValue(response.getEntity( String.class ), List.class ); // Assertions ...
  • 72. Issue  Request   @Test public void shouldReturnColleaguesWithSimilarSkills() throws Exception { Client client = Client.create( new DefaultClientConfig() ); WebResource resource = client .resource( "http://localhost:7474/colleagues/similar-skills/Ian" ); ClientResponse response = resource .accept( MediaType.APPLICATION_JSON ) .get( ClientResponse.class ); List<Map<String, Object>> results = new ObjectMapper() .readValue(response.getEntity( String.class ), List.class ); // Assertions ...
  • 73. Parse  Response   @Test public void shouldReturnColleaguesWithSimilarSkills() throws Exception { Client client = Client.create( new DefaultClientConfig() ); WebResource resource = client .resource( "http://localhost:7474/colleagues/similar-skills/Ian" ); ClientResponse response = resource .accept( MediaType.APPLICATION_JSON ) .get( ClientResponse.class ); List<Map<String, Object>> results = new ObjectMapper() .readValue(response.getEntity( String.class ), List.class ); // Assertions ...
  • 74. Assert  Results   ... assertEquals( 200, response.getStatus() ); assertEquals( MediaType.APPLICATION_JSON, response.getHeaders().get( "Content-Type" ).get( 0 ) ); assertEquals( "Lucy", results.get( 0 ).get( "name" ) ); assertThat( (Iterable<String>) results.get( 0 ).get( "skills" ), hasItems( "Java", "Neo4j" ) ); }
  • 75. ts gy en lo i m no pl h m ec Co eo T N of Graph h Databases Thank  you   Twi$er:  @ianSrobinson   #neo4j     Neo4j  User  Group   30th  Oct   Skillsma$er                       Ian Robinson, Jim Webber & Emil Eifrem github.com/iansrobinson/neo4j-­‐good-­‐prac@ces