SlideShare a Scribd company logo
Designing	
  and	
  Building	
  a	
  Graph	
  
Database	
  Applica5on

	
  

Twi9er:	
  @ianSrobinson	
  
#neo4j	
  
	
  
Outline	
  
•  Data	
  modeling	
  with	
  graphs	
  
•  Neo4j	
  applica5on	
  architecture	
  op5ons	
  
•  Tes5ng	
  your	
  data	
  model	
  	
  
Graph	
  data	
  modeling	
  
Labeled	
  Property	
  Graph	
  
Models	
  
Purposeful	
  abstrac5on	
  of	
  a	
  domain	
  designed	
  to	
  
sa5sfy	
  par5cular	
  applica5on/end-­‐user	
  goals	
  

Images:	
  en.wikipedia.org	
  
Example	
  Applica5on	
  
•  Knowledge	
  management	
  
–  People,	
  companies,	
  skills	
  
–  Cross	
  organiza5onal	
  

•  Find	
  my	
  professional	
  social	
  network	
  
–  Exchange	
  knowledge	
  
–  Interest	
  groups	
  
–  Help	
  
–  Staff	
  projects	
  
Applica5on/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	
  
Ques5ons	
  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?	
  
Iden5fy	
  En55es	
  
Which	
  people,	
  who	
  work	
  for	
  the	
  same	
  company	
  
as	
  me,	
  have	
  similar	
  skills	
  to	
  me?	
  
	
  
Person	
  
Company	
  
Skill	
  
Iden5fy	
  Rela5onships	
  Between	
  En55es	
  
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	
  
Rela5onship	
  

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	
  Ques5on	
  as	
  Graph	
  Pa9ern	
  
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	
  Pa9ern	
  
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	
  Pa9ern	
  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	
  Projec5on	
  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)
Tes5ng	
  
Why	
  Test?	
  
•  Ensure	
  model	
  is	
  fit	
  for	
  queries	
  
–  Rapid	
  feedback	
  

•  Ensure	
  correctness	
  of	
  queries	
  
•  Document	
  your	
  understanding	
  of	
  your	
  domain	
  
–  Including	
  corner	
  cases	
  and	
  excep5ons	
  

•  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	
  
applica5on	
  architecture…	
  
Applica5on	
  Architectures	
  
•  Embedded	
  
•  Server	
  
•  Server	
  with	
  Extensions	
  
Applica5on	
  Architectures	
  
•  Embedded	
  
–  Host	
  in	
  Java	
  process	
  
–  Access	
  to	
  Java	
  APIs	
  

•  Server	
  
•  Server	
  with	
  Extensions	
  

Applica5on	
  
Java	
  APIs	
  
Applica5on	
  Architectures	
  
•  Embedded	
  
•  Server	
  
–  HTTP/JSON	
  interface	
  
–  Server	
  wraps	
  embedded	
  
instance	
  

•  Server	
  with	
  Extensions	
  

Applica5on	
  
REST	
  Client	
  

Write	
  LB	
  
REST	
  API	
  

REST	
  API	
  

Read	
  LB	
  
REST	
  API	
  
Applica5on	
  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	
  	
  
Execu5onEngine	
  
ImpermanentGraphDatabase	
  
•  In-­‐memory	
  
•  For	
  tes5ng	
  only,	
  not	
  produc5on!	
  
	
	
<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	
  Execu5onEngine	
  
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	
  5me,	
  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	
  Annota5ons	
  
@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	
  
Execu5onEngine	
  
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	
  
•  Programma5c	
  configura5on	
  
	
	
<dependency>	
<groupId>org.neo4j.app</groupId>	
<artifactId>neo4j-server</artifactId>	
<version>${project.version}</version>	
<type>test-jar</type>	
</dependency>
Tes5ng	
  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	
  
Twi9er:	
  @ianSrobinson	
  
#neo4j	
  
	
  
	
  
	
  

Ian Robinson,
Jim Webber & Emil Eifrem

github.com/iansrobinson/neo4j-­‐good-­‐prac5ces	
  
	
  

More Related Content

What's hot (20)

PDF
Knowledge Graphs - The Power of Graph-Based Search
Neo4j
 
PDF
Data Mesh
Piethein Strengholt
 
PDF
PySpark Best Practices
Cloudera, Inc.
 
PDF
From Data Warehouse to Lakehouse
Modern Data Stack France
 
PPTX
Graphs in Automotive and Manufacturing - Unlock New Value from Your Data
Neo4j
 
PDF
Scaling and Modernizing Data Platform with Databricks
Databricks
 
PDF
Intro to Delta Lake
Databricks
 
PPTX
Open Metadata and Governance with Apache Atlas
DataWorks Summit
 
PPTX
Data Lakehouse, Data Mesh, and Data Fabric (r1)
James Serra
 
PDF
A Practical Enterprise Feature Store on Delta Lake
Databricks
 
PDF
Scaling into Billions of Nodes and Relationships with Neo4j Graph Data Science
Neo4j
 
PDF
Apache Sqoop Tutorial | Sqoop: Import & Export Data From MySQL To HDFS | Hado...
Edureka!
 
PDF
Data Lake,beyond the Data Warehouse
Data Science Thailand
 
PDF
Graph Databases for Master Data Management
Neo4j
 
PPTX
Zero to Snowflake Presentation
Brett VanderPlaats
 
PDF
Snowflake: The most cost-effective agile and scalable data warehouse ever!
Visual_BI
 
PDF
Pipelines and Data Flows: Introduction to Data Integration in Azure Synapse A...
Cathrine Wilhelmsen
 
PDF
Introduction to Graph Databases
DataStax
 
PPTX
Great Expectations Presentation
Adam Doyle
 
PDF
Data Mesh Part 4 Monolith to Mesh
Jeffrey T. Pollock
 
Knowledge Graphs - The Power of Graph-Based Search
Neo4j
 
PySpark Best Practices
Cloudera, Inc.
 
From Data Warehouse to Lakehouse
Modern Data Stack France
 
Graphs in Automotive and Manufacturing - Unlock New Value from Your Data
Neo4j
 
Scaling and Modernizing Data Platform with Databricks
Databricks
 
Intro to Delta Lake
Databricks
 
Open Metadata and Governance with Apache Atlas
DataWorks Summit
 
Data Lakehouse, Data Mesh, and Data Fabric (r1)
James Serra
 
A Practical Enterprise Feature Store on Delta Lake
Databricks
 
Scaling into Billions of Nodes and Relationships with Neo4j Graph Data Science
Neo4j
 
Apache Sqoop Tutorial | Sqoop: Import & Export Data From MySQL To HDFS | Hado...
Edureka!
 
Data Lake,beyond the Data Warehouse
Data Science Thailand
 
Graph Databases for Master Data Management
Neo4j
 
Zero to Snowflake Presentation
Brett VanderPlaats
 
Snowflake: The most cost-effective agile and scalable data warehouse ever!
Visual_BI
 
Pipelines and Data Flows: Introduction to Data Integration in Azure Synapse A...
Cathrine Wilhelmsen
 
Introduction to Graph Databases
DataStax
 
Great Expectations Presentation
Adam Doyle
 
Data Mesh Part 4 Monolith to Mesh
Jeffrey T. Pollock
 

Viewers also liked (15)

PPTX
Relational to Graph - Import
Neo4j
 
PDF
Graph Database, a little connected tour - Castano
Codemotion
 
PDF
Converting Relational to Graph Databases
Antonio Maccioni
 
PPTX
Relational databases vs Non-relational databases
James Serra
 
PDF
Graph Based Recommendation Systems at eBay
DataStax Academy
 
PPT
An Introduction to Graph Databases
InfiniteGraph
 
PDF
Relational vs. Non-Relational
PostgreSQL Experts, Inc.
 
KEY
NoSQL: Why, When, and How
BigBlueHat
 
PPTX
Lju Lazarevic
Connected Data World
 
PPTX
Neo4j - graph database for recommendations
proksik
 
PPTX
An Introduction to NOSQL, Graph Databases and Neo4j
Debanjan Mahata
 
PDF
Introduction to graph databases GraphDays
Neo4j
 
PPTX
Introduction to Graph Databases
Max De Marzi
 
PPTX
Data Mining: Graph mining and social network analysis
DataminingTools Inc
 
PDF
Data Modeling with Neo4j
Neo4j
 
Relational to Graph - Import
Neo4j
 
Graph Database, a little connected tour - Castano
Codemotion
 
Converting Relational to Graph Databases
Antonio Maccioni
 
Relational databases vs Non-relational databases
James Serra
 
Graph Based Recommendation Systems at eBay
DataStax Academy
 
An Introduction to Graph Databases
InfiniteGraph
 
Relational vs. Non-Relational
PostgreSQL Experts, Inc.
 
NoSQL: Why, When, and How
BigBlueHat
 
Lju Lazarevic
Connected Data World
 
Neo4j - graph database for recommendations
proksik
 
An Introduction to NOSQL, Graph Databases and Neo4j
Debanjan Mahata
 
Introduction to graph databases GraphDays
Neo4j
 
Introduction to Graph Databases
Max De Marzi
 
Data Mining: Graph mining and social network analysis
DataminingTools Inc
 
Data Modeling with Neo4j
Neo4j
 
Ad

Similar to Designing and Building a Graph Database Application – Architectural Choices, Data Modeling, and Testing - Ian Robinson @ GraphConnect NY 2013 (20)

PDF
Designing and Building a Graph Database Application - Ian Robinson (Neo Techn...
jaxLondonConference
 
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
Hr analytics and graphs : job recommendations
Linkurious
 
PPTX
Graph Database Query Languages
Jay Coskey
 
PPTX
Neo4j 20 minutes introduction
András Fehér
 
PDF
Graph Search: The Power of Connected Data
Codemotion
 
PDF
Neo4j Introduction (Basics, Cypher, RDBMS to GRAPH)
David Fombella Pombal
 
PPTX
Introduction to Neo4j and .Net
Neo4j
 
PPTX
Next generation linked in talent search
Ryan Wu
 
PDF
Neo4j Data Science Presentation
Max De Marzi
 
PPT
Processing Large Graphs
Nishant Gandhi
 
PDF
How to Integrate Spark MLlib and Apache Solr to Build Real-Time Entity Type R...
Spark Summit
 
PDF
Propel your Performance: AgensGraph, the multi-model database
Joshua Bae
 
PDF
Recommending job ads to people
Fabian Abel
 
PDF
Open data with Neo4j and Kotlin
Neo4j
 
Designing and Building a Graph Database Application - Ian Robinson (Neo Techn...
jaxLondonConference
 
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
 
Hr analytics and graphs : job recommendations
Linkurious
 
Graph Database Query Languages
Jay Coskey
 
Neo4j 20 minutes introduction
András Fehér
 
Graph Search: The Power of Connected Data
Codemotion
 
Neo4j Introduction (Basics, Cypher, RDBMS to GRAPH)
David Fombella Pombal
 
Introduction to Neo4j and .Net
Neo4j
 
Next generation linked in talent search
Ryan Wu
 
Neo4j Data Science Presentation
Max De Marzi
 
Processing Large Graphs
Nishant Gandhi
 
How to Integrate Spark MLlib and Apache Solr to Build Real-Time Entity Type R...
Spark Summit
 
Propel your Performance: AgensGraph, the multi-model database
Joshua Bae
 
Recommending job ads to people
Fabian Abel
 
Open data with Neo4j and Kotlin
Neo4j
 
Ad

More from Neo4j (20)

PDF
GraphSummit Singapore Master Deck - May 20, 2025
Neo4j
 
PPTX
Graphs & GraphRAG - Essential Ingredients for GenAI
Neo4j
 
PPTX
Neo4j Knowledge for Customer Experience.pptx
Neo4j
 
PPTX
GraphTalk New Zealand - The Art of The Possible.pptx
Neo4j
 
PDF
Neo4j: The Art of the Possible with Graph
Neo4j
 
PDF
Smarter Knowledge Graphs For Public Sector
Neo4j
 
PDF
GraphRAG and Knowledge Graphs Exploring AI's Future
Neo4j
 
PDF
Matinée GenAI & GraphRAG Paris - Décembre 24
Neo4j
 
PDF
ANZ Presentation: GraphSummit Melbourne 2024
Neo4j
 
PDF
Google Cloud Presentation GraphSummit Melbourne 2024: Building Generative AI ...
Neo4j
 
PDF
Telstra Presentation GraphSummit Melbourne: Optimising Business Outcomes with...
Neo4j
 
PDF
Hands-On GraphRAG Workshop: GraphSummit Melbourne 2024
Neo4j
 
PDF
Démonstration Digital Twin Building Wire Management
Neo4j
 
PDF
Swiss Life - Les graphes au service de la détection de fraude dans le domaine...
Neo4j
 
PDF
Démonstration Supply Chain - GraphTalk Paris
Neo4j
 
PDF
The Art of Possible - GraphTalk Paris Opening Session
Neo4j
 
PPTX
How Siemens bolstered supply chain resilience with graph-powered AI insights ...
Neo4j
 
PDF
Knowledge Graphs for AI-Ready Data and Enterprise Deployment - Gartner IT Sym...
Neo4j
 
PDF
Neo4j Graph Data Modelling Session - GraphTalk
Neo4j
 
PDF
Neo4j: The Art of Possible with Graph Technology
Neo4j
 
GraphSummit Singapore Master Deck - May 20, 2025
Neo4j
 
Graphs & GraphRAG - Essential Ingredients for GenAI
Neo4j
 
Neo4j Knowledge for Customer Experience.pptx
Neo4j
 
GraphTalk New Zealand - The Art of The Possible.pptx
Neo4j
 
Neo4j: The Art of the Possible with Graph
Neo4j
 
Smarter Knowledge Graphs For Public Sector
Neo4j
 
GraphRAG and Knowledge Graphs Exploring AI's Future
Neo4j
 
Matinée GenAI & GraphRAG Paris - Décembre 24
Neo4j
 
ANZ Presentation: GraphSummit Melbourne 2024
Neo4j
 
Google Cloud Presentation GraphSummit Melbourne 2024: Building Generative AI ...
Neo4j
 
Telstra Presentation GraphSummit Melbourne: Optimising Business Outcomes with...
Neo4j
 
Hands-On GraphRAG Workshop: GraphSummit Melbourne 2024
Neo4j
 
Démonstration Digital Twin Building Wire Management
Neo4j
 
Swiss Life - Les graphes au service de la détection de fraude dans le domaine...
Neo4j
 
Démonstration Supply Chain - GraphTalk Paris
Neo4j
 
The Art of Possible - GraphTalk Paris Opening Session
Neo4j
 
How Siemens bolstered supply chain resilience with graph-powered AI insights ...
Neo4j
 
Knowledge Graphs for AI-Ready Data and Enterprise Deployment - Gartner IT Sym...
Neo4j
 
Neo4j Graph Data Modelling Session - GraphTalk
Neo4j
 
Neo4j: The Art of Possible with Graph Technology
Neo4j
 

Recently uploaded (20)

PDF
AI Agents in the Cloud: The Rise of Agentic Cloud Architecture
Lilly Gracia
 
PDF
The Rise of AI and IoT in Mobile App Tech.pdf
IMG Global Infotech
 
PPTX
Designing_the_Future_AI_Driven_Product_Experiences_Across_Devices.pptx
presentifyai
 
PDF
Book industry state of the nation 2025 - Tech Forum 2025
BookNet Canada
 
PDF
LOOPS in C Programming Language - Technology
RishabhDwivedi43
 
PDF
Transforming Utility Networks: Large-scale Data Migrations with FME
Safe Software
 
PDF
Peak of Data & AI Encore AI-Enhanced Workflows for the Real World
Safe Software
 
PDF
“Computer Vision at Sea: Automated Fish Tracking for Sustainable Fishing,” a ...
Edge AI and Vision Alliance
 
PDF
POV_ Why Enterprises Need to Find Value in ZERO.pdf
darshakparmar
 
PDF
The 2025 InfraRed Report - Redpoint Ventures
Razin Mustafiz
 
PDF
UPDF - AI PDF Editor & Converter Key Features
DealFuel
 
PPTX
New ThousandEyes Product Innovations: Cisco Live June 2025
ThousandEyes
 
PDF
Staying Human in a Machine- Accelerated World
Catalin Jora
 
PDF
[Newgen] NewgenONE Marvin Brochure 1.pdf
darshakparmar
 
PPTX
Agentforce World Tour Toronto '25 - MCP with MuleSoft
Alexandra N. Martinez
 
PDF
Newgen Beyond Frankenstein_Build vs Buy_Digital_version.pdf
darshakparmar
 
PDF
How do you fast track Agentic automation use cases discovery?
DianaGray10
 
PPTX
Q2 FY26 Tableau User Group Leader Quarterly Call
lward7
 
PDF
“Squinting Vision Pipelines: Detecting and Correcting Errors in Vision Models...
Edge AI and Vision Alliance
 
PDF
Go Concurrency Real-World Patterns, Pitfalls, and Playground Battles.pdf
Emily Achieng
 
AI Agents in the Cloud: The Rise of Agentic Cloud Architecture
Lilly Gracia
 
The Rise of AI and IoT in Mobile App Tech.pdf
IMG Global Infotech
 
Designing_the_Future_AI_Driven_Product_Experiences_Across_Devices.pptx
presentifyai
 
Book industry state of the nation 2025 - Tech Forum 2025
BookNet Canada
 
LOOPS in C Programming Language - Technology
RishabhDwivedi43
 
Transforming Utility Networks: Large-scale Data Migrations with FME
Safe Software
 
Peak of Data & AI Encore AI-Enhanced Workflows for the Real World
Safe Software
 
“Computer Vision at Sea: Automated Fish Tracking for Sustainable Fishing,” a ...
Edge AI and Vision Alliance
 
POV_ Why Enterprises Need to Find Value in ZERO.pdf
darshakparmar
 
The 2025 InfraRed Report - Redpoint Ventures
Razin Mustafiz
 
UPDF - AI PDF Editor & Converter Key Features
DealFuel
 
New ThousandEyes Product Innovations: Cisco Live June 2025
ThousandEyes
 
Staying Human in a Machine- Accelerated World
Catalin Jora
 
[Newgen] NewgenONE Marvin Brochure 1.pdf
darshakparmar
 
Agentforce World Tour Toronto '25 - MCP with MuleSoft
Alexandra N. Martinez
 
Newgen Beyond Frankenstein_Build vs Buy_Digital_version.pdf
darshakparmar
 
How do you fast track Agentic automation use cases discovery?
DianaGray10
 
Q2 FY26 Tableau User Group Leader Quarterly Call
lward7
 
“Squinting Vision Pipelines: Detecting and Correcting Errors in Vision Models...
Edge AI and Vision Alliance
 
Go Concurrency Real-World Patterns, Pitfalls, and Playground Battles.pdf
Emily Achieng
 

Designing and Building a Graph Database Application – Architectural Choices, Data Modeling, and Testing - Ian Robinson @ GraphConnect NY 2013

  • 1. Designing  and  Building  a  Graph   Database  Applica5on   Twi9er:  @ianSrobinson   #neo4j    
  • 2. Outline   •  Data  modeling  with  graphs   •  Neo4j  applica5on  architecture  op5ons   •  Tes5ng  your  data  model    
  • 5. Models   Purposeful  abstrac5on  of  a  domain  designed  to   sa5sfy  par5cular  applica5on/end-­‐user  goals   Images:  en.wikipedia.org  
  • 6. Example  Applica5on   •  Knowledge  management   –  People,  companies,  skills   –  Cross  organiza5onal   •  Find  my  professional  social  network   –  Exchange  knowledge   –  Interest  groups   –  Help   –  Staff  projects  
  • 7. Applica5on/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  
  • 8. Ques5ons  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?  
  • 9. Iden5fy  En55es   Which  people,  who  work  for  the  same  company   as  me,  have  similar  skills  to  me?     Person   Company   Skill  
  • 10. Iden5fy  Rela5onships  Between  En55es   Which  people,  who  work  for  the  same  company   as  me,  have  similar  skills  to  me?     Person  WORKS_FOR  Company   Person  HAS_SKILL  Skill  
  • 11. Convert  to  Cypher  Paths   Rela5onship   Person  WORKS_FOR  Company   Person  HAS_SKILL  Skill   Label   (:Person)-[:WORKS_FOR]->(:Company), (:Person)-[:HAS_SKILL]->(:Skill)
  • 13. Candidate  Data  Model   (:Company)<-[:WORKS_FOR]-(:Person)-[:HAS_SKILL]->(:Skill)
  • 14. Express  Ques5on  as  Graph  Pa9ern   Which  people,  who  work  for  the  same  company   as  me,  have  similar  skills  to  me?  
  • 15. 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
  • 16. Graph  Pa9ern   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
  • 17. Anchor  Pa9ern  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  
  • 18. Create  Projec5on  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
  • 22. Running  the  Query   +-----------------------------------+ | name | score | skills | +-----------------------------------+ | "Lucy" | 2 | ["Java","Neo4j"] | | "Bill" | 1 | ["Neo4j"] | +-----------------------------------+ 2 rows
  • 23. 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)
  • 25. Why  Test?   •  Ensure  model  is  fit  for  queries   –  Rapid  feedback   •  Ensure  correctness  of  queries   •  Document  your  understanding  of  your  domain   –  Including  corner  cases  and  excep5ons   •  Provide  a  regression  test  suite   –  Allows  you  to  change  and  evolve  model  and   queries  
  • 26. 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   applica5on  architecture…  
  • 27. Applica5on  Architectures   •  Embedded   •  Server   •  Server  with  Extensions  
  • 28. Applica5on  Architectures   •  Embedded   –  Host  in  Java  process   –  Access  to  Java  APIs   •  Server   •  Server  with  Extensions   Applica5on   Java  APIs  
  • 29. Applica5on  Architectures   •  Embedded   •  Server   –  HTTP/JSON  interface   –  Server  wraps  embedded   instance   •  Server  with  Extensions   Applica5on   REST  Client   Write  LB   REST  API   REST  API   Read  LB   REST  API  
  • 30. Applica5on  Architectures   •  Embedded   •  Server   •  Server  with  Extensions   REST  API   –  Execute  complex  logic  on  server   –  Control  HTTP  request/response  format   Extensions  
  • 31. Embedded  Example   •  Company  social  network   •  Find  colleagues  with  similar  skills   •  Encapsulate  query  in  a  ColleagueFinder
  • 32. 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(); } }
  • 33. 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(); } }
  • 34. 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(); } }
  • 35. 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     Execu5onEngine  
  • 36. ImpermanentGraphDatabase   •  In-­‐memory   •  For  tes5ng  only,  not  produc5on!   <dependency> <groupId>org.neo4j</groupId> <artifactId>neo4j-kernel</artifactId> <version>${project.version}</version> <type>test-jar</type> <scope>test</scope> </dependency>
  • 37. 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 ); }
  • 38. 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() ); }
  • 39. 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() ); }
  • 40. 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() ); }
  • 41. 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() ); }
  • 42. ColleagueFinder   public class ColleagueFinder { private final ExecutionEngine executionEngine; public ColleagueFinder( ExecutionEngine executionEngine ) { this.executionEngine = executionEngine; } public Iterator<Map<String, Object>> findColleaguesFor( String name ) { ... } }
  • 43. Inject  Execu5onEngine   public class ColleagueFinder { private final ExecutionEngine executionEngine; public ColleagueFinder( ExecutionEngine executionEngine ) { this.executionEngine = executionEngine; } public Iterator<Map<String, Object>> findColleaguesFor( String name ) { ... } }
  • 44. 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(); }
  • 45. 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(); }
  • 46. 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(); }
  • 47. 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(); }
  • 48. Server  Extension  Example   •  Same  data  mode  and  query  as  before   •  This  5me,  we’ll  host  ColleagueFinder  in  a   server  extension  
  • 49. 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(); } }
  • 50. JAX-­‐RS  Annota5ons   @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(); } }
  • 51. 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  
  • 52. CypherExecutor  Injected  by  Server   Ensures   Execu5onEngine   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(); } }
  • 53. 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(); } }
  • 54. 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(); } }
  • 55. 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(); } }
  • 56. 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(); } }
  • 57. 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(); } }
  • 58. CommunityServerBuilder   •  Programma5c  configura5on   <dependency> <groupId>org.neo4j.app</groupId> <artifactId>neo4j-server</artifactId> <version>${project.version}</version> <type>test-jar</type> </dependency>
  • 59. Tes5ng  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 ...
  • 60. 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 ...
  • 61. 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 ...
  • 62. 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 ...
  • 63. 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" ) ); }
  • 64. ts gy en lo i m no pl h m ec Co eo T N of Graph h Databases Thank  you   Twi9er:  @ianSrobinson   #neo4j         Ian Robinson, Jim Webber & Emil Eifrem github.com/iansrobinson/neo4j-­‐good-­‐prac5ces