testdriven’datamodeling’with’...

75
#neo4j TestDriven Data Modeling With Graphs @ianSrobinson [email protected]

Upload: others

Post on 30-May-2020

4 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: TestDriven’DataModeling’With’ Graphsnosqlroadshow.com/dl/.../TestDrivenDataModeling_Ian_Robinson.pdf · #neo4j’ Create’sample’data public static void populate( GraphDatabaseService

#neo4j  

Test-­‐Driven  Data  Modeling  With  Graphs  

@ianSrobinson  [email protected]  

 

Page 2: TestDriven’DataModeling’With’ Graphsnosqlroadshow.com/dl/.../TestDrivenDataModeling_Ian_Robinson.pdf · #neo4j’ Create’sample’data public static void populate( GraphDatabaseService

#neo4j  

Outline  

•  Data  modeling  with  graphs  •  Neo4j  applicaDon  architecture  opDons  •  TesDng  your  data  model    

Page 3: TestDriven’DataModeling’With’ Graphsnosqlroadshow.com/dl/.../TestDrivenDataModeling_Ian_Robinson.pdf · #neo4j’ Create’sample’data public static void populate( GraphDatabaseService

#neo4j  

Graph  data  modeling  

Page 4: TestDriven’DataModeling’With’ Graphsnosqlroadshow.com/dl/.../TestDrivenDataModeling_Ian_Robinson.pdf · #neo4j’ Create’sample’data public static void populate( GraphDatabaseService

#neo4j  

Property  Graph  Data  Model  

Page 5: TestDriven’DataModeling’With’ Graphsnosqlroadshow.com/dl/.../TestDrivenDataModeling_Ian_Robinson.pdf · #neo4j’ Create’sample’data public static void populate( GraphDatabaseService

#neo4j  

Models  

Images:  en.wikipedia.org  

Page 6: TestDriven’DataModeling’With’ Graphsnosqlroadshow.com/dl/.../TestDrivenDataModeling_Ian_Robinson.pdf · #neo4j’ Create’sample’data public static void populate( GraphDatabaseService

#neo4j  

User  stories  

As  an  employee    

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

So  that  we  can  exchange  knowledge  

Page 7: TestDriven’DataModeling’With’ Graphsnosqlroadshow.com/dl/.../TestDrivenDataModeling_Ian_Robinson.pdf · #neo4j’ Create’sample’data public static void populate( GraphDatabaseService

#neo4j  

Derive  quesDons  

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 8: TestDriven’DataModeling’With’ Graphsnosqlroadshow.com/dl/.../TestDrivenDataModeling_Ian_Robinson.pdf · #neo4j’ Create’sample’data public static void populate( GraphDatabaseService

#neo4j  

IdenDfy  enDDes  

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

Page 9: TestDriven’DataModeling’With’ Graphsnosqlroadshow.com/dl/.../TestDrivenDataModeling_Ian_Robinson.pdf · #neo4j’ Create’sample’data public static void populate( GraphDatabaseService

#neo4j  

IdenDfy  relaDonships  between  enDDes  

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

Page 10: TestDriven’DataModeling’With’ Graphsnosqlroadshow.com/dl/.../TestDrivenDataModeling_Ian_Robinson.pdf · #neo4j’ Create’sample’data public static void populate( GraphDatabaseService

#neo4j  

Convert  to  Cypher  paths  

person  WORKS_FOR  company  person  HAS_SKILL  skill    (person)-[:WORKS_FOR]->(company),(person)-[:HAS_SKILL]->(skill)

Page 11: TestDriven’DataModeling’With’ Graphsnosqlroadshow.com/dl/.../TestDrivenDataModeling_Ian_Robinson.pdf · #neo4j’ Create’sample’data public static void populate( GraphDatabaseService

#neo4j  

Cypher  paths  (person)-[:WORKS_FOR]->(company),(person)-[:HAS_SKILL]->(skill)

(company)<-[:WORKS_FOR]-(person)-[:HAS_SKILL]->(skill)

Page 12: TestDriven’DataModeling’With’ Graphsnosqlroadshow.com/dl/.../TestDrivenDataModeling_Ian_Robinson.pdf · #neo4j’ Create’sample’data public static void populate( GraphDatabaseService

#neo4j  

Data  model  

(company)<-[:WORKS_FOR]-(person)-[:HAS_SKILL]->(skill)

Page 13: TestDriven’DataModeling’With’ Graphsnosqlroadshow.com/dl/.../TestDrivenDataModeling_Ian_Robinson.pdf · #neo4j’ Create’sample’data public static void populate( GraphDatabaseService

#neo4j  

FormulaDng  quesDon  as  graph  paVern  

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

Page 14: TestDriven’DataModeling’With’ Graphsnosqlroadshow.com/dl/.../TestDrivenDataModeling_Ian_Robinson.pdf · #neo4j’ Create’sample’data public static void populate( GraphDatabaseService

#neo4j  

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 15: TestDriven’DataModeling’With’ Graphsnosqlroadshow.com/dl/.../TestDrivenDataModeling_Ian_Robinson.pdf · #neo4j’ Create’sample’data public static void populate( GraphDatabaseService

#neo4j  

Graph  paVern  

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: TestDriven’DataModeling’With’ Graphsnosqlroadshow.com/dl/.../TestDrivenDataModeling_Ian_Robinson.pdf · #neo4j’ Create’sample’data public static void populate( GraphDatabaseService

#neo4j  

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

Page 17: TestDriven’DataModeling’With’ Graphsnosqlroadshow.com/dl/.../TestDrivenDataModeling_Ian_Robinson.pdf · #neo4j’ Create’sample’data public static void populate( GraphDatabaseService

#neo4j  

Create  projecDon  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 18: TestDriven’DataModeling’With’ Graphsnosqlroadshow.com/dl/.../TestDrivenDataModeling_Ian_Robinson.pdf · #neo4j’ Create’sample’data public static void populate( GraphDatabaseService

#neo4j  

First  match  

Page 19: TestDriven’DataModeling’With’ Graphsnosqlroadshow.com/dl/.../TestDrivenDataModeling_Ian_Robinson.pdf · #neo4j’ Create’sample’data public static void populate( GraphDatabaseService

#neo4j  

Second  match  

Page 20: TestDriven’DataModeling’With’ Graphsnosqlroadshow.com/dl/.../TestDrivenDataModeling_Ian_Robinson.pdf · #neo4j’ Create’sample’data public static void populate( GraphDatabaseService

#neo4j  

Third  match  

Page 21: TestDriven’DataModeling’With’ Graphsnosqlroadshow.com/dl/.../TestDrivenDataModeling_Ian_Robinson.pdf · #neo4j’ Create’sample’data public static void populate( GraphDatabaseService

#neo4j  

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

Page 22: TestDriven’DataModeling’With’ Graphsnosqlroadshow.com/dl/.../TestDrivenDataModeling_Ian_Robinson.pdf · #neo4j’ Create’sample’data public static void populate( GraphDatabaseService

#neo4j  

From  user  story  to  model  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 23: TestDriven’DataModeling’With’ Graphsnosqlroadshow.com/dl/.../TestDrivenDataModeling_Ian_Robinson.pdf · #neo4j’ Create’sample’data public static void populate( GraphDatabaseService

#neo4j  

Nodes  for  things  

Page 24: TestDriven’DataModeling’With’ Graphsnosqlroadshow.com/dl/.../TestDrivenDataModeling_Ian_Robinson.pdf · #neo4j’ Create’sample’data public static void populate( GraphDatabaseService

#neo4j  

Labels  for  grouping  

Page 25: TestDriven’DataModeling’With’ Graphsnosqlroadshow.com/dl/.../TestDrivenDataModeling_Ian_Robinson.pdf · #neo4j’ Create’sample’data public static void populate( GraphDatabaseService

#neo4j  

RelaDonships  for  structure  

Page 26: TestDriven’DataModeling’With’ Graphsnosqlroadshow.com/dl/.../TestDrivenDataModeling_Ian_Robinson.pdf · #neo4j’ Create’sample’data public static void populate( GraphDatabaseService

#neo4j  

Don’t  model  enDDes  as  relaDonships  

•  Limits  data  model  evoluDon  – Unable  to  associate  more  enDDes  

•  EnDDes  someDmes  hidden  in  a  verb  •  Smells:  – Lots  of  aVribute-­‐like  properDes  – Property  value  redundancy  – Heavy  use  of  relaDonship  indexes  

Page 27: TestDriven’DataModeling’With’ Graphsnosqlroadshow.com/dl/.../TestDrivenDataModeling_Ian_Robinson.pdf · #neo4j’ Create’sample’data public static void populate( GraphDatabaseService

#neo4j  

Example:  Reviews  

Page 28: TestDriven’DataModeling’With’ Graphsnosqlroadshow.com/dl/.../TestDrivenDataModeling_Ian_Robinson.pdf · #neo4j’ Create’sample’data public static void populate( GraphDatabaseService

#neo4j  

Add  another  review  

Page 29: TestDriven’DataModeling’With’ Graphsnosqlroadshow.com/dl/.../TestDrivenDataModeling_Ian_Robinson.pdf · #neo4j’ Create’sample’data public static void populate( GraphDatabaseService

#neo4j  

And  another  

Page 30: TestDriven’DataModeling’With’ Graphsnosqlroadshow.com/dl/.../TestDrivenDataModeling_Ian_Robinson.pdf · #neo4j’ Create’sample’data public static void populate( GraphDatabaseService

#neo4j  

Problems  •  Redundant  data    (2  x  amazon.co.uk)  

•  Difficult  to  find  reviews  for  source  

•  Users  can’t  comment  on  reviews  

Page 31: TestDriven’DataModeling’With’ Graphsnosqlroadshow.com/dl/.../TestDrivenDataModeling_Ian_Robinson.pdf · #neo4j’ Create’sample’data public static void populate( GraphDatabaseService

#neo4j  

Revised  model  

Page 32: TestDriven’DataModeling’With’ Graphsnosqlroadshow.com/dl/.../TestDrivenDataModeling_Ian_Robinson.pdf · #neo4j’ Create’sample’data public static void populate( GraphDatabaseService

#neo4j  

Model  acDons  in  terms  of  products  

Page 33: TestDriven’DataModeling’With’ Graphsnosqlroadshow.com/dl/.../TestDrivenDataModeling_Ian_Robinson.pdf · #neo4j’ Create’sample’data public static void populate( GraphDatabaseService

#neo4j  

ApplicaDon  architectures  

Page 34: TestDriven’DataModeling’With’ Graphsnosqlroadshow.com/dl/.../TestDrivenDataModeling_Ian_Robinson.pdf · #neo4j’ Create’sample’data public static void populate( GraphDatabaseService

#neo4j  

Embedded  

•  Host  in  Java  process  •  Access  to  Java  APIs  

Java  APIs  

ApplicaDon  

Page 35: TestDriven’DataModeling’With’ Graphsnosqlroadshow.com/dl/.../TestDrivenDataModeling_Ian_Robinson.pdf · #neo4j’ Create’sample’data public static void populate( GraphDatabaseService

#neo4j  

Server  

•  HTTP/JSON  interface  •  Server  wraps  embedded  instance  

REST  API  REST  API  REST  API  

REST  Client  

ApplicaDon  

Write  LB   Read  LB  

Page 36: TestDriven’DataModeling’With’ Graphsnosqlroadshow.com/dl/.../TestDrivenDataModeling_Ian_Robinson.pdf · #neo4j’ Create’sample’data public static void populate( GraphDatabaseService

#neo4j  

•  Encapsulate  complex  server-­‐side  logic  •  Control  HTTP  request/response  format  

Server  Extensions  

REST  API   Extensions  

Page 37: TestDriven’DataModeling’With’ Graphsnosqlroadshow.com/dl/.../TestDrivenDataModeling_Ian_Robinson.pdf · #neo4j’ Create’sample’data public static void populate( GraphDatabaseService

#neo4j  

TesDng  

Page 38: TestDriven’DataModeling’With’ Graphsnosqlroadshow.com/dl/.../TestDrivenDataModeling_Ian_Robinson.pdf · #neo4j’ Create’sample’data public static void populate( GraphDatabaseService

#neo4j  

Test-­‐driven  data  modeling  

•  Unit  test  with  small,  well-­‐known  datasets  –  Inject  small  graphs  to  test  individual  queries  – Datasets  express  understanding  of  domain  – Use  the  tests  to  idenDfy  regressions  as  your  data  model  evolves  

•  Performance  test  queries  against  representaDve  dataset  

Page 39: TestDriven’DataModeling’With’ Graphsnosqlroadshow.com/dl/.../TestDrivenDataModeling_Ian_Robinson.pdf · #neo4j’ Create’sample’data public static void populate( GraphDatabaseService

#neo4j  

Query  Dmes  proporDonal  to  size  of  subgraph  searched  

Page 40: TestDriven’DataModeling’With’ Graphsnosqlroadshow.com/dl/.../TestDrivenDataModeling_Ian_Robinson.pdf · #neo4j’ Create’sample’data public static void populate( GraphDatabaseService

#neo4j  

Query  Dmes  proporDonal  to  size  of  subgraph  searched  

Page 41: TestDriven’DataModeling’With’ Graphsnosqlroadshow.com/dl/.../TestDrivenDataModeling_Ian_Robinson.pdf · #neo4j’ Create’sample’data public static void populate( GraphDatabaseService

#neo4j  

Query  Dmes  proporDonal  to  size  of  subgraph  searched  

Page 42: TestDriven’DataModeling’With’ Graphsnosqlroadshow.com/dl/.../TestDrivenDataModeling_Ian_Robinson.pdf · #neo4j’ Create’sample’data public static void populate( GraphDatabaseService

#neo4j  

Query  Dmes  remain  constant  …  

Page 43: TestDriven’DataModeling’With’ Graphsnosqlroadshow.com/dl/.../TestDrivenDataModeling_Ian_Robinson.pdf · #neo4j’ Create’sample’data public static void populate( GraphDatabaseService

#neo4j  

…  unless  subgraph  seached  grows  

Page 44: TestDriven’DataModeling’With’ Graphsnosqlroadshow.com/dl/.../TestDrivenDataModeling_Ian_Robinson.pdf · #neo4j’ Create’sample’data public static void populate( GraphDatabaseService

#neo4j  

Unit  test  fixture  public class ColleagueFinderTest { private static GraphDatabaseService db; private static ColleagueFinder finder; @BeforeClass public static void init() { db = new TestGraphDatabaseFactory().newImpermanentDatabase(); ExampleGraph.populate( db ); finder = new ColleagueFinder( db ); } @AfterClass public static void shutdown() { db.shutdown(); }}

Page 45: TestDriven’DataModeling’With’ Graphsnosqlroadshow.com/dl/.../TestDrivenDataModeling_Ian_Robinson.pdf · #neo4j’ Create’sample’data public static void populate( GraphDatabaseService

#neo4j  

Create  database  public class ColleagueFinderTest { private static GraphDatabaseService db; private static ColleagueFinder finder; @BeforeClass public static void init() { db = new TestGraphDatabaseFactory().newImpermanentDatabase(); ExampleGraph.populate( db ); finder = new ColleagueFinder( db ); } @AfterClass public static void shutdown() { db.shutdown(); }}

Page 46: TestDriven’DataModeling’With’ Graphsnosqlroadshow.com/dl/.../TestDrivenDataModeling_Ian_Robinson.pdf · #neo4j’ Create’sample’data public static void populate( GraphDatabaseService

#neo4j  

Populate  graph  public class ColleagueFinderTest { private static GraphDatabaseService db; private static ColleagueFinder finder; @BeforeClass public static void init() { db = new TestGraphDatabaseFactory().newImpermanentDatabase(); ExampleGraph.populate( db ); finder = new ColleagueFinder( db ); } @AfterClass public static void shutdown() { db.shutdown(); }}

Page 47: TestDriven’DataModeling’With’ Graphsnosqlroadshow.com/dl/.../TestDrivenDataModeling_Ian_Robinson.pdf · #neo4j’ Create’sample’data public static void populate( GraphDatabaseService

#neo4j  

Create  object  under  test  public class ColleagueFinderTest { private static GraphDatabaseService db; private static ColleagueFinder finder; @BeforeClass public static void init() { db = new TestGraphDatabaseFactory().newImpermanentDatabase(); ExampleGraph.populate( db ); finder = new ColleagueFinder( db ); } @AfterClass public static void shutdown() { db.shutdown(); }}

Inject  database  

Page 48: TestDriven’DataModeling’With’ Graphsnosqlroadshow.com/dl/.../TestDrivenDataModeling_Ian_Robinson.pdf · #neo4j’ Create’sample’data public static void populate( GraphDatabaseService

#neo4j  

ImpermanentGraphDatabase  •  In-­‐memory  •  For  tesDng  only   <dependency> <groupId>org.neo4j</groupId> <artifactId>neo4j-kernel</artifactId> <version>${project.version}</version> <type>test-jar</type> <scope>test</scope> </dependency>

Page 49: TestDriven’DataModeling’With’ Graphsnosqlroadshow.com/dl/.../TestDrivenDataModeling_Ian_Robinson.pdf · #neo4j’ Create’sample’data public static void populate( GraphDatabaseService

#neo4j  

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 50: TestDriven’DataModeling’With’ Graphsnosqlroadshow.com/dl/.../TestDrivenDataModeling_Ian_Robinson.pdf · #neo4j’ Create’sample’data public static void populate( GraphDatabaseService

#neo4j  

Unit  test  @Testpublic void shouldFindColleaguesWithSimilarSkills() throws Exception { // when Iterator<Map<String, Object>> results = finder.findFor( "Ian" ); // then assertEquals( "Lucy", results.next().get( "name" ) ); assertEquals( "Bill", results.next().get( "name" ) ); assertFalse( results.hasNext() );}

Page 51: TestDriven’DataModeling’With’ Graphsnosqlroadshow.com/dl/.../TestDrivenDataModeling_Ian_Robinson.pdf · #neo4j’ Create’sample’data public static void populate( GraphDatabaseService

#neo4j  

Execute  unit  under  test  @Testpublic void shouldFindColleaguesWithSimilarSkills() throws Exception { // when Iterator<Map<String, Object>> results = finder.findFor( "Ian" ); // then assertEquals( "Lucy", results.next().get( "name" ) ); assertEquals( "Bill", results.next().get( "name" ) ); assertFalse( results.hasNext() );}

Page 52: TestDriven’DataModeling’With’ Graphsnosqlroadshow.com/dl/.../TestDrivenDataModeling_Ian_Robinson.pdf · #neo4j’ Create’sample’data public static void populate( GraphDatabaseService

#neo4j  

Assert  results  @Testpublic void shouldFindColleaguesWithSimilarSkills() throws Exception { // when Iterator<Map<String, Object>> results = finder.findFor( "Ian" ); // then assertEquals( "Lucy", results.next().get( "name" ) ); assertEquals( "Bill", results.next().get( "name" ) ); assertFalse( results.hasNext() );}

Page 53: TestDriven’DataModeling’With’ Graphsnosqlroadshow.com/dl/.../TestDrivenDataModeling_Ian_Robinson.pdf · #neo4j’ Create’sample’data public static void populate( GraphDatabaseService

#neo4j  

Ensure  no  more  results  @Testpublic void shouldFindColleaguesWithSimilarSkills() throws Exception { // when Iterator<Map<String, Object>> results = finder.findFor( "Ian" ); // then assertEquals( "Lucy", results.next().get( "name" ) ); assertEquals( "Bill", results.next().get( "name" ) ); assertFalse( results.hasNext() );}

Page 54: TestDriven’DataModeling’With’ Graphsnosqlroadshow.com/dl/.../TestDrivenDataModeling_Ian_Robinson.pdf · #neo4j’ Create’sample’data public static void populate( GraphDatabaseService

#neo4j  

Object  under  test  public class ColleagueFinder { private final ExecutionEngine cypherEngine; public ColleagueFinder( GraphDatabaseService db ) { this.cypherEngine = new ExecutionEngine( db ); } public Iterator<Map<String, Object>> findFor( String name ) { ... }}

Page 55: TestDriven’DataModeling’With’ Graphsnosqlroadshow.com/dl/.../TestDrivenDataModeling_Ian_Robinson.pdf · #neo4j’ Create’sample’data public static void populate( GraphDatabaseService

#neo4j  

Inject  database  public class ColleagueFinder { private final ExecutionEngine cypherEngine; public ColleagueFinder( GraphDatabaseService db ) { this.cypherEngine = new ExecutionEngine( db ); } public Iterator<Map<String, Object>> findFor( String name ) { ... }}

Page 56: TestDriven’DataModeling’With’ Graphsnosqlroadshow.com/dl/.../TestDrivenDataModeling_Ian_Robinson.pdf · #neo4j’ Create’sample’data public static void populate( GraphDatabaseService

#neo4j  

findFor()  method  public Iterator<Map<String, Object>> findFor( 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 cypherEngine.execute( cypher, params ).iterator();}

Page 57: TestDriven’DataModeling’With’ Graphsnosqlroadshow.com/dl/.../TestDrivenDataModeling_Ian_Robinson.pdf · #neo4j’ Create’sample’data public static void populate( GraphDatabaseService

#neo4j  

Cypher  query  public Iterator<Map<String, Object>> findFor( 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 cypherEngine.execute( cypher, params ).iterator();}

Page 58: TestDriven’DataModeling’With’ Graphsnosqlroadshow.com/dl/.../TestDrivenDataModeling_Ian_Robinson.pdf · #neo4j’ Create’sample’data public static void populate( GraphDatabaseService

#neo4j  

Parameterized  query  public Iterator<Map<String, Object>> findFor( 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 cypherEngine.execute( cypher, params ).iterator();}

Page 59: TestDriven’DataModeling’With’ Graphsnosqlroadshow.com/dl/.../TestDrivenDataModeling_Ian_Robinson.pdf · #neo4j’ Create’sample’data public static void populate( GraphDatabaseService

#neo4j  

Execute  query  public Iterator<Map<String, Object>> findFor( 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 cypherEngine.execute( cypher, params ).iterator();}

Page 60: TestDriven’DataModeling’With’ Graphsnosqlroadshow.com/dl/.../TestDrivenDataModeling_Ian_Robinson.pdf · #neo4j’ Create’sample’data public static void populate( GraphDatabaseService

#neo4j  

Unmanaged  extension  @Path("/similar-skills")public class ColleagueFinderExtension { private static final ObjectMapper MAPPER = new ObjectMapper(); private final ColleagueFinder colleagueFinder; public ColleagueFinderExtension( @Context GraphDatabaseService db ) { this.colleagueFinder = new ColleagueFinder( db ); } @GET @Produces(MediaType.APPLICATION_JSON) @Path("/{name}") public Response getColleagues( @PathParam("name") String name ) throws IOException { String json = MAPPER .writeValueAsString( colleagueFinder.findFor( name ) ); return Response.ok().entity( json ).build(); }}

Page 61: TestDriven’DataModeling’With’ Graphsnosqlroadshow.com/dl/.../TestDrivenDataModeling_Ian_Robinson.pdf · #neo4j’ Create’sample’data public static void populate( GraphDatabaseService

#neo4j  

JAX-­‐RS  annotaDons  @Path("/similar-skills")public class ColleagueFinderExtension { private static final ObjectMapper MAPPER = new ObjectMapper(); private final ColleagueFinder colleagueFinder; public ColleagueFinderExtension( @Context GraphDatabaseService db ) { this.colleagueFinder = new ColleagueFinder( db ); } @GET @Produces(MediaType.APPLICATION_JSON) @Path("/{name}") public Response getColleagues( @PathParam("name") String name ) throws IOException { String json = MAPPER .writeValueAsString( colleagueFinder.findFor( name ) ); return Response.ok().entity( json ).build(); }}

Page 62: TestDriven’DataModeling’With’ Graphsnosqlroadshow.com/dl/.../TestDrivenDataModeling_Ian_Robinson.pdf · #neo4j’ Create’sample’data public static void populate( GraphDatabaseService

#neo4j  

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 GraphDatabaseService db ) { this.colleagueFinder = new ColleagueFinder( db ); } @GET @Produces(MediaType.APPLICATION_JSON) @Path("/{name}") public Response getColleagues( @PathParam("name") String name ) throws IOException { String json = MAPPER .writeValueAsString( colleagueFinder.findFor( name ) ); return Response.ok().entity( json ).build(); }}

GET   /similar-­‐skills   /Sue  

Page 63: TestDriven’DataModeling’With’ Graphsnosqlroadshow.com/dl/.../TestDrivenDataModeling_Ian_Robinson.pdf · #neo4j’ Create’sample’data public static void populate( GraphDatabaseService

#neo4j  

Database  injected  by  server  @Path("/similar-skills")public class ColleagueFinderExtension { private static final ObjectMapper MAPPER = new ObjectMapper(); private final ColleagueFinder colleagueFinder; public ColleagueFinderExtension( @Context GraphDatabaseService db ) { this.colleagueFinder = new ColleagueFinder( db ); } @GET @Produces(MediaType.APPLICATION_JSON) @Path("/{name}") public Response getColleagues( @PathParam("name") String name ) throws IOException { String json = MAPPER .writeValueAsString( colleagueFinder.findFor( name ) ); return Response.ok().entity( json ).build(); }}

Page 64: TestDriven’DataModeling’With’ Graphsnosqlroadshow.com/dl/.../TestDrivenDataModeling_Ian_Robinson.pdf · #neo4j’ Create’sample’data public static void populate( GraphDatabaseService

#neo4j  

Generate  and  format  response  @Path("/similar-skills")public class ColleagueFinderExtension { private static final ObjectMapper MAPPER = new ObjectMapper(); private final ColleagueFinder colleagueFinder; public ColleagueFinderExtension( @Context GraphDatabaseService db ) { this.colleagueFinder = new ColleagueFinder( db ); } @GET @Produces(MediaType.APPLICATION_JSON) @Path("/{name}") public Response getColleagues( @PathParam("name") String name ) throws IOException { String json = MAPPER .writeValueAsString( colleagueFinder.findFor( name ) ); return Response.ok().entity( json ).build(); }}

Page 65: TestDriven’DataModeling’With’ Graphsnosqlroadshow.com/dl/.../TestDrivenDataModeling_Ian_Robinson.pdf · #neo4j’ Create’sample’data public static void populate( GraphDatabaseService

#neo4j  

Extension  test  fixture  public class ColleagueFinderExtensionTest { private static CommunityNeoServer server; @BeforeClass public static void startServer() throws IOException { server = ServerBuilder.server() .withThirdPartyJaxRsPackage( "org.neo4j.good_practices", "/colleagues" ) .build(); server.start(); ExampleGraph.populate( server.getDatabase().getGraph() ); } @AfterClass public static void stopServer() { server.stop(); }}

Page 66: TestDriven’DataModeling’With’ Graphsnosqlroadshow.com/dl/.../TestDrivenDataModeling_Ian_Robinson.pdf · #neo4j’ Create’sample’data public static void populate( GraphDatabaseService

#neo4j  

Build  and  configure  server  public class ColleagueFinderExtensionTest { private static CommunityNeoServer server; @BeforeClass public static void startServer() throws IOException { server = ServerBuilder.server() .withThirdPartyJaxRsPackage( "org.neo4j.good_practices", "/colleagues" ) .build(); server.start(); ExampleGraph.populate( server.getDatabase().getGraph() ); } @AfterClass public static void stopServer() { server.stop(); }}

Page 67: TestDriven’DataModeling’With’ Graphsnosqlroadshow.com/dl/.../TestDrivenDataModeling_Ian_Robinson.pdf · #neo4j’ Create’sample’data public static void populate( GraphDatabaseService

#neo4j  

Start  server  public class ColleagueFinderExtensionTest { private static CommunityNeoServer server; @BeforeClass public static void startServer() throws IOException { server = ServerBuilder.server() .withThirdPartyJaxRsPackage( "org.neo4j.good_practices", "/colleagues" ) .build(); server.start(); ExampleGraph.populate( server.getDatabase().getGraph() ); } @AfterClass public static void stopServer() { server.stop(); }}

Page 68: TestDriven’DataModeling’With’ Graphsnosqlroadshow.com/dl/.../TestDrivenDataModeling_Ian_Robinson.pdf · #neo4j’ Create’sample’data public static void populate( GraphDatabaseService

#neo4j  

Populate  database  public class ColleagueFinderExtensionTest { private static CommunityNeoServer server; @BeforeClass public static void startServer() throws IOException { server = ServerBuilder.server() .withThirdPartyJaxRsPackage( "org.neo4j.good_practices", "/colleagues" ) .build(); server.start(); ExampleGraph.populate( server.getDatabase().getGraph() ); } @AfterClass public static void stopServer() { server.stop(); }}

Page 69: TestDriven’DataModeling’With’ Graphsnosqlroadshow.com/dl/.../TestDrivenDataModeling_Ian_Robinson.pdf · #neo4j’ Create’sample’data public static void populate( GraphDatabaseService

#neo4j  

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

Page 70: TestDriven’DataModeling’With’ Graphsnosqlroadshow.com/dl/.../TestDrivenDataModeling_Ian_Robinson.pdf · #neo4j’ Create’sample’data public static void populate( GraphDatabaseService

#neo4j  

TesDng  extensions  @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 71: TestDriven’DataModeling’With’ Graphsnosqlroadshow.com/dl/.../TestDrivenDataModeling_Ian_Robinson.pdf · #neo4j’ Create’sample’data public static void populate( GraphDatabaseService

#neo4j  

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 72: TestDriven’DataModeling’With’ Graphsnosqlroadshow.com/dl/.../TestDrivenDataModeling_Ian_Robinson.pdf · #neo4j’ Create’sample’data public static void populate( GraphDatabaseService

#neo4j  

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 73: TestDriven’DataModeling’With’ Graphsnosqlroadshow.com/dl/.../TestDrivenDataModeling_Ian_Robinson.pdf · #neo4j’ Create’sample’data public static void populate( GraphDatabaseService

#neo4j  

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 74: TestDriven’DataModeling’With’ Graphsnosqlroadshow.com/dl/.../TestDrivenDataModeling_Ian_Robinson.pdf · #neo4j’ Create’sample’data public static void populate( GraphDatabaseService

#neo4j  

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 75: TestDriven’DataModeling’With’ Graphsnosqlroadshow.com/dl/.../TestDrivenDataModeling_Ian_Robinson.pdf · #neo4j’ Create’sample’data public static void populate( GraphDatabaseService

#neo4j  hVp://graphdatabases.com  

Ian Robinson, Jim Webber & Emil Eifrem

Graph Databases

h

Compliments

of Neo Technology

Thank  you  @ianSrobinson  

[email protected]  github.com/iansrobinson