64
Designing and Building a Graph Database Applica5on Twi9er: @ianSrobinson #neo4j

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

Embed Size (px)

DESCRIPTION

Ian closely looks at design and implementation strategies you can employ when building a Neo4j-based graph database solution, including architectural choices, data modelling, and testing.g

Citation preview

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

Designing  and  Building  a  Graph  Database  Applica5on  

Twi9er:  @ianSrobinson  #neo4j  

 

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

Outline  

•  Data  modeling  with  graphs  •  Neo4j  applica5on  architecture  op5ons  •  Tes5ng  your  data  model    

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

Graph  data  modeling  

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

Labeled  Property  Graph  

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

Models  

Images:  en.wikipedia.org  

Purposeful  abstrac5on  of  a  domain  designed  to  sa5sfy  par5cular  applica5on/end-­‐user  goals  

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

Example  Applica5on  

•  Knowledge  management  – People,  companies,  skills  – Cross  organiza5onal  

•  Find  my  professional  social  network  – Exchange  knowledge  –  Interest  groups  – Help  – Staff  projects  

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

Applica5on/End-­‐User  Goals  

As  an  employee    

I  want  to  know  who  in  the  company  has  similar  skills  to  me    

So  that  we  can  exchange  knowledge  

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

Ques5ons  To  Ask  of  the  Domain  

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

As  an  employee    I  want  to  know  who  in  the  company  has  similar  skills  to  me    So  that  we  can  exchange  knowledge  

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

Iden5fy  En55es  

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

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

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  

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

Convert  to  Cypher  Paths  

Person  WORKS_FOR  Company  Person  HAS_SKILL  Skill  

Rela5onship  

Label  

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

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

Consolidate  Paths  

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

(:Company)<-[:WORKS_FOR]-(:Person)-[:HAS_SKILL]->(:Skill)

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

Candidate  Data  Model  

(:Company)<-[:WORKS_FOR]-(:Person)-[:HAS_SKILL]->(:Skill)

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

Express  Ques5on  as  Graph  Pa9ern  

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

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

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 skillsORDER BY score DESC

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

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 skillsORDER BY score DESC

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

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 skillsORDER BY score DESC

Anchor  Pa9ern  in  Graph  

Search  nodes  labeled  ‘Person’,  matching  on  

‘name’  property  

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

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 skillsORDER BY score DESC

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

First  Match  

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

Second  Match  

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

Third  Match  

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

Running  the  Query  

+-----------------------------------+| name | score | skills |+-----------------------------------+| "Lucy" | 2 | ["Java","Neo4j"] || "Bill" | 1 | ["Neo4j"] |+-----------------------------------+2 rows

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

From  User  Story  to  Model  and  Query  

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 skillsORDER BY score DESC

As  an  employee    I  want  to  know  who  in  the  company  has  similar  skills  to  me    So  that  we  can  exchange  knowledge  

(:Company)<-[:WORKS_FOR]-(:Person)-[:HAS_SKILL]->(:Skill)

Person  WORKS_FOR  Company  Person  HAS_SKILL  Skill

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

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

Tes5ng  

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

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  

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

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…  

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

Applica5on  Architectures  

•  Embedded  •  Server  •  Server  with  Extensions  

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

Applica5on  Architectures  

•  Embedded  – Host  in  Java  process  – Access  to  Java  APIs  

•  Server  •  Server  with  Extensions  

Java  APIs  

Applica5on  

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

Applica5on  Architectures  

•  Embedded  •  Server  – HTTP/JSON  interface  – Server  wraps  embedded  instance  

•  Server  with  Extensions  

REST  API  REST  API  REST  API  

REST  Client  

Applica5on  

Write  LB   Read  LB  

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

Applica5on  Architectures  

•  Embedded  •  Server  •  Server  with  Extensions  – Execute  complex  logic  on  server  – Control  HTTP  request/response  format  

REST  API   Extensions  

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

Embedded  Example  

•  Company  social  network  •  Find  colleagues  with  similar  skills  •  Encapsulate  query  in  a  ColleagueFinder

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

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(); }}

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

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(); }}

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

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(); }}

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

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  

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

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>

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

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 );}

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

Unit  Test  

@Testpublic 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() );}

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

Execute  Object  Under  Test  

@Testpublic 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() );}

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

Assert  Results  

@Testpublic 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() );}

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

Ensure  No  More  Results  

@Testpublic 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() );}

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

ColleagueFinder  

public class ColleagueFinder { private final ExecutionEngine executionEngine; public ColleagueFinder( ExecutionEngine executionEngine ) { this.executionEngine = executionEngine; } public Iterator<Map<String, Object>> findColleaguesFor( String name ) { ... }}

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

Inject  Execu5onEngine  

public class ColleagueFinder { private final ExecutionEngine executionEngine; public ColleagueFinder( ExecutionEngine executionEngine ) { this.executionEngine = executionEngine; } public Iterator<Map<String, Object>> findColleaguesFor( String name ) { ... }}

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

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 skills\n" + "ORDER BY score DESC"; Map<String, Object> params = new HashMap<String, Object>(); params.put( "name", name ); return executionEngine.execute( cypher, params ).iterator();}

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

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 skills\n" + "ORDER BY score DESC"; Map<String, Object> params = new HashMap<String, Object>(); params.put( "name", name ); return executionEngine.execute( cypher, params ).iterator();}

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

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 skills\n" + "ORDER BY score DESC"; Map<String, Object> params = new HashMap<String, Object>(); params.put( "name", name ); return executionEngine.execute( cypher, params ).iterator();}

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

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 skills\n" + "ORDER BY score DESC"; Map<String, Object> params = new HashMap<String, Object>(); params.put( "name", name ); return executionEngine.execute( cypher, params ).iterator();}

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

Server  Extension  Example  

•  Same  data  mode  and  query  as  before  •  This  5me,  we’ll  host  ColleagueFinder  in  a  server  extension  

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

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(); }}

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

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(); }}

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

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  

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

CypherExecutor  Injected  by  Server  

@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(); }}

Ensures  Execu5onEngine  reused  across  resource  instances  

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

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(); }}

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

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(); }}

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

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(); }}

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

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(); }}

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

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(); }}

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

CommunityServerBuilder  

•  Programma5c  configura5on   <dependency> <groupId>org.neo4j.app</groupId> <artifactId>neo4j-server</artifactId> <version>${project.version}</version> <type>test-jar</type> </dependency>

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

Tes5ng  Extension  Using  HTTP  Client  @Testpublic 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 ...

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

Create  HTTP  Client  @Testpublic 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 ...

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

Issue  Request  @Testpublic 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 ...

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

Parse  Response  @Testpublic 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 ...

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

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" ) );}

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

Thank  you  

Twi9er:  @ianSrobinson  #neo4j  

                       

Ian Robinson, Jim Webber & Emil Eifrem

Graph Databases

h

Compliments

of Neo Technology

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