+ All Categories
Home > Documents > TestDriven+DataModeling+With+Graphsnosqlroadshow.com/dl/NoSQL-Amsterdam-2013/Slides/...Outline+ •...

TestDriven+DataModeling+With+Graphsnosqlroadshow.com/dl/NoSQL-Amsterdam-2013/Slides/...Outline+ •...

Date post: 06-Aug-2020
Category:
Upload: others
View: 0 times
Download: 0 times
Share this document with a friend
64
TestDriven Data Modeling With Graphs Twi7er: @ianSrobinson #neo4j
Transcript
Page 1: TestDriven+DataModeling+With+Graphsnosqlroadshow.com/dl/NoSQL-Amsterdam-2013/Slides/...Outline+ • Datamodeling+with+graphs+ • Neo4j+applicaon+architecture+opDons+ • TesDng+your+datamodel++

Test-­‐Driven  Data  Modeling  With  Graphs  

Twi7er:  @ianSrobinson  #neo4j  

 

Page 2: TestDriven+DataModeling+With+Graphsnosqlroadshow.com/dl/NoSQL-Amsterdam-2013/Slides/...Outline+ • Datamodeling+with+graphs+ • Neo4j+applicaon+architecture+opDons+ • TesDng+your+datamodel++

Outline  

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

Page 3: TestDriven+DataModeling+With+Graphsnosqlroadshow.com/dl/NoSQL-Amsterdam-2013/Slides/...Outline+ • Datamodeling+with+graphs+ • Neo4j+applicaon+architecture+opDons+ • TesDng+your+datamodel++

Graph  data  modeling  

Page 4: TestDriven+DataModeling+With+Graphsnosqlroadshow.com/dl/NoSQL-Amsterdam-2013/Slides/...Outline+ • Datamodeling+with+graphs+ • Neo4j+applicaon+architecture+opDons+ • TesDng+your+datamodel++

Labeled  Property  Graph  

Page 5: TestDriven+DataModeling+With+Graphsnosqlroadshow.com/dl/NoSQL-Amsterdam-2013/Slides/...Outline+ • Datamodeling+with+graphs+ • Neo4j+applicaon+architecture+opDons+ • TesDng+your+datamodel++

Models  

Images:  en.wikipedia.org  

Purposeful  abstracDon  of  a  domain  designed  to  saDsfy  parDcular  applicaDon/end-­‐user  goals  

Page 6: TestDriven+DataModeling+With+Graphsnosqlroadshow.com/dl/NoSQL-Amsterdam-2013/Slides/...Outline+ • Datamodeling+with+graphs+ • Neo4j+applicaon+architecture+opDons+ • TesDng+your+datamodel++

Example  ApplicaDon  

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

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

Page 7: TestDriven+DataModeling+With+Graphsnosqlroadshow.com/dl/NoSQL-Amsterdam-2013/Slides/...Outline+ • Datamodeling+with+graphs+ • Neo4j+applicaon+architecture+opDons+ • TesDng+your+datamodel++

ApplicaDon/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: TestDriven+DataModeling+With+Graphsnosqlroadshow.com/dl/NoSQL-Amsterdam-2013/Slides/...Outline+ • Datamodeling+with+graphs+ • Neo4j+applicaon+architecture+opDons+ • TesDng+your+datamodel++

QuesDons  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: TestDriven+DataModeling+With+Graphsnosqlroadshow.com/dl/NoSQL-Amsterdam-2013/Slides/...Outline+ • Datamodeling+with+graphs+ • Neo4j+applicaon+architecture+opDons+ • TesDng+your+datamodel++

IdenDfy  EnDDes  

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

Page 10: TestDriven+DataModeling+With+Graphsnosqlroadshow.com/dl/NoSQL-Amsterdam-2013/Slides/...Outline+ • Datamodeling+with+graphs+ • Neo4j+applicaon+architecture+opDons+ • TesDng+your+datamodel++

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 11: TestDriven+DataModeling+With+Graphsnosqlroadshow.com/dl/NoSQL-Amsterdam-2013/Slides/...Outline+ • Datamodeling+with+graphs+ • Neo4j+applicaon+architecture+opDons+ • TesDng+your+datamodel++

Convert  to  Cypher  Paths  

Person  WORKS_FOR  Company  Person  HAS_SKILL  Skill  

RelaDonship  

Label  

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

Page 12: TestDriven+DataModeling+With+Graphsnosqlroadshow.com/dl/NoSQL-Amsterdam-2013/Slides/...Outline+ • Datamodeling+with+graphs+ • Neo4j+applicaon+architecture+opDons+ • TesDng+your+datamodel++

Consolidate  Paths  

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

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

Page 13: TestDriven+DataModeling+With+Graphsnosqlroadshow.com/dl/NoSQL-Amsterdam-2013/Slides/...Outline+ • Datamodeling+with+graphs+ • Neo4j+applicaon+architecture+opDons+ • TesDng+your+datamodel++

Candidate  Data  Model  

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

Page 14: TestDriven+DataModeling+With+Graphsnosqlroadshow.com/dl/NoSQL-Amsterdam-2013/Slides/...Outline+ • Datamodeling+with+graphs+ • Neo4j+applicaon+architecture+opDons+ • TesDng+your+datamodel++

Express  QuesDon  as  Graph  Pa7ern  

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

Page 15: TestDriven+DataModeling+With+Graphsnosqlroadshow.com/dl/NoSQL-Amsterdam-2013/Slides/...Outline+ • Datamodeling+with+graphs+ • Neo4j+applicaon+architecture+opDons+ • TesDng+your+datamodel++

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: TestDriven+DataModeling+With+Graphsnosqlroadshow.com/dl/NoSQL-Amsterdam-2013/Slides/...Outline+ • Datamodeling+with+graphs+ • Neo4j+applicaon+architecture+opDons+ • TesDng+your+datamodel++

Graph  Pa7ern  

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/NoSQL-Amsterdam-2013/Slides/...Outline+ • Datamodeling+with+graphs+ • Neo4j+applicaon+architecture+opDons+ • TesDng+your+datamodel++

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  Pa7ern  in  Graph  

Search  nodes  labeled  ‘Person’,  matching  on  

‘name’  property  

Page 18: TestDriven+DataModeling+With+Graphsnosqlroadshow.com/dl/NoSQL-Amsterdam-2013/Slides/...Outline+ • Datamodeling+with+graphs+ • Neo4j+applicaon+architecture+opDons+ • TesDng+your+datamodel++

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 19: TestDriven+DataModeling+With+Graphsnosqlroadshow.com/dl/NoSQL-Amsterdam-2013/Slides/...Outline+ • Datamodeling+with+graphs+ • Neo4j+applicaon+architecture+opDons+ • TesDng+your+datamodel++

First  Match  

Page 20: TestDriven+DataModeling+With+Graphsnosqlroadshow.com/dl/NoSQL-Amsterdam-2013/Slides/...Outline+ • Datamodeling+with+graphs+ • Neo4j+applicaon+architecture+opDons+ • TesDng+your+datamodel++

Second  Match  

Page 21: TestDriven+DataModeling+With+Graphsnosqlroadshow.com/dl/NoSQL-Amsterdam-2013/Slides/...Outline+ • Datamodeling+with+graphs+ • Neo4j+applicaon+architecture+opDons+ • TesDng+your+datamodel++

Third  Match  

Page 22: TestDriven+DataModeling+With+Graphsnosqlroadshow.com/dl/NoSQL-Amsterdam-2013/Slides/...Outline+ • Datamodeling+with+graphs+ • Neo4j+applicaon+architecture+opDons+ • TesDng+your+datamodel++

Running  the  Query  

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

Page 23: TestDriven+DataModeling+With+Graphsnosqlroadshow.com/dl/NoSQL-Amsterdam-2013/Slides/...Outline+ • Datamodeling+with+graphs+ • Neo4j+applicaon+architecture+opDons+ • TesDng+your+datamodel++

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: TestDriven+DataModeling+With+Graphsnosqlroadshow.com/dl/NoSQL-Amsterdam-2013/Slides/...Outline+ • Datamodeling+with+graphs+ • Neo4j+applicaon+architecture+opDons+ • TesDng+your+datamodel++

TesDng  

Page 25: TestDriven+DataModeling+With+Graphsnosqlroadshow.com/dl/NoSQL-Amsterdam-2013/Slides/...Outline+ • Datamodeling+with+graphs+ • Neo4j+applicaon+architecture+opDons+ • TesDng+your+datamodel++

Why  Test?  

•  Ensure  model  is  fit  for  queries  – Rapid  feedback  

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

•  Provide  a  regression  test  suite  – Allows  you  to  change  and  evolve  model  and  queries  

Page 26: TestDriven+DataModeling+With+Graphsnosqlroadshow.com/dl/NoSQL-Amsterdam-2013/Slides/...Outline+ • Datamodeling+with+graphs+ • Neo4j+applicaon+architecture+opDons+ • TesDng+your+datamodel++

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  applicaDon  architecture…  

Page 27: TestDriven+DataModeling+With+Graphsnosqlroadshow.com/dl/NoSQL-Amsterdam-2013/Slides/...Outline+ • Datamodeling+with+graphs+ • Neo4j+applicaon+architecture+opDons+ • TesDng+your+datamodel++

ApplicaDon  Architectures  

•  Embedded  •  Server  •  Server  with  Extensions  

Page 28: TestDriven+DataModeling+With+Graphsnosqlroadshow.com/dl/NoSQL-Amsterdam-2013/Slides/...Outline+ • Datamodeling+with+graphs+ • Neo4j+applicaon+architecture+opDons+ • TesDng+your+datamodel++

ApplicaDon  Architectures  

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

•  Server  •  Server  with  Extensions  

Java  APIs  

ApplicaDon  

Page 29: TestDriven+DataModeling+With+Graphsnosqlroadshow.com/dl/NoSQL-Amsterdam-2013/Slides/...Outline+ • Datamodeling+with+graphs+ • Neo4j+applicaon+architecture+opDons+ • TesDng+your+datamodel++

ApplicaDon  Architectures  

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

•  Server  with  Extensions  

REST  API  REST  API  REST  API  

REST  Client  

ApplicaDon  

Write  LB   Read  LB  

Page 30: TestDriven+DataModeling+With+Graphsnosqlroadshow.com/dl/NoSQL-Amsterdam-2013/Slides/...Outline+ • Datamodeling+with+graphs+ • Neo4j+applicaon+architecture+opDons+ • TesDng+your+datamodel++

ApplicaDon  Architectures  

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

REST  API   Extensions  

Page 31: TestDriven+DataModeling+With+Graphsnosqlroadshow.com/dl/NoSQL-Amsterdam-2013/Slides/...Outline+ • Datamodeling+with+graphs+ • Neo4j+applicaon+architecture+opDons+ • TesDng+your+datamodel++

Embedded  Example  

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

Page 32: TestDriven+DataModeling+With+Graphsnosqlroadshow.com/dl/NoSQL-Amsterdam-2013/Slides/...Outline+ • Datamodeling+with+graphs+ • Neo4j+applicaon+architecture+opDons+ • TesDng+your+datamodel++

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: TestDriven+DataModeling+With+Graphsnosqlroadshow.com/dl/NoSQL-Amsterdam-2013/Slides/...Outline+ • Datamodeling+with+graphs+ • Neo4j+applicaon+architecture+opDons+ • TesDng+your+datamodel++

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: TestDriven+DataModeling+With+Graphsnosqlroadshow.com/dl/NoSQL-Amsterdam-2013/Slides/...Outline+ • Datamodeling+with+graphs+ • Neo4j+applicaon+architecture+opDons+ • TesDng+your+datamodel++

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: TestDriven+DataModeling+With+Graphsnosqlroadshow.com/dl/NoSQL-Amsterdam-2013/Slides/...Outline+ • Datamodeling+with+graphs+ • Neo4j+applicaon+architecture+opDons+ • TesDng+your+datamodel++

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    ExecuDonEngine  

Page 36: TestDriven+DataModeling+With+Graphsnosqlroadshow.com/dl/NoSQL-Amsterdam-2013/Slides/...Outline+ • Datamodeling+with+graphs+ • Neo4j+applicaon+architecture+opDons+ • TesDng+your+datamodel++

ImpermanentGraphDatabase  

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

Page 37: TestDriven+DataModeling+With+Graphsnosqlroadshow.com/dl/NoSQL-Amsterdam-2013/Slides/...Outline+ • Datamodeling+with+graphs+ • Neo4j+applicaon+architecture+opDons+ • TesDng+your+datamodel++

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: TestDriven+DataModeling+With+Graphsnosqlroadshow.com/dl/NoSQL-Amsterdam-2013/Slides/...Outline+ • Datamodeling+with+graphs+ • Neo4j+applicaon+architecture+opDons+ • TesDng+your+datamodel++

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: TestDriven+DataModeling+With+Graphsnosqlroadshow.com/dl/NoSQL-Amsterdam-2013/Slides/...Outline+ • Datamodeling+with+graphs+ • Neo4j+applicaon+architecture+opDons+ • TesDng+your+datamodel++

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: TestDriven+DataModeling+With+Graphsnosqlroadshow.com/dl/NoSQL-Amsterdam-2013/Slides/...Outline+ • Datamodeling+with+graphs+ • Neo4j+applicaon+architecture+opDons+ • TesDng+your+datamodel++

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: TestDriven+DataModeling+With+Graphsnosqlroadshow.com/dl/NoSQL-Amsterdam-2013/Slides/...Outline+ • Datamodeling+with+graphs+ • Neo4j+applicaon+architecture+opDons+ • TesDng+your+datamodel++

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: TestDriven+DataModeling+With+Graphsnosqlroadshow.com/dl/NoSQL-Amsterdam-2013/Slides/...Outline+ • Datamodeling+with+graphs+ • Neo4j+applicaon+architecture+opDons+ • TesDng+your+datamodel++

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: TestDriven+DataModeling+With+Graphsnosqlroadshow.com/dl/NoSQL-Amsterdam-2013/Slides/...Outline+ • Datamodeling+with+graphs+ • Neo4j+applicaon+architecture+opDons+ • TesDng+your+datamodel++

Inject  ExecuDonEngine  

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

Page 44: TestDriven+DataModeling+With+Graphsnosqlroadshow.com/dl/NoSQL-Amsterdam-2013/Slides/...Outline+ • Datamodeling+with+graphs+ • Neo4j+applicaon+architecture+opDons+ • TesDng+your+datamodel++

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: TestDriven+DataModeling+With+Graphsnosqlroadshow.com/dl/NoSQL-Amsterdam-2013/Slides/...Outline+ • Datamodeling+with+graphs+ • Neo4j+applicaon+architecture+opDons+ • TesDng+your+datamodel++

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: TestDriven+DataModeling+With+Graphsnosqlroadshow.com/dl/NoSQL-Amsterdam-2013/Slides/...Outline+ • Datamodeling+with+graphs+ • Neo4j+applicaon+architecture+opDons+ • TesDng+your+datamodel++

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: TestDriven+DataModeling+With+Graphsnosqlroadshow.com/dl/NoSQL-Amsterdam-2013/Slides/...Outline+ • Datamodeling+with+graphs+ • Neo4j+applicaon+architecture+opDons+ • TesDng+your+datamodel++

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: TestDriven+DataModeling+With+Graphsnosqlroadshow.com/dl/NoSQL-Amsterdam-2013/Slides/...Outline+ • Datamodeling+with+graphs+ • Neo4j+applicaon+architecture+opDons+ • TesDng+your+datamodel++

Server  Extension  Example  

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

Page 49: TestDriven+DataModeling+With+Graphsnosqlroadshow.com/dl/NoSQL-Amsterdam-2013/Slides/...Outline+ • Datamodeling+with+graphs+ • Neo4j+applicaon+architecture+opDons+ • TesDng+your+datamodel++

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: TestDriven+DataModeling+With+Graphsnosqlroadshow.com/dl/NoSQL-Amsterdam-2013/Slides/...Outline+ • Datamodeling+with+graphs+ • Neo4j+applicaon+architecture+opDons+ • TesDng+your+datamodel++

JAX-­‐RS  AnnotaDons  

@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: TestDriven+DataModeling+With+Graphsnosqlroadshow.com/dl/NoSQL-Amsterdam-2013/Slides/...Outline+ • Datamodeling+with+graphs+ • Neo4j+applicaon+architecture+opDons+ • TesDng+your+datamodel++

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: TestDriven+DataModeling+With+Graphsnosqlroadshow.com/dl/NoSQL-Amsterdam-2013/Slides/...Outline+ • Datamodeling+with+graphs+ • Neo4j+applicaon+architecture+opDons+ • TesDng+your+datamodel++

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  ExecuDonEngine  reused  across  resource  instances  

Page 53: TestDriven+DataModeling+With+Graphsnosqlroadshow.com/dl/NoSQL-Amsterdam-2013/Slides/...Outline+ • Datamodeling+with+graphs+ • Neo4j+applicaon+architecture+opDons+ • TesDng+your+datamodel++

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: TestDriven+DataModeling+With+Graphsnosqlroadshow.com/dl/NoSQL-Amsterdam-2013/Slides/...Outline+ • Datamodeling+with+graphs+ • Neo4j+applicaon+architecture+opDons+ • TesDng+your+datamodel++

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: TestDriven+DataModeling+With+Graphsnosqlroadshow.com/dl/NoSQL-Amsterdam-2013/Slides/...Outline+ • Datamodeling+with+graphs+ • Neo4j+applicaon+architecture+opDons+ • TesDng+your+datamodel++

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: TestDriven+DataModeling+With+Graphsnosqlroadshow.com/dl/NoSQL-Amsterdam-2013/Slides/...Outline+ • Datamodeling+with+graphs+ • Neo4j+applicaon+architecture+opDons+ • TesDng+your+datamodel++

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: TestDriven+DataModeling+With+Graphsnosqlroadshow.com/dl/NoSQL-Amsterdam-2013/Slides/...Outline+ • Datamodeling+with+graphs+ • Neo4j+applicaon+architecture+opDons+ • TesDng+your+datamodel++

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: TestDriven+DataModeling+With+Graphsnosqlroadshow.com/dl/NoSQL-Amsterdam-2013/Slides/...Outline+ • Datamodeling+with+graphs+ • Neo4j+applicaon+architecture+opDons+ • TesDng+your+datamodel++

CommunityServerBuilder  

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

Page 59: TestDriven+DataModeling+With+Graphsnosqlroadshow.com/dl/NoSQL-Amsterdam-2013/Slides/...Outline+ • Datamodeling+with+graphs+ • Neo4j+applicaon+architecture+opDons+ • TesDng+your+datamodel++

TesDng  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: TestDriven+DataModeling+With+Graphsnosqlroadshow.com/dl/NoSQL-Amsterdam-2013/Slides/...Outline+ • Datamodeling+with+graphs+ • Neo4j+applicaon+architecture+opDons+ • TesDng+your+datamodel++

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: TestDriven+DataModeling+With+Graphsnosqlroadshow.com/dl/NoSQL-Amsterdam-2013/Slides/...Outline+ • Datamodeling+with+graphs+ • Neo4j+applicaon+architecture+opDons+ • TesDng+your+datamodel++

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: TestDriven+DataModeling+With+Graphsnosqlroadshow.com/dl/NoSQL-Amsterdam-2013/Slides/...Outline+ • Datamodeling+with+graphs+ • Neo4j+applicaon+architecture+opDons+ • TesDng+your+datamodel++

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: TestDriven+DataModeling+With+Graphsnosqlroadshow.com/dl/NoSQL-Amsterdam-2013/Slides/...Outline+ • Datamodeling+with+graphs+ • Neo4j+applicaon+architecture+opDons+ • TesDng+your+datamodel++

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: TestDriven+DataModeling+With+Graphsnosqlroadshow.com/dl/NoSQL-Amsterdam-2013/Slides/...Outline+ • Datamodeling+with+graphs+ • Neo4j+applicaon+architecture+opDons+ • TesDng+your+datamodel++

Thank  you  

Twi7er:  @ianSrobinson  #neo4j  

                       

Ian Robinson, Jim Webber & Emil Eifrem

Graph Databases

h

Compliments

of Neo Technology

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


Recommended