Test%Driven+Data+Modeling+With+Graphs+
Twi7er:+@ianSrobinson+ #neo4j+ +
Test%Driven+Data+Modeling+With+Graphs + Twi7er:+@ianSrobinson+ - - PowerPoint PPT Presentation
Test%Driven+Data+Modeling+With+Graphs + Twi7er:+@ianSrobinson+ #neo4j+ + Outline+ Data+modeling+with+graphs+ Neo4j+applicaDon+architecture+opDons+ TesDng+your+data+model++ Graph+data+modeling+ Labeled+Property+Graph+ Models+
Twi7er:+@ianSrobinson+ #neo4j+ +
Images:+en.wikipedia.org+
'
'
As#an#employee' ' I#want#to'know'who'in'the'company' has'similar'skills'to'me' ' So#that#we'can'exchange'knowledge'
RelaDonship+ Label+
(:Person)-[:WORKS_FOR]->(:Company), (:Person)-[:HAS_SKILL]->(:Skill)
(:Company)<-[:WORKS_FOR]-(:Person)-[:HAS_SKILL]->(:Skill)
(:Company)<-[:WORKS_FOR]-(:Person)-[:HAS_SKILL]->(:Skill)
MATCH (company)<-[:WORKS_FOR]-(me:Person)-[:HAS_SKILL]->(skill), (company)<-[:WORKS_FOR]-(colleague)-[:HAS_SKILL]->(skill) WHERE me.name = {name} RETURN colleague.name AS name, count(skill) AS score, collect(skill.name) AS skills ORDER BY score DESC
MATCH (company)<-[:WORKS_FOR]-( MATCH (company)<-[:WORKS_FOR]-(me:Person me:Person)-[:HAS_SKILL]->(skill), )-[:HAS_SKILL]->(skill), (company)<-[:WORKS_FOR]-(colleague)-[:HAS_SKILL]->(skill) (company)<-[:WORKS_FOR]-(colleague)-[:HAS_SKILL]->(skill) WHERE me.name = {name} RETURN colleague.name AS name, count(skill) AS score, collect(skill.name) AS skills ORDER BY score DESC
MATCH (company)<-[:WORKS_FOR]-(me:Person me:Person)-[:HAS_SKILL]->(skill), (company)<-[:WORKS_FOR]-(colleague)-[:HAS_SKILL]->(skill) WHERE WHERE me.name me.name = {name} = {name} RETURN colleague.name AS name, count(skill) AS score, collect(skill.name) AS skills ORDER BY score DESC
Search+nodes+labeled+ ‘Person’,+matching+on+ ‘name’+property+
MATCH (company)<-[:WORKS_FOR]-(me:Person)-[:HAS_SKILL]->(skill), (company)<-[:WORKS_FOR]-(colleague)-[:HAS_SKILL]->(skill) WHERE me.name = {name} RETURN RETURN colleague.name colleague.name AS name, AS name, count(skill) AS score, count(skill) AS score, collect( collect(skill.name skill.name) AS skills ) AS skills ORDER BY score DESC ORDER BY score DESC
MATCH (company)<-[:WORKS_FOR]-(me:Person)-[:HAS_SKILL]->(skill), (company)<-[:WORKS_FOR]-(colleague)-[:HAS_SKILL]->(skill) WHERE me.name = {name} RETURN colleague.name AS name, count(skill) AS score, collect(skill.name) AS skills ORDER BY score DESC 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?
Java+APIs+ ApplicaDon+
REST+API+ REST+API+ REST+API+ REST+Client+ ApplicaDon+ Write+LB+ Read+LB+
REST+API+ Extensions+
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(); } }
public class ColleagueFinderTest { private private GraphDatabaseService GraphDatabaseService db db; private ColleagueFinder finder; @Before public void init() { db db = new = new TestGraphDatabaseFactory TestGraphDatabaseFactory(). ().newImpermanentDatabase newImpermanentDatabase(); (); ExampleGraph.populate( db ); finder = new ColleagueFinder( new ExecutionEngine( db ) ); } @After public void shutdown() { db.shutdown(); } }
public class ColleagueFinderTest { private GraphDatabaseService db; private ColleagueFinder finder; @Before public void init() { db = new TestGraphDatabaseFactory().newImpermanentDatabase(); ExampleGraph.populate ExampleGraph.populate( ( db db ); ); finder = new ColleagueFinder( new ExecutionEngine( db ) ); } @After public void shutdown() { db.shutdown(); } }
public class ColleagueFinderTest { private GraphDatabaseService db; private private ColleagueFinder ColleagueFinder finder; finder; @Before public void init() { db = new TestGraphDatabaseFactory().newImpermanentDatabase(); ExampleGraph.populate( db ); finder = new finder = new ColleagueFinder ColleagueFinder( new ( new ExecutionEngine ExecutionEngine( ( db db ) ); ) ); } @After public void shutdown() { db.shutdown(); } }
Inject++ ExecuDonEngine+
<dependency> <groupId>org.neo4j</groupId> <artifactId>neo4j-kernel</artifactId> <version>${project.version}</version> <type>test-jar</type> <scope>test</scope> </dependency>
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 ); }
@Test public void shouldFindColleaguesWithSimilarSkills() throws Exception { // when Iterator<Map<String, Object>> results = finder.findColleaguesFor( "Ian" ); // then assertEquals( "Lucy", results.next().get( "name" ) ); assertEquals( "Bill", results.next().get( "name" ) ); assertFalse( results.hasNext() ); }
@Test public void shouldFindColleaguesWithSimilarSkills() throws Exception { // when Iterator<Map<String, Object>> results = Iterator<Map<String, Object>> results = finder.findColleaguesFor finder.findColleaguesFor( "Ian" ); ( "Ian" ); // then assertEquals( "Lucy", results.next().get( "name" ) ); assertEquals( "Bill", results.next().get( "name" ) ); assertFalse( results.hasNext() ); }
@Test public void shouldFindColleaguesWithSimilarSkills() throws Exception { // when Iterator<Map<String, Object>> results = finder.findColleaguesFor( "Ian" ); // then assertEquals assertEquals( "Lucy", ( "Lucy", results.next results.next().get( "name" ) ); ().get( "name" ) ); assertEquals assertEquals( "Bill", ( "Bill", results.next results.next().get( "name" ) ); ().get( "name" ) ); assertFalse( results.hasNext() ); }
@Test public void shouldFindColleaguesWithSimilarSkills() throws Exception { // when Iterator<Map<String, Object>> results = finder.findColleaguesFor( "Ian" ); // then assertEquals( "Lucy", results.next().get( "name" ) ); assertEquals( "Bill", results.next().get( "name" ) ); assertFalse assertFalse( ( results.hasNext results.hasNext() ); () ); }
public class ColleagueFinder { private final ExecutionEngine executionEngine; public ColleagueFinder( ExecutionEngine executionEngine ) { this.executionEngine = executionEngine; } public Iterator<Map<String, Object>> findColleaguesFor( String name ) { ... } }
public class ColleagueFinder { private final private final ExecutionEngine ExecutionEngine executionEngine executionEngine; public ColleagueFinder( ExecutionEngine ExecutionEngine executionEngine executionEngine ) { this.executionEngine this.executionEngine = = executionEngine executionEngine; } public Iterator<Map<String, Object>> findColleaguesFor( String name ) { ... } }
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(); }
public Iterator<Map<String, Object>> findColleaguesFor( String name ) { String cypher = String cypher = "MATCH ( "MATCH (me:Person me:Person)-[:WORKS_FOR]->(company),\n" + )-[:WORKS_FOR]->(company),\n" + " (me)-[:HAS_SKILL]->(skill),\n" + " (me)-[:HAS_SKILL]->(skill),\n" + " (colleague)-[:WORKS_FOR]->(company),\n" + " (colleague)-[:WORKS_FOR]->(company),\n" + " (colleague)-[:HAS_SKILL]->(skill)\n" + " (colleague)-[:HAS_SKILL]->(skill)\n" + "WHERE "WHERE me.name me.name = {name}\n" + = {name}\n" + "RETURN "RETURN colleague.name colleague.name AS name,\n" + AS name,\n" + " count(skill) AS score,\n" + " count(skill) AS score,\n" + " collect( " collect(skill.name skill.name) AS skills\n" + ) AS skills\n" + "ORDER BY score DESC"; "ORDER BY score DESC"; Map<String, Object> params = new HashMap<String, Object>(); params.put( "name", name ); return executionEngine.execute( cypher, params ).iterator(); }
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} {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> Map<String, Object> params params = new = new HashMap HashMap<String, Object>(); <String, Object>(); params.put params.put( "name", name ); ( "name", name ); return executionEngine.execute( cypher, params ).iterator(); }
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 return executionEngine.execute executionEngine.execute( cypher, ( cypher, params params ).iterator(); ).iterator(); }
@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(); } }
@Path("/similar-skills") @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 @GET @Produces( @Produces(MediaType.APPLICATION_JSON MediaType.APPLICATION_JSON) @Path("/{name}") @Path("/{name}") public Response getColleagues( @PathParam PathParam("name") ("name") String name ) throws IOException { String json = MAPPER .writeValueAsString( colleagueFinder.findColleaguesFor( name ) ); return Response.ok().entity( json ).build(); } }
@Path("/similar-skills") @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 @GET @Produces( @Produces(MediaType.APPLICATION_JSON MediaType.APPLICATION_JSON) @Path("/{name}") @Path("/{name}") public Response getColleagues( @PathParam PathParam("name") ("name") String name ) throws IOException { String json = MAPPER .writeValueAsString( colleagueFinder.findColleaguesFor( name ) ); return Response.ok().entity( json ).build(); } }
GET+ /similar%skills+ /Ian+
@Path("/similar-skills") public class ColleagueFinderExtension { private static final ObjectMapper MAPPER = new ObjectMapper(); private final private final ColleagueFinder ColleagueFinder colleagueFinder colleagueFinder; public ColleagueFinderExtension( @Context @Context CypherExecutor CypherExecutor cypherExecutor cypherExecutor ) { this.colleagueFinder this.colleagueFinder = new = new ColleagueFinder ColleagueFinder( ( cypherExecutor.getExecutionEngine 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+
@Path("/similar-skills") public class ColleagueFinderExtension { private static final private static final ObjectMapper ObjectMapper MAPPER = new MAPPER = new ObjectMapper 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 String json json = MAPPER = MAPPER . .writeValueAsString writeValueAsString( ( colleagueFinder.findColleaguesFor colleagueFinder.findColleaguesFor( name ) ); ( name ) ); return return Response.ok Response.ok().entity( ().entity( json json ).build(); ).build(); } }
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(); } }
public class ColleagueFinderExtensionTest { private private CommunityNeoServer CommunityNeoServer server; server; @Before public void startServer() throws IOException { server = server = CommunityServerBuilder.server CommunityServerBuilder.server() () . .withThirdPartyJaxRsPackage withThirdPartyJaxRsPackage( "org.neo4j.good_practices", "/colleagues" ) "org.neo4j.good_practices", "/colleagues" ) .build(); .build(); server.start(); ExampleGraph.populate( server.getDatabase().getGraph() ); } @After public void stopServer() { server.stop(); } }
public class ColleagueFinderExtensionTest { private CommunityNeoServer server; @Before public void startServer() throws IOException { server = CommunityServerBuilder.server() .withThirdPartyJaxRsPackage( "org.neo4j.good_practices", "/colleagues" ) .build(); server.start server.start(); (); ExampleGraph.populate( server.getDatabase().getGraph() ); } @After public void stopServer() { server.stop(); } }
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 ExampleGraph.populate( ( server.getDatabase server.getDatabase(). ().getGraph getGraph() ); () ); } @After public void stopServer() { server.stop(); } }
<dependency> <groupId>org.neo4j.app</groupId> <artifactId>neo4j-server</artifactId> <version>${project.version}</version> <type>test-jar</type> </dependency>
@Test public void shouldReturnColleaguesWithSimilarSkills() throws Exception { Client client = Client.create( new DefaultClientConfig() ); WebResource resource = client .resource( "http://localhost:7474/colleagues/similar-skills/Ian" ); ClientResponse response = resource .accept( MediaType.APPLICATION_JSON ) .get( ClientResponse.class ); List<Map<String, Object>> results = new ObjectMapper() .readValue(response.getEntity( String.class ), List.class ); // Assertions ...
@Test public void shouldReturnColleaguesWithSimilarSkills() throws Exception { Client client = Client client = Client.create Client.create( new ( new DefaultClientConfig 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 ...
@Test public void shouldReturnColleaguesWithSimilarSkills() throws Exception { Client client = Client.create( new DefaultClientConfig() ); WebResource WebResource resource = client resource = client .resource( "http://localhost:7474/colleagues/similar-skills/Ian" ); .resource( "http://localhost:7474/colleagues/similar-skills/Ian" ); ClientResponse ClientResponse response = resource response = resource .accept( .accept( MediaType.APPLICATION_JSON MediaType.APPLICATION_JSON ) ) .get( .get( ClientResponse.class ClientResponse.class ); ); List<Map<String, Object>> results = new ObjectMapper() .readValue(response.getEntity( String.class ), List.class ); // Assertions ...
@Test public void shouldReturnColleaguesWithSimilarSkills() throws Exception { Client client = Client.create( new DefaultClientConfig() ); WebResource resource = client .resource( "http://localhost:7474/colleagues/similar-skills/Ian" ); ClientResponse response = resource .accept( MediaType.APPLICATION_JSON ) .get( ClientResponse.class ); List<Map<String, Object>> results = new List<Map<String, Object>> results = new ObjectMapper ObjectMapper() () . .readValue readValue(response.getEntity response.getEntity( ( String.class String.class ), ), List.class List.class ); ); // Assertions ...
... 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" ) ); }
Twi7er:+@ianSrobinson+ #neo4j+ + + +
Ian Robinson, Jim Webber & Emil Eifrem
Compliments
github.com/iansrobinson/neo4j%good%pracDces+ +