SlideShare une entreprise Scribd logo
1  sur  64
Télécharger pour lire hors ligne
Designing	
  and	
  Building	
  a	
  Graph	
  
Database	
  Applica5on

	
  

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

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

•  Find	
  my	
  professional	
  social	
  network	
  
–  Exchange	
  knowledge	
  
–  Interest	
  groups	
  
–  Help	
  
–  Staff	
  projects	
  
Applica5on/End-­‐User	
  Goals	
  
As	
  an	
  emp

	
  

loyee	
  

I	
  want	
  to	
  k
now	
  who	
  i
n	
  the	
  comp
has	
  similar
any	
  
	
  skills	
  to	
  m
	
  
e	
  
So	
  that	
  we
	
  can	
  excha
nge	
  knowl
edge	
  
Ques5ons	
  To	
  Ask	
  of	
  the	
  Domain	
  
As	
  an	
  emp
loyee	
  
	
  
I	
  want	
  to	
  k
now	
  who	
  i
n	
  the	
  co
has	
  similar	
  
skills	
  to	
  me mpany	
  
	
  
	
  
So	
  that	
  we
	
  can	
  excha
nge	
  knowle
dge	
  

Which	
  people,	
  who	
  work	
  for	
  the	
  same	
  company	
  
as	
  me,	
  have	
  similar	
  skills	
  to	
  me?	
  
Iden5fy	
  En55es	
  
Which	
  people,	
  who	
  work	
  for	
  the	
  same	
  company	
  
as	
  me,	
  have	
  similar	
  skills	
  to	
  me?	
  
	
  
Person	
  
Company	
  
Skill	
  
Iden5fy	
  Rela5onships	
  Between	
  En55es	
  
Which	
  people,	
  who	
  work	
  for	
  the	
  same	
  company	
  
as	
  me,	
  have	
  similar	
  skills	
  to	
  me?	
  
	
  
Person	
  WORKS_FOR	
  Company	
  
Person	
  HAS_SKILL	
  Skill	
  
Convert	
  to	
  Cypher	
  Paths	
  
Rela5onship	
  

Person	
  WORKS_FOR	
  Company	
  
Person	
  HAS_SKILL	
  Skill	
  
Label	
  

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

(:Company)<-[:WORKS_FOR]-(:Person)-[:HAS_SKILL]->(:Skill)
Candidate	
  Data	
  Model	
  
(:Company)<-[:WORKS_FOR]-(:Person)-[:HAS_SKILL]->(:Skill)
Express	
  Ques5on	
  as	
  Graph	
  Pa9ern	
  
Which	
  people,	
  who	
  work	
  for	
  the	
  same	
  company	
  
as	
  me,	
  have	
  similar	
  skills	
  to	
  me?	
  
Cypher	
  Query	
  
Which	
  people,	
  who	
  work	
  for	
  the	
  same	
  company	
  
as	
  me,	
  have	
  similar	
  skills	
  to	
  me?	
  
	
MATCH (company)<-[:WORKS_FOR]-(me:Person)-[:HAS_SKILL]->(skill),	
(company)<-[:WORKS_FOR]-(colleague)-[:HAS_SKILL]->(skill)	
WHERE me.name = {name}	
RETURN colleague.name AS name,	
count(skill) AS score,	
collect(skill.name) AS skills	
ORDER BY score DESC
Graph	
  Pa9ern	
  
Which	
  people,	
  who	
  work	
  for	
  the	
  same	
  company	
  
as	
  me,	
  have	
  similar	
  skills	
  to	
  me?	
  
	
MATCH (company)<-[:WORKS_FOR]-(me:Person)-[:HAS_SKILL]->(skill),	
(company)<-[:WORKS_FOR]-(colleague)-[:HAS_SKILL]->(skill)	
WHERE me.name = {name}	
RETURN colleague.name AS name,	
count(skill) AS score,	
collect(skill.name) AS skills	
ORDER BY score DESC
Anchor	
  Pa9ern	
  in	
  Graph	
  
Which	
  people,	
  who	
  work	
  for	
  the	
  same	
  company	
  
as	
  me,	
  have	
  similar	
  skills	
  to	
  me?	
  
	
MATCH (company)<-[:WORKS_FOR]-(me:Person)-[:HAS_SKILL]->(skill),	
(company)<-[:WORKS_FOR]-(colleague)-[:HAS_SKILL]->(skill)	
WHERE me.name = {name}	
RETURN colleague.name AS name,	
count(skill) AS score,	
collect(skill.name) AS skills	
ORDER BY score DESC	

Search	
  nodes	
  labeled	
  
‘Person’,	
  matching	
  on	
  
‘name’	
  property	
  
Create	
  Projec5on	
  of	
  Results	
  
Which	
  people,	
  who	
  work	
  for	
  the	
  same	
  company	
  
as	
  me,	
  have	
  similar	
  skills	
  to	
  me?	
  
	
MATCH (company)<-[:WORKS_FOR]-(me:Person)-[:HAS_SKILL]->(skill),	
(company)<-[:WORKS_FOR]-(colleague)-[:HAS_SKILL]->(skill)	
WHERE me.name = {name}	
RETURN colleague.name AS name,	
count(skill) AS score,	
collect(skill.name) AS skills	
ORDER BY score DESC
First	
  Match	
  
Second	
  Match	
  
Third	
  Match	
  
Running	
  the	
  Query	
  
+-----------------------------------+	
| name
| score | skills
|	
+-----------------------------------+	
| "Lucy" | 2
| ["Java","Neo4j"] |	
| "Bill" | 1
| ["Neo4j"]
|	
+-----------------------------------+	
2 rows
From	
  User	
  Story	
  to	
  Model	
  and	
  Query	
  
As	
  an	
  emp
loyee	
  
	
  
I	
  want	
  to	
  k
now	
  who	
  i
n	
  the	
  co
has	
  similar	
  
skills	
  to	
  me mpany	
  
	
  
	
  
So	
  that	
  we
	
  can	
  excha
nge	
  knowle
dge	
  

MATCH (company)<-[:WORKS_FOR]-(me:Person)-[:HAS_SKILL]->(skill),	
(company)<-[:WORKS_FOR]-(colleague)-[:HAS_SKILL]->(skill)	
WHERE me.name = {name}	
RETURN colleague.name AS name,	
count(skill) AS score,	
collect(skill.name) AS skills	
ORDER BY score DESC	

?

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

Person	
  WORKS_FOR	
  Company	
  
Person	
  HAS_SKILL	
  Skill	

(:Company)<-[:WORKS_FOR]-(:Person)-[:HAS_SKILL]->(:Skill)
Tes5ng	
  
Why	
  Test?	
  
•  Ensure	
  model	
  is	
  fit	
  for	
  queries	
  
–  Rapid	
  feedback	
  

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

•  Provide	
  a	
  regression	
  test	
  suite	
  
–  Allows	
  you	
  to	
  change	
  and	
  evolve	
  model	
  and	
  
queries	
  
Method	
  
•  Develop	
  queries,	
  or	
  classes	
  that	
  encapsulate	
  
queries,	
  using	
  unit	
  tests	
  
•  Use	
  small,	
  well-­‐understood	
  datasets	
  in	
  each	
  test	
  
–  Create	
  data	
  in	
  test	
  setup	
  
–  Test	
  dataset	
  expresses	
  your	
  understanding	
  of	
  (part	
  of)	
  
the	
  domain	
  

•  Inject	
  in-­‐memory	
  graph	
  database	
  (or	
  Cypher	
  
engine)	
  into	
  object	
  under	
  test	
  
•  The	
  exact	
  strategy	
  you	
  use	
  depends	
  on	
  your	
  
applica5on	
  architecture…	
  
Applica5on	
  Architectures	
  
•  Embedded	
  
•  Server	
  
•  Server	
  with	
  Extensions	
  
Applica5on	
  Architectures	
  
•  Embedded	
  
–  Host	
  in	
  Java	
  process	
  
–  Access	
  to	
  Java	
  APIs	
  

•  Server	
  
•  Server	
  with	
  Extensions	
  

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

•  Server	
  with	
  Extensions	
  

Applica5on	
  
REST	
  Client	
  

Write	
  LB	
  
REST	
  API	
  

REST	
  API	
  

Read	
  LB	
  
REST	
  API	
  
Applica5on	
  Architectures	
  
•  Embedded	
  
•  Server	
  
•  Server	
  with	
  Extensions	
  

REST	
  API	
  

–  Execute	
  complex	
  logic	
  on	
  server	
  
–  Control	
  HTTP	
  request/response	
  format	
  

Extensions	
  
Embedded	
  Example	
  
•  Company	
  social	
  network	
  
•  Find	
  colleagues	
  with	
  similar	
  skills	
  
•  Encapsulate	
  query	
  in	
  a	
  ColleagueFinder
Unit	
  Test	
  Fixture	
  
public class ColleagueFinderTest {	
	
private GraphDatabaseService db;	
private ColleagueFinder finder;	
	
@Before	
public void init() {	
db = new TestGraphDatabaseFactory().newImpermanentDatabase();	
ExampleGraph.populate( db );	
finder = new ColleagueFinder( new ExecutionEngine( db ) );	
}	
	
@After	
public void shutdown() {	
db.shutdown();	
}	
}
Create	
  Database	
  
public class ColleagueFinderTest {	
	
private GraphDatabaseService db;	
private ColleagueFinder finder;	
	
@Before	
public void init() {	
db = new TestGraphDatabaseFactory().newImpermanentDatabase();	
ExampleGraph.populate( db );	
finder = new ColleagueFinder( new ExecutionEngine( db ) );	
}	
	
@After	
public void shutdown() {	
db.shutdown();	
}	
}
Populate	
  Graph	
  
public class ColleagueFinderTest {	
	
private GraphDatabaseService db;	
private ColleagueFinder finder;	
	
@Before	
public void init() {	
db = new TestGraphDatabaseFactory().newImpermanentDatabase();	
ExampleGraph.populate( db );	
finder = new ColleagueFinder( new ExecutionEngine( db ) );	
}	
	
@After	
public void shutdown() {	
db.shutdown();	
}	
}
Create	
  Object	
  Under	
  Test	
  
public class ColleagueFinderTest {	
	
private GraphDatabaseService db;	
private ColleagueFinder finder;	
	
@Before	
public void init() {	
db = new TestGraphDatabaseFactory().newImpermanentDatabase();	
ExampleGraph.populate( db );	
finder = new ColleagueFinder( new ExecutionEngine( db ) );	
}	
	
@After	
public void shutdown() {	
db.shutdown();	
}	
}	

Inject	
  	
  
Execu5onEngine	
  
ImpermanentGraphDatabase	
  
•  In-­‐memory	
  
•  For	
  tes5ng	
  only,	
  not	
  produc5on!	
  
	
	
<dependency>	
<groupId>org.neo4j</groupId>	
<artifactId>neo4j-kernel</artifactId>	
<version>${project.version}</version>	
<type>test-jar</type>	
<scope>test</scope>	
</dependency>
Create	
  Sample	
  Data	
  
public static void populate( GraphDatabaseService db ) {	
	
ExecutionEngine engine = new ExecutionEngine( db );	
	
String cypher = 	
"CREATE ian:Person VALUES {name:'Ian'},n" +	
"
bill:Person VALUES {name:'Bill'},n" +	
"
lucy:Person VALUES {name:'Lucy'},n" +	
"
acme:Company VALUES {name:'Acme'},n" +	
	
// Cypher continues...	
	
"
"
"
"

(bill)-[:HAS_SKILL]->(neo4j),n" +	
(bill)-[:HAS_SKILL]->(ruby),n" +	
(lucy)-[:HAS_SKILL]->(java),n" +	
(lucy)-[:HAS_SKILL]->(neo4j)";	

	
engine.execute( cypher );	
}
Unit	
  Test	
  
@Test	
public void shouldFindColleaguesWithSimilarSkills() throws Exception {	
	
// when	
Iterator<Map<String, Object>> results = finder.findColleaguesFor( "Ian" );	
	
// then	
assertEquals( "Lucy", results.next().get( "name" ) );	
assertEquals( "Bill", results.next().get( "name" ) );	
	
assertFalse( results.hasNext() );	
}
Execute	
  Object	
  Under	
  Test	
  
@Test	
public void shouldFindColleaguesWithSimilarSkills() throws Exception {	
	
// when	
Iterator<Map<String, Object>> results = finder.findColleaguesFor( "Ian" );	
	
// then	
assertEquals( "Lucy", results.next().get( "name" ) );	
assertEquals( "Bill", results.next().get( "name" ) );	
	
assertFalse( results.hasNext() );	
}
Assert	
  Results	
  
@Test	
public void shouldFindColleaguesWithSimilarSkills() throws Exception {	
	
// when	
Iterator<Map<String, Object>> results = finder.findColleaguesFor( "Ian" );	
	
// then	
assertEquals( "Lucy", results.next().get( "name" ) );	
assertEquals( "Bill", results.next().get( "name" ) );	
	
assertFalse( results.hasNext() );	
}
Ensure	
  No	
  More	
  Results	
  
@Test	
public void shouldFindColleaguesWithSimilarSkills() throws Exception {	
	
// when	
Iterator<Map<String, Object>> results = finder.findColleaguesFor( "Ian" );	
	
// then	
assertEquals( "Lucy", results.next().get( "name" ) );	
assertEquals( "Bill", results.next().get( "name" ) );	
	
assertFalse( results.hasNext() );	
}
ColleagueFinder	
  
public class ColleagueFinder {	
	
private final ExecutionEngine executionEngine;	
	
public ColleagueFinder( ExecutionEngine executionEngine ) {	
this.executionEngine = executionEngine;	
}	
	
public Iterator<Map<String, Object>> findColleaguesFor( String name ) {	
...	
}	
}
Inject	
  Execu5onEngine	
  
public class ColleagueFinder {	
	
private final ExecutionEngine executionEngine;	
	
public ColleagueFinder( ExecutionEngine executionEngine ) {	
this.executionEngine = executionEngine;	
}	
	
public Iterator<Map<String, Object>> findColleaguesFor( String name ) {	
...	
}	
}
findColleaguesFor()	
  Method	
  
public Iterator<Map<String, Object>> findColleaguesFor( String name ) {	
	
String cypher =	
"MATCH (me:Person)-[:WORKS_FOR]->(company),n" +	
"
(me)-[:HAS_SKILL]->(skill),n" +	
"
(colleague)-[:WORKS_FOR]->(company),n" +	
"
(colleague)-[:HAS_SKILL]->(skill)n" +	
"WHERE me.name = {name}n" +	
"RETURN colleague.name AS name,n" +	
"
count(skill) AS score,n" +	
"
collect(skill.name) AS skillsn" +	
"ORDER BY score DESC";	
	
Map<String, Object> params = new HashMap<String, Object>();	
params.put( "name", name );	
	
return executionEngine.execute( cypher, params ).iterator();	
}
Cypher	
  Query	
  
public Iterator<Map<String, Object>> findColleaguesFor( String name ) {	
	
String cypher =	
"MATCH (me:Person)-[:WORKS_FOR]->(company),n" +	
"
(me)-[:HAS_SKILL]->(skill),n" +	
"
(colleague)-[:WORKS_FOR]->(company),n" +	
"
(colleague)-[:HAS_SKILL]->(skill)n" +	
"WHERE me.name = {name}n" +	
"RETURN colleague.name AS name,n" +	
"
count(skill) AS score,n" +	
"
collect(skill.name) AS skillsn" +	
"ORDER BY score DESC";	
	
Map<String, Object> params = new HashMap<String, Object>();	
params.put( "name", name );	
	
return executionEngine.execute( cypher, params ).iterator();	
}
Parameterized	
  Query	
  
public Iterator<Map<String, Object>> findColleaguesFor( String name ) {	
	
String cypher =	
"MATCH (me:Person)-[:WORKS_FOR]->(company),n" +	
"
(me)-[:HAS_SKILL]->(skill),n" +	
"
(colleague)-[:WORKS_FOR]->(company),n" +	
"
(colleague)-[:HAS_SKILL]->(skill)n" +	
"WHERE me.name = {name}n" +	
"RETURN colleague.name AS name,n" +	
"
count(skill) AS score,n" +	
"
collect(skill.name) AS skillsn" +	
"ORDER BY score DESC";	
	
Map<String, Object> params = new HashMap<String, Object>();	
params.put( "name", name );	
	
return executionEngine.execute( cypher, params ).iterator();	
}
Execute	
  Query	
  
public Iterator<Map<String, Object>> findColleaguesFor( String name ) {	
	
String cypher =	
"MATCH (me:Person)-[:WORKS_FOR]->(company),n" +	
"
(me)-[:HAS_SKILL]->(skill),n" +	
"
(colleague)-[:WORKS_FOR]->(company),n" +	
"
(colleague)-[:HAS_SKILL]->(skill)n" +	
"WHERE me.name = {name}n" +	
"RETURN colleague.name AS name,n" +	
"
count(skill) AS score,n" +	
"
collect(skill.name) AS skillsn" +	
"ORDER BY score DESC";	
	
Map<String, Object> params = new HashMap<String, Object>();	
params.put( "name", name );	
	
return executionEngine.execute( cypher, params ).iterator();	
}
Server	
  Extension	
  Example	
  
•  Same	
  data	
  mode	
  and	
  query	
  as	
  before	
  
•  This	
  5me,	
  we’ll	
  host	
  ColleagueFinder	
  in	
  a	
  
server	
  extension	
  
Server	
  Extension	
  
@Path("/similar-skills")	
public class ColleagueFinderExtension {	
private static final ObjectMapper MAPPER = new ObjectMapper();	
private final ColleagueFinder colleagueFinder;	
	
public ColleagueFinderExtension( @Context CypherExecutor cypherExecutor ) {	
this.colleagueFinder = new ColleagueFinder( cypherExecutor.getExecutionEngine() );	
}	
	
@GET	
@Produces(MediaType.APPLICATION_JSON)	
@Path("/{name}")	
public Response getColleagues( @PathParam("name") String name ) 	
throws IOException {	
	
String json = MAPPER	
.writeValueAsString( colleagueFinder.findColleaguesFor( name ) );	
return Response.ok().entity( json ).build();	
}	
}
JAX-­‐RS	
  Annota5ons	
  
@Path("/similar-skills")	
public class ColleagueFinderExtension {	
private static final ObjectMapper MAPPER = new ObjectMapper();	
private final ColleagueFinder colleagueFinder;	
	
public ColleagueFinderExtension( @Context CypherExecutor cypherExecutor ) {	
this.colleagueFinder = new ColleagueFinder( cypherExecutor.getExecutionEngine() );	
}	
	
@GET	
@Produces(MediaType.APPLICATION_JSON)	
@Path("/{name}")	
public Response getColleagues( @PathParam("name") String name ) 	
throws IOException {	
	
String json = MAPPER	
.writeValueAsString( colleagueFinder.findColleaguesFor( name ) );	
return Response.ok().entity( json ).build();	
}	
}
Map	
  HTTP	
  Request	
  to	
  Object	
  +	
  Method	
  
@Path("/similar-skills")	
public class ColleagueFinderExtension {	
private static final ObjectMapper MAPPER = new ObjectMapper();	
private final ColleagueFinder colleagueFinder;	
	
public ColleagueFinderExtension( @Context CypherExecutor cypherExecutor ) {	
this.colleagueFinder = new ColleagueFinder( cypherExecutor.getExecutionEngine() );	
}	
	
@GET	
@Produces(MediaType.APPLICATION_JSON)	
@Path("/{name}")	
public Response getColleagues( @PathParam("name") String name ) 	
throws IOException {	
	
String json = MAPPER	
.writeValueAsString( colleagueFinder.findColleaguesFor( name ) );	
return Response.ok().entity( json ).build();	
}	
}	

GET	
  

/similar-­‐skills	
   /Ian	
  
CypherExecutor	
  Injected	
  by	
  Server	
  
Ensures	
  
Execu5onEngine	
  
reused	
  across	
  
resource	
  instances	
  

@Path("/similar-skills")	
public class ColleagueFinderExtension {	
private static final ObjectMapper MAPPER = new ObjectMapper();	
private final ColleagueFinder colleagueFinder;	
	
public ColleagueFinderExtension( @Context CypherExecutor cypherExecutor ) {	
this.colleagueFinder = new ColleagueFinder( cypherExecutor.getExecutionEngine() );	
}	
	
@GET	
@Produces(MediaType.APPLICATION_JSON)	
@Path("/{name}")	
public Response getColleagues( @PathParam("name") String name ) 	
throws IOException {	
	
String json = MAPPER	
.writeValueAsString( colleagueFinder.findColleaguesFor( name ) );	
return Response.ok().entity( json ).build();	
}	
}
Generate	
  and	
  Format	
  Response	
  
@Path("/similar-skills")	
public class ColleagueFinderExtension {	
private static final ObjectMapper MAPPER = new ObjectMapper();	
private final ColleagueFinder colleagueFinder;	
	
public ColleagueFinderExtension( @Context CypherExecutor cypherExecutor ) {	
this.colleagueFinder = new ColleagueFinder( cypherExecutor.getExecutionEngine() );	
}	
	
@GET	
@Produces(MediaType.APPLICATION_JSON)	
@Path("/{name}")	
public Response getColleagues( @PathParam("name") String name ) 	
throws IOException {	
	
String json = MAPPER	
.writeValueAsString( colleagueFinder.findColleaguesFor( name ) );	
return Response.ok().entity( json ).build();	
}	
}
Extension	
  Test	
  Fixture	
  
public class ColleagueFinderExtensionTest {	
private CommunityNeoServer server;	
	
@Before	
public void startServer() throws IOException	
{	
server = CommunityServerBuilder.server()	
.withThirdPartyJaxRsPackage(	
"org.neo4j.good_practices", "/colleagues" )	
.build();	
server.start();	
	
ExampleGraph.populate( server.getDatabase().getGraph() );	
}	
	
@After	
public void stopServer() {	
server.stop();	
}	
}
Build	
  and	
  Configure	
  Server	
  
public class ColleagueFinderExtensionTest {	
private CommunityNeoServer server;	
	
@Before	
public void startServer() throws IOException	
{	
server = CommunityServerBuilder.server()	
.withThirdPartyJaxRsPackage(	
"org.neo4j.good_practices", "/colleagues" )	
.build();	
server.start();	
	
ExampleGraph.populate( server.getDatabase().getGraph() );	
}	
	
@After	
public void stopServer() {	
server.stop();	
}	
}
Start	
  Server	
  
public class ColleagueFinderExtensionTest {	
private CommunityNeoServer server;	
	
@Before	
public void startServer() throws IOException	
{	
server = CommunityServerBuilder.server()	
.withThirdPartyJaxRsPackage(	
"org.neo4j.good_practices", "/colleagues" )	
.build();	
server.start();	
	
ExampleGraph.populate( server.getDatabase().getGraph() );	
}	
	
@After	
public void stopServer() {	
server.stop();	
}	
}
Populate	
  Database	
  
public class ColleagueFinderExtensionTest {	
private CommunityNeoServer server;	
	
@Before	
public void startServer() throws IOException	
{	
server = CommunityServerBuilder.server()	
.withThirdPartyJaxRsPackage(	
"org.neo4j.good_practices", "/colleagues" )	
.build();	
server.start();	
	
ExampleGraph.populate( server.getDatabase().getGraph() );	
}	
	
@After	
public void stopServer() {	
server.stop();	
}	
}
CommunityServerBuilder	
  
•  Programma5c	
  configura5on	
  
	
	
<dependency>	
<groupId>org.neo4j.app</groupId>	
<artifactId>neo4j-server</artifactId>	
<version>${project.version}</version>	
<type>test-jar</type>	
</dependency>
Tes5ng	
  Extension	
  Using	
  HTTP	
  Client	
  
@Test	
public void shouldReturnColleaguesWithSimilarSkills() throws Exception {	
	
Client client = Client.create( new DefaultClientConfig() );	
	
WebResource resource = client	
.resource( "http://localhost:7474/colleagues/similar-skills/Ian" );	
	
ClientResponse response = resource	
.accept( MediaType.APPLICATION_JSON )	
.get( ClientResponse.class );	
	
List<Map<String, Object>> results = new ObjectMapper()	
.readValue(response.getEntity( String.class ), List.class );	
	
// Assertions	
	
...
Create	
  HTTP	
  Client	
  
@Test	
public void shouldReturnColleaguesWithSimilarSkills() throws Exception {	
	
Client client = Client.create( new DefaultClientConfig() );	
	
WebResource resource = client	
.resource( "http://localhost:7474/colleagues/similar-skills/Ian" );	
	
ClientResponse response = resource	
.accept( MediaType.APPLICATION_JSON )	
.get( ClientResponse.class );	
	
List<Map<String, Object>> results = new ObjectMapper()	
.readValue(response.getEntity( String.class ), List.class );	
	
// Assertions	
	
...
Issue	
  Request	
  
@Test	
public void shouldReturnColleaguesWithSimilarSkills() throws Exception {	
	
Client client = Client.create( new DefaultClientConfig() );	
	
WebResource resource = client	
.resource( "http://localhost:7474/colleagues/similar-skills/Ian" );	
	
ClientResponse response = resource	
.accept( MediaType.APPLICATION_JSON )	
.get( ClientResponse.class );	
	
List<Map<String, Object>> results = new ObjectMapper()	
.readValue(response.getEntity( String.class ), List.class );	
	
// Assertions	
	
...
Parse	
  Response	
  
@Test	
public void shouldReturnColleaguesWithSimilarSkills() throws Exception {	
	
Client client = Client.create( new DefaultClientConfig() );	
	
WebResource resource = client	
.resource( "http://localhost:7474/colleagues/similar-skills/Ian" );	
	
ClientResponse response = resource	
.accept( MediaType.APPLICATION_JSON )	
.get( ClientResponse.class );	
	
List<Map<String, Object>> results = new ObjectMapper()	
.readValue(response.getEntity( String.class ), List.class );	
	
// Assertions	
	
...
Assert	
  Results	
  
	
...	
	
assertEquals( 200, response.getStatus() );	
assertEquals( MediaType.APPLICATION_JSON, 	
response.getHeaders().get( "Content-Type" ).get( 0 ) );	
	
assertEquals( "Lucy", results.get( 0 ).get( "name" ) );	
assertThat( (Iterable<String>) results.get( 0 ).get( "skills" ), 	
hasItems( "Java", "Neo4j" ) );	
}
ts gy
en lo
i m no
pl h
m ec
Co eo T
N
of

Graph
h
Databases

Thank	
  you	
  
Twi9er:	
  @ianSrobinson	
  
#neo4j	
  
	
  
	
  
	
  

Ian Robinson,
Jim Webber & Emil Eifrem

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

Contenu connexe

Tendances

The openCypher Project - An Open Graph Query Language
The openCypher Project - An Open Graph Query LanguageThe openCypher Project - An Open Graph Query Language
The openCypher Project - An Open Graph Query Language
Neo4j
 

Tendances (20)

Introduction to NOSQL databases
Introduction to NOSQL databasesIntroduction to NOSQL databases
Introduction to NOSQL databases
 
NoSQL Graph Databases - Why, When and Where
NoSQL Graph Databases - Why, When and WhereNoSQL Graph Databases - Why, When and Where
NoSQL Graph Databases - Why, When and Where
 
Property graph vs. RDF Triplestore comparison in 2020
Property graph vs. RDF Triplestore comparison in 2020Property graph vs. RDF Triplestore comparison in 2020
Property graph vs. RDF Triplestore comparison in 2020
 
Neo4j in Depth
Neo4j in DepthNeo4j in Depth
Neo4j in Depth
 
Graph database Use Cases
Graph database Use CasesGraph database Use Cases
Graph database Use Cases
 
Machine Learning with Spark MLlib
Machine Learning with Spark MLlibMachine Learning with Spark MLlib
Machine Learning with Spark MLlib
 
Getting Started with Knowledge Graphs
Getting Started with Knowledge GraphsGetting Started with Knowledge Graphs
Getting Started with Knowledge Graphs
 
Spark
SparkSpark
Spark
 
Traversing Graph Databases with Gremlin
Traversing Graph Databases with GremlinTraversing Graph Databases with Gremlin
Traversing Graph Databases with Gremlin
 
The Basics of MongoDB
The Basics of MongoDBThe Basics of MongoDB
The Basics of MongoDB
 
Neo4j + Tableau Visual Analytics - GraphConnect SF 2015
Neo4j + Tableau Visual Analytics - GraphConnect SF 2015 Neo4j + Tableau Visual Analytics - GraphConnect SF 2015
Neo4j + Tableau Visual Analytics - GraphConnect SF 2015
 
Building a Knowledge Graph using NLP and Ontologies
Building a Knowledge Graph using NLP and OntologiesBuilding a Knowledge Graph using NLP and Ontologies
Building a Knowledge Graph using NLP and Ontologies
 
Importing Data into Neo4j quickly and easily - StackOverflow
Importing Data into Neo4j quickly and easily - StackOverflowImporting Data into Neo4j quickly and easily - StackOverflow
Importing Data into Neo4j quickly and easily - StackOverflow
 
Using Amazon Neptune to power identity resolution at scale - ADB303 - Atlanta...
Using Amazon Neptune to power identity resolution at scale - ADB303 - Atlanta...Using Amazon Neptune to power identity resolution at scale - ADB303 - Atlanta...
Using Amazon Neptune to power identity resolution at scale - ADB303 - Atlanta...
 
Intro to Neo4j and Graph Databases
Intro to Neo4j and Graph DatabasesIntro to Neo4j and Graph Databases
Intro to Neo4j and Graph Databases
 
Data Lake Overview
Data Lake OverviewData Lake Overview
Data Lake Overview
 
The openCypher Project - An Open Graph Query Language
The openCypher Project - An Open Graph Query LanguageThe openCypher Project - An Open Graph Query Language
The openCypher Project - An Open Graph Query Language
 
Data Platform Architecture Principles and Evaluation Criteria
Data Platform Architecture Principles and Evaluation CriteriaData Platform Architecture Principles and Evaluation Criteria
Data Platform Architecture Principles and Evaluation Criteria
 
Introduction to Graph Databases
Introduction to Graph DatabasesIntroduction to Graph Databases
Introduction to Graph Databases
 
Oracle Data Warehouse
Oracle Data WarehouseOracle Data Warehouse
Oracle Data Warehouse
 

En vedette

An Introduction to Graph Databases
An Introduction to Graph DatabasesAn Introduction to Graph Databases
An Introduction to Graph Databases
InfiniteGraph
 
NoSQL: Why, When, and How
NoSQL: Why, When, and HowNoSQL: Why, When, and How
NoSQL: Why, When, and How
BigBlueHat
 
Introduction to graph databases GraphDays
Introduction to graph databases  GraphDaysIntroduction to graph databases  GraphDays
Introduction to graph databases GraphDays
Neo4j
 

En vedette (14)

Semantic Graph Databases: The Evolution of Relational Databases
Semantic Graph Databases: The Evolution of Relational DatabasesSemantic Graph Databases: The Evolution of Relational Databases
Semantic Graph Databases: The Evolution of Relational Databases
 
Relational to Graph - Import
Relational to Graph - ImportRelational to Graph - Import
Relational to Graph - Import
 
Graph Database, a little connected tour - Castano
Graph Database, a little connected tour - CastanoGraph Database, a little connected tour - Castano
Graph Database, a little connected tour - Castano
 
Converting Relational to Graph Databases
Converting Relational to Graph DatabasesConverting Relational to Graph Databases
Converting Relational to Graph Databases
 
Graph Based Recommendation Systems at eBay
Graph Based Recommendation Systems at eBayGraph Based Recommendation Systems at eBay
Graph Based Recommendation Systems at eBay
 
An Introduction to Graph Databases
An Introduction to Graph DatabasesAn Introduction to Graph Databases
An Introduction to Graph Databases
 
Relational vs. Non-Relational
Relational vs. Non-RelationalRelational vs. Non-Relational
Relational vs. Non-Relational
 
NoSQL: Why, When, and How
NoSQL: Why, When, and HowNoSQL: Why, When, and How
NoSQL: Why, When, and How
 
Lju Lazarevic
Lju LazarevicLju Lazarevic
Lju Lazarevic
 
Neo4j - graph database for recommendations
Neo4j - graph database for recommendationsNeo4j - graph database for recommendations
Neo4j - graph database for recommendations
 
An Introduction to NOSQL, Graph Databases and Neo4j
An Introduction to NOSQL, Graph Databases and Neo4jAn Introduction to NOSQL, Graph Databases and Neo4j
An Introduction to NOSQL, Graph Databases and Neo4j
 
Introduction to graph databases GraphDays
Introduction to graph databases  GraphDaysIntroduction to graph databases  GraphDays
Introduction to graph databases GraphDays
 
Introduction to Graph Databases
Introduction to Graph DatabasesIntroduction to Graph Databases
Introduction to Graph Databases
 
Data Mining: Graph mining and social network analysis
Data Mining: Graph mining and social network analysisData Mining: Graph mining and social network analysis
Data Mining: Graph mining and social network analysis
 

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

Net campus2015 antimomusone
Net campus2015 antimomusoneNet campus2015 antimomusone
Net campus2015 antimomusone
DotNetCampus
 

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

Designing and Building a Graph Database Application - Ian Robinson (Neo Techn...
Designing and Building a Graph Database Application - Ian Robinson (Neo Techn...Designing and Building a Graph Database Application - Ian Robinson (Neo Techn...
Designing and Building a Graph Database Application - Ian Robinson (Neo Techn...
 
Data modeling with neo4j tutorial
Data modeling with neo4j tutorialData modeling with neo4j tutorial
Data modeling with neo4j tutorial
 
20141216 graph database prototyping ams meetup
20141216 graph database prototyping ams meetup20141216 graph database prototyping ams meetup
20141216 graph database prototyping ams meetup
 
Building Applications with a Graph Database
Building Applications with a Graph DatabaseBuilding Applications with a Graph Database
Building Applications with a Graph Database
 
The power of polyglot searching
The power of polyglot searchingThe power of polyglot searching
The power of polyglot searching
 
Power of Polyglot Search
Power of Polyglot SearchPower of Polyglot Search
Power of Polyglot Search
 
Machine Learning for (JVM) Developers
Machine Learning for (JVM) DevelopersMachine Learning for (JVM) Developers
Machine Learning for (JVM) Developers
 
Alex mang patterns for scalability in microsoft azure application
Alex mang   patterns for scalability in microsoft azure applicationAlex mang   patterns for scalability in microsoft azure application
Alex mang patterns for scalability in microsoft azure application
 
Leveraging Lucene/Solr as a Knowledge Graph and Intent Engine
Leveraging Lucene/Solr as a Knowledge Graph and Intent EngineLeveraging Lucene/Solr as a Knowledge Graph and Intent Engine
Leveraging Lucene/Solr as a Knowledge Graph and Intent Engine
 
2018 data warehouse features in spark
2018   data warehouse features in spark2018   data warehouse features in spark
2018 data warehouse features in spark
 
Team Nation 2022 - How to choose between Dataverse, SQL Azure, SharePoint lis...
Team Nation 2022 - How to choose between Dataverse, SQL Azure, SharePoint lis...Team Nation 2022 - How to choose between Dataverse, SQL Azure, SharePoint lis...
Team Nation 2022 - How to choose between Dataverse, SQL Azure, SharePoint lis...
 
Salesforce Basic Development
Salesforce Basic DevelopmentSalesforce Basic Development
Salesforce Basic Development
 
Net campus2015 antimomusone
Net campus2015 antimomusoneNet campus2015 antimomusone
Net campus2015 antimomusone
 
PREDICT THE FUTURE , MACHINE LEARNING & BIG DATA
PREDICT THE FUTURE , MACHINE LEARNING & BIG DATAPREDICT THE FUTURE , MACHINE LEARNING & BIG DATA
PREDICT THE FUTURE , MACHINE LEARNING & BIG DATA
 
Deep-Dive into Deep Learning Pipelines with Sue Ann Hong and Tim Hunter
Deep-Dive into Deep Learning Pipelines with Sue Ann Hong and Tim HunterDeep-Dive into Deep Learning Pipelines with Sue Ann Hong and Tim Hunter
Deep-Dive into Deep Learning Pipelines with Sue Ann Hong and Tim Hunter
 
Continuous delivery for machine learning
Continuous delivery for machine learningContinuous delivery for machine learning
Continuous delivery for machine learning
 
Building Content Recommendation Systems Using Apache MXNet and Gluon - MCL402...
Building Content Recommendation Systems Using Apache MXNet and Gluon - MCL402...Building Content Recommendation Systems Using Apache MXNet and Gluon - MCL402...
Building Content Recommendation Systems Using Apache MXNet and Gluon - MCL402...
 
Polyalgebra
PolyalgebraPolyalgebra
Polyalgebra
 
Planning with Polyalgebra: Bringing Together Relational, Complex and Machine ...
Planning with Polyalgebra: Bringing Together Relational, Complex and Machine ...Planning with Polyalgebra: Bringing Together Relational, Complex and Machine ...
Planning with Polyalgebra: Bringing Together Relational, Complex and Machine ...
 
Efficient Rails Test Driven Development (class 3) by Wolfram Arnold
Efficient Rails Test Driven Development (class 3) by Wolfram ArnoldEfficient Rails Test Driven Development (class 3) by Wolfram Arnold
Efficient Rails Test Driven Development (class 3) by Wolfram Arnold
 

Plus de Neo4j

Plus de Neo4j (20)

Workshop - Best of Both Worlds_ Combine KG and Vector search for enhanced R...
Workshop - Best of Both Worlds_ Combine  KG and Vector search for  enhanced R...Workshop - Best of Both Worlds_ Combine  KG and Vector search for  enhanced R...
Workshop - Best of Both Worlds_ Combine KG and Vector search for enhanced R...
 
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...
 
QIAGEN: Biomedical Knowledge Graphs for Data Scientists and Bioinformaticians
QIAGEN: Biomedical Knowledge Graphs for Data Scientists and BioinformaticiansQIAGEN: Biomedical Knowledge Graphs for Data Scientists and Bioinformaticians
QIAGEN: Biomedical Knowledge Graphs for Data Scientists and Bioinformaticians
 
EY_Graph Database Powered Sustainability
EY_Graph Database Powered SustainabilityEY_Graph Database Powered Sustainability
EY_Graph Database Powered Sustainability
 
SIEMENS: RAPUNZEL – A Tale About Knowledge Graph
SIEMENS: RAPUNZEL – A Tale About Knowledge GraphSIEMENS: RAPUNZEL – A Tale About Knowledge Graph
SIEMENS: RAPUNZEL – A Tale About Knowledge Graph
 
Build your next Gen AI Breakthrough - April 2024
Build your next Gen AI Breakthrough - April 2024Build your next Gen AI Breakthrough - April 2024
Build your next Gen AI Breakthrough - April 2024
 
Connecting the Dots for Information Discovery.pdf
Connecting the Dots for Information Discovery.pdfConnecting the Dots for Information Discovery.pdf
Connecting the Dots for Information Discovery.pdf
 
ISDEFE - GraphSummit Madrid - ARETA: Aviation Real-Time Emissions Token Accre...
ISDEFE - GraphSummit Madrid - ARETA: Aviation Real-Time Emissions Token Accre...ISDEFE - GraphSummit Madrid - ARETA: Aviation Real-Time Emissions Token Accre...
ISDEFE - GraphSummit Madrid - ARETA: Aviation Real-Time Emissions Token Accre...
 
BBVA - GraphSummit Madrid - Caso de éxito en BBVA: Optimizando con grafos
BBVA - GraphSummit Madrid - Caso de éxito en BBVA: Optimizando con grafosBBVA - GraphSummit Madrid - Caso de éxito en BBVA: Optimizando con grafos
BBVA - GraphSummit Madrid - Caso de éxito en BBVA: Optimizando con grafos
 
Graph Everywhere - Josep Taruella - Por qué Graph Data Science en tus modelos...
Graph Everywhere - Josep Taruella - Por qué Graph Data Science en tus modelos...Graph Everywhere - Josep Taruella - Por qué Graph Data Science en tus modelos...
Graph Everywhere - Josep Taruella - Por qué Graph Data Science en tus modelos...
 
GraphSummit Madrid - Product Vision and Roadmap - Luis Salvador Neo4j
GraphSummit Madrid - Product Vision and Roadmap - Luis Salvador Neo4jGraphSummit Madrid - Product Vision and Roadmap - Luis Salvador Neo4j
GraphSummit Madrid - Product Vision and Roadmap - Luis Salvador Neo4j
 
Neo4j_Exploring the Impact of Graph Technology on Financial Services.pdf
Neo4j_Exploring the Impact of Graph Technology on Financial Services.pdfNeo4j_Exploring the Impact of Graph Technology on Financial Services.pdf
Neo4j_Exploring the Impact of Graph Technology on Financial Services.pdf
 
Rabobank_Exploring the Impact of Graph Technology on Financial Services.pdf
Rabobank_Exploring the Impact of Graph Technology on Financial Services.pdfRabobank_Exploring the Impact of Graph Technology on Financial Services.pdf
Rabobank_Exploring the Impact of Graph Technology on Financial Services.pdf
 
Webinar - IA generativa e grafi Neo4j: RAG time!
Webinar - IA generativa e grafi Neo4j: RAG time!Webinar - IA generativa e grafi Neo4j: RAG time!
Webinar - IA generativa e grafi Neo4j: RAG time!
 
IA Generativa y Grafos de Neo4j: RAG time
IA Generativa y Grafos de Neo4j: RAG timeIA Generativa y Grafos de Neo4j: RAG time
IA Generativa y Grafos de Neo4j: RAG time
 
Neo4j: Data Engineering for RAG (retrieval augmented generation)
Neo4j: Data Engineering for RAG (retrieval augmented generation)Neo4j: Data Engineering for RAG (retrieval augmented generation)
Neo4j: Data Engineering for RAG (retrieval augmented generation)
 
Neo4j Graph Summit 2024 Workshop - EMEA - Breda_and_Munchen.pdf
Neo4j Graph Summit 2024 Workshop - EMEA - Breda_and_Munchen.pdfNeo4j Graph Summit 2024 Workshop - EMEA - Breda_and_Munchen.pdf
Neo4j Graph Summit 2024 Workshop - EMEA - Breda_and_Munchen.pdf
 
Enabling GenAI Breakthroughs with Knowledge Graphs
Enabling GenAI Breakthroughs with Knowledge GraphsEnabling GenAI Breakthroughs with Knowledge Graphs
Enabling GenAI Breakthroughs with Knowledge Graphs
 
Neo4j_Anurag Tandon_Product Vision and Roadmap.Benelux.pptx.pdf
Neo4j_Anurag Tandon_Product Vision and Roadmap.Benelux.pptx.pdfNeo4j_Anurag Tandon_Product Vision and Roadmap.Benelux.pptx.pdf
Neo4j_Anurag Tandon_Product Vision and Roadmap.Benelux.pptx.pdf
 
Neo4j Jesus Barrasa The Art of the Possible with Graph
Neo4j Jesus Barrasa The Art of the Possible with GraphNeo4j Jesus Barrasa The Art of the Possible with Graph
Neo4j Jesus Barrasa The Art of the Possible with Graph
 

Dernier

Why Teams call analytics are critical to your entire business
Why Teams call analytics are critical to your entire businessWhy Teams call analytics are critical to your entire business
Why Teams call analytics are critical to your entire business
panagenda
 
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers:  A Deep Dive into Serverless Spatial Data and FMECloud Frontiers:  A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
Safe Software
 

Dernier (20)

Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot TakeoffStrategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
 
Emergent Methods: Multi-lingual narrative tracking in the news - real-time ex...
Emergent Methods: Multi-lingual narrative tracking in the news - real-time ex...Emergent Methods: Multi-lingual narrative tracking in the news - real-time ex...
Emergent Methods: Multi-lingual narrative tracking in the news - real-time ex...
 
Polkadot JAM Slides - Token2049 - By Dr. Gavin Wood
Polkadot JAM Slides - Token2049 - By Dr. Gavin WoodPolkadot JAM Slides - Token2049 - By Dr. Gavin Wood
Polkadot JAM Slides - Token2049 - By Dr. Gavin Wood
 
Why Teams call analytics are critical to your entire business
Why Teams call analytics are critical to your entire businessWhy Teams call analytics are critical to your entire business
Why Teams call analytics are critical to your entire business
 
TrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law DevelopmentsTrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
 
Manulife - Insurer Transformation Award 2024
Manulife - Insurer Transformation Award 2024Manulife - Insurer Transformation Award 2024
Manulife - Insurer Transformation Award 2024
 
GenAI Risks & Security Meetup 01052024.pdf
GenAI Risks & Security Meetup 01052024.pdfGenAI Risks & Security Meetup 01052024.pdf
GenAI Risks & Security Meetup 01052024.pdf
 
Ransomware_Q4_2023. The report. [EN].pdf
Ransomware_Q4_2023. The report. [EN].pdfRansomware_Q4_2023. The report. [EN].pdf
Ransomware_Q4_2023. The report. [EN].pdf
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected Worker
 
Boost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdfBoost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdf
 
AXA XL - Insurer Innovation Award Americas 2024
AXA XL - Insurer Innovation Award Americas 2024AXA XL - Insurer Innovation Award Americas 2024
AXA XL - Insurer Innovation Award Americas 2024
 
MINDCTI Revenue Release Quarter One 2024
MINDCTI Revenue Release Quarter One 2024MINDCTI Revenue Release Quarter One 2024
MINDCTI Revenue Release Quarter One 2024
 
Repurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost Saving
Repurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost SavingRepurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost Saving
Repurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost Saving
 
"I see eyes in my soup": How Delivery Hero implemented the safety system for ...
"I see eyes in my soup": How Delivery Hero implemented the safety system for ..."I see eyes in my soup": How Delivery Hero implemented the safety system for ...
"I see eyes in my soup": How Delivery Hero implemented the safety system for ...
 
A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?
 
Artificial Intelligence Chap.5 : Uncertainty
Artificial Intelligence Chap.5 : UncertaintyArtificial Intelligence Chap.5 : Uncertainty
Artificial Intelligence Chap.5 : Uncertainty
 
presentation ICT roal in 21st century education
presentation ICT roal in 21st century educationpresentation ICT roal in 21st century education
presentation ICT roal in 21st century education
 
Data Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt RobisonData Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt Robison
 
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
 
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers:  A Deep Dive into Serverless Spatial Data and FMECloud Frontiers:  A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
 

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

  • 1. Designing  and  Building  a  Graph   Database  Applica5on   Twi9er:  @ianSrobinson   #neo4j    
  • 2. Outline   •  Data  modeling  with  graphs   •  Neo4j  applica5on  architecture  op5ons   •  Tes5ng  your  data  model    
  • 5. Models   Purposeful  abstrac5on  of  a  domain  designed  to   sa5sfy  par5cular  applica5on/end-­‐user  goals   Images:  en.wikipedia.org  
  • 6. Example  Applica5on   •  Knowledge  management   –  People,  companies,  skills   –  Cross  organiza5onal   •  Find  my  professional  social  network   –  Exchange  knowledge   –  Interest  groups   –  Help   –  Staff  projects  
  • 7. Applica5on/End-­‐User  Goals   As  an  emp   loyee   I  want  to  k now  who  i n  the  comp has  similar any    skills  to  m   e   So  that  we  can  excha nge  knowl edge  
  • 8. Ques5ons  To  Ask  of  the  Domain   As  an  emp loyee     I  want  to  k now  who  i n  the  co has  similar   skills  to  me mpany       So  that  we  can  excha nge  knowle dge   Which  people,  who  work  for  the  same  company   as  me,  have  similar  skills  to  me?  
  • 9. Iden5fy  En55es   Which  people,  who  work  for  the  same  company   as  me,  have  similar  skills  to  me?     Person   Company   Skill  
  • 10. Iden5fy  Rela5onships  Between  En55es   Which  people,  who  work  for  the  same  company   as  me,  have  similar  skills  to  me?     Person  WORKS_FOR  Company   Person  HAS_SKILL  Skill  
  • 11. Convert  to  Cypher  Paths   Rela5onship   Person  WORKS_FOR  Company   Person  HAS_SKILL  Skill   Label   (:Person)-[:WORKS_FOR]->(:Company), (:Person)-[:HAS_SKILL]->(:Skill)
  • 13. Candidate  Data  Model   (:Company)<-[:WORKS_FOR]-(:Person)-[:HAS_SKILL]->(:Skill)
  • 14. Express  Ques5on  as  Graph  Pa9ern   Which  people,  who  work  for  the  same  company   as  me,  have  similar  skills  to  me?  
  • 15. Cypher  Query   Which  people,  who  work  for  the  same  company   as  me,  have  similar  skills  to  me?   MATCH (company)<-[:WORKS_FOR]-(me:Person)-[:HAS_SKILL]->(skill), (company)<-[:WORKS_FOR]-(colleague)-[:HAS_SKILL]->(skill) WHERE me.name = {name} RETURN colleague.name AS name, count(skill) AS score, collect(skill.name) AS skills ORDER BY score DESC
  • 16. Graph  Pa9ern   Which  people,  who  work  for  the  same  company   as  me,  have  similar  skills  to  me?   MATCH (company)<-[:WORKS_FOR]-(me:Person)-[:HAS_SKILL]->(skill), (company)<-[:WORKS_FOR]-(colleague)-[:HAS_SKILL]->(skill) WHERE me.name = {name} RETURN colleague.name AS name, count(skill) AS score, collect(skill.name) AS skills ORDER BY score DESC
  • 17. Anchor  Pa9ern  in  Graph   Which  people,  who  work  for  the  same  company   as  me,  have  similar  skills  to  me?   MATCH (company)<-[:WORKS_FOR]-(me:Person)-[:HAS_SKILL]->(skill), (company)<-[:WORKS_FOR]-(colleague)-[:HAS_SKILL]->(skill) WHERE me.name = {name} RETURN colleague.name AS name, count(skill) AS score, collect(skill.name) AS skills ORDER BY score DESC Search  nodes  labeled   ‘Person’,  matching  on   ‘name’  property  
  • 18. Create  Projec5on  of  Results   Which  people,  who  work  for  the  same  company   as  me,  have  similar  skills  to  me?   MATCH (company)<-[:WORKS_FOR]-(me:Person)-[:HAS_SKILL]->(skill), (company)<-[:WORKS_FOR]-(colleague)-[:HAS_SKILL]->(skill) WHERE me.name = {name} RETURN colleague.name AS name, count(skill) AS score, collect(skill.name) AS skills ORDER BY score DESC
  • 22. Running  the  Query   +-----------------------------------+ | name | score | skills | +-----------------------------------+ | "Lucy" | 2 | ["Java","Neo4j"] | | "Bill" | 1 | ["Neo4j"] | +-----------------------------------+ 2 rows
  • 23. From  User  Story  to  Model  and  Query   As  an  emp loyee     I  want  to  k now  who  i n  the  co has  similar   skills  to  me mpany       So  that  we  can  excha nge  knowle dge   MATCH (company)<-[:WORKS_FOR]-(me:Person)-[:HAS_SKILL]->(skill), (company)<-[:WORKS_FOR]-(colleague)-[:HAS_SKILL]->(skill) WHERE me.name = {name} RETURN colleague.name AS name, count(skill) AS score, collect(skill.name) AS skills ORDER BY score DESC ? Which  people,  who  work  for  the  same   company  as  me,  have  similar  skills  to  me? Person  WORKS_FOR  Company   Person  HAS_SKILL  Skill (:Company)<-[:WORKS_FOR]-(:Person)-[:HAS_SKILL]->(:Skill)
  • 25. Why  Test?   •  Ensure  model  is  fit  for  queries   –  Rapid  feedback   •  Ensure  correctness  of  queries   •  Document  your  understanding  of  your  domain   –  Including  corner  cases  and  excep5ons   •  Provide  a  regression  test  suite   –  Allows  you  to  change  and  evolve  model  and   queries  
  • 26. Method   •  Develop  queries,  or  classes  that  encapsulate   queries,  using  unit  tests   •  Use  small,  well-­‐understood  datasets  in  each  test   –  Create  data  in  test  setup   –  Test  dataset  expresses  your  understanding  of  (part  of)   the  domain   •  Inject  in-­‐memory  graph  database  (or  Cypher   engine)  into  object  under  test   •  The  exact  strategy  you  use  depends  on  your   applica5on  architecture…  
  • 27. Applica5on  Architectures   •  Embedded   •  Server   •  Server  with  Extensions  
  • 28. Applica5on  Architectures   •  Embedded   –  Host  in  Java  process   –  Access  to  Java  APIs   •  Server   •  Server  with  Extensions   Applica5on   Java  APIs  
  • 29. Applica5on  Architectures   •  Embedded   •  Server   –  HTTP/JSON  interface   –  Server  wraps  embedded   instance   •  Server  with  Extensions   Applica5on   REST  Client   Write  LB   REST  API   REST  API   Read  LB   REST  API  
  • 30. Applica5on  Architectures   •  Embedded   •  Server   •  Server  with  Extensions   REST  API   –  Execute  complex  logic  on  server   –  Control  HTTP  request/response  format   Extensions  
  • 31. Embedded  Example   •  Company  social  network   •  Find  colleagues  with  similar  skills   •  Encapsulate  query  in  a  ColleagueFinder
  • 32. Unit  Test  Fixture   public class ColleagueFinderTest { private GraphDatabaseService db; private ColleagueFinder finder; @Before public void init() { db = new TestGraphDatabaseFactory().newImpermanentDatabase(); ExampleGraph.populate( db ); finder = new ColleagueFinder( new ExecutionEngine( db ) ); } @After public void shutdown() { db.shutdown(); } }
  • 33. Create  Database   public class ColleagueFinderTest { private GraphDatabaseService db; private ColleagueFinder finder; @Before public void init() { db = new TestGraphDatabaseFactory().newImpermanentDatabase(); ExampleGraph.populate( db ); finder = new ColleagueFinder( new ExecutionEngine( db ) ); } @After public void shutdown() { db.shutdown(); } }
  • 34. Populate  Graph   public class ColleagueFinderTest { private GraphDatabaseService db; private ColleagueFinder finder; @Before public void init() { db = new TestGraphDatabaseFactory().newImpermanentDatabase(); ExampleGraph.populate( db ); finder = new ColleagueFinder( new ExecutionEngine( db ) ); } @After public void shutdown() { db.shutdown(); } }
  • 35. Create  Object  Under  Test   public class ColleagueFinderTest { private GraphDatabaseService db; private ColleagueFinder finder; @Before public void init() { db = new TestGraphDatabaseFactory().newImpermanentDatabase(); ExampleGraph.populate( db ); finder = new ColleagueFinder( new ExecutionEngine( db ) ); } @After public void shutdown() { db.shutdown(); } } Inject     Execu5onEngine  
  • 36. ImpermanentGraphDatabase   •  In-­‐memory   •  For  tes5ng  only,  not  produc5on!   <dependency> <groupId>org.neo4j</groupId> <artifactId>neo4j-kernel</artifactId> <version>${project.version}</version> <type>test-jar</type> <scope>test</scope> </dependency>
  • 37. Create  Sample  Data   public static void populate( GraphDatabaseService db ) { ExecutionEngine engine = new ExecutionEngine( db ); String cypher = "CREATE ian:Person VALUES {name:'Ian'},n" + " bill:Person VALUES {name:'Bill'},n" + " lucy:Person VALUES {name:'Lucy'},n" + " acme:Company VALUES {name:'Acme'},n" + // Cypher continues... " " " " (bill)-[:HAS_SKILL]->(neo4j),n" + (bill)-[:HAS_SKILL]->(ruby),n" + (lucy)-[:HAS_SKILL]->(java),n" + (lucy)-[:HAS_SKILL]->(neo4j)"; engine.execute( cypher ); }
  • 38. Unit  Test   @Test public void shouldFindColleaguesWithSimilarSkills() throws Exception { // when Iterator<Map<String, Object>> results = finder.findColleaguesFor( "Ian" ); // then assertEquals( "Lucy", results.next().get( "name" ) ); assertEquals( "Bill", results.next().get( "name" ) ); assertFalse( results.hasNext() ); }
  • 39. Execute  Object  Under  Test   @Test public void shouldFindColleaguesWithSimilarSkills() throws Exception { // when Iterator<Map<String, Object>> results = finder.findColleaguesFor( "Ian" ); // then assertEquals( "Lucy", results.next().get( "name" ) ); assertEquals( "Bill", results.next().get( "name" ) ); assertFalse( results.hasNext() ); }
  • 40. Assert  Results   @Test public void shouldFindColleaguesWithSimilarSkills() throws Exception { // when Iterator<Map<String, Object>> results = finder.findColleaguesFor( "Ian" ); // then assertEquals( "Lucy", results.next().get( "name" ) ); assertEquals( "Bill", results.next().get( "name" ) ); assertFalse( results.hasNext() ); }
  • 41. Ensure  No  More  Results   @Test public void shouldFindColleaguesWithSimilarSkills() throws Exception { // when Iterator<Map<String, Object>> results = finder.findColleaguesFor( "Ian" ); // then assertEquals( "Lucy", results.next().get( "name" ) ); assertEquals( "Bill", results.next().get( "name" ) ); assertFalse( results.hasNext() ); }
  • 42. ColleagueFinder   public class ColleagueFinder { private final ExecutionEngine executionEngine; public ColleagueFinder( ExecutionEngine executionEngine ) { this.executionEngine = executionEngine; } public Iterator<Map<String, Object>> findColleaguesFor( String name ) { ... } }
  • 43. Inject  Execu5onEngine   public class ColleagueFinder { private final ExecutionEngine executionEngine; public ColleagueFinder( ExecutionEngine executionEngine ) { this.executionEngine = executionEngine; } public Iterator<Map<String, Object>> findColleaguesFor( String name ) { ... } }
  • 44. findColleaguesFor()  Method   public Iterator<Map<String, Object>> findColleaguesFor( String name ) { String cypher = "MATCH (me:Person)-[:WORKS_FOR]->(company),n" + " (me)-[:HAS_SKILL]->(skill),n" + " (colleague)-[:WORKS_FOR]->(company),n" + " (colleague)-[:HAS_SKILL]->(skill)n" + "WHERE me.name = {name}n" + "RETURN colleague.name AS name,n" + " count(skill) AS score,n" + " collect(skill.name) AS skillsn" + "ORDER BY score DESC"; Map<String, Object> params = new HashMap<String, Object>(); params.put( "name", name ); return executionEngine.execute( cypher, params ).iterator(); }
  • 45. Cypher  Query   public Iterator<Map<String, Object>> findColleaguesFor( String name ) { String cypher = "MATCH (me:Person)-[:WORKS_FOR]->(company),n" + " (me)-[:HAS_SKILL]->(skill),n" + " (colleague)-[:WORKS_FOR]->(company),n" + " (colleague)-[:HAS_SKILL]->(skill)n" + "WHERE me.name = {name}n" + "RETURN colleague.name AS name,n" + " count(skill) AS score,n" + " collect(skill.name) AS skillsn" + "ORDER BY score DESC"; Map<String, Object> params = new HashMap<String, Object>(); params.put( "name", name ); return executionEngine.execute( cypher, params ).iterator(); }
  • 46. Parameterized  Query   public Iterator<Map<String, Object>> findColleaguesFor( String name ) { String cypher = "MATCH (me:Person)-[:WORKS_FOR]->(company),n" + " (me)-[:HAS_SKILL]->(skill),n" + " (colleague)-[:WORKS_FOR]->(company),n" + " (colleague)-[:HAS_SKILL]->(skill)n" + "WHERE me.name = {name}n" + "RETURN colleague.name AS name,n" + " count(skill) AS score,n" + " collect(skill.name) AS skillsn" + "ORDER BY score DESC"; Map<String, Object> params = new HashMap<String, Object>(); params.put( "name", name ); return executionEngine.execute( cypher, params ).iterator(); }
  • 47. Execute  Query   public Iterator<Map<String, Object>> findColleaguesFor( String name ) { String cypher = "MATCH (me:Person)-[:WORKS_FOR]->(company),n" + " (me)-[:HAS_SKILL]->(skill),n" + " (colleague)-[:WORKS_FOR]->(company),n" + " (colleague)-[:HAS_SKILL]->(skill)n" + "WHERE me.name = {name}n" + "RETURN colleague.name AS name,n" + " count(skill) AS score,n" + " collect(skill.name) AS skillsn" + "ORDER BY score DESC"; Map<String, Object> params = new HashMap<String, Object>(); params.put( "name", name ); return executionEngine.execute( cypher, params ).iterator(); }
  • 48. Server  Extension  Example   •  Same  data  mode  and  query  as  before   •  This  5me,  we’ll  host  ColleagueFinder  in  a   server  extension  
  • 49. Server  Extension   @Path("/similar-skills") public class ColleagueFinderExtension { private static final ObjectMapper MAPPER = new ObjectMapper(); private final ColleagueFinder colleagueFinder; public ColleagueFinderExtension( @Context CypherExecutor cypherExecutor ) { this.colleagueFinder = new ColleagueFinder( cypherExecutor.getExecutionEngine() ); } @GET @Produces(MediaType.APPLICATION_JSON) @Path("/{name}") public Response getColleagues( @PathParam("name") String name ) throws IOException { String json = MAPPER .writeValueAsString( colleagueFinder.findColleaguesFor( name ) ); return Response.ok().entity( json ).build(); } }
  • 50. JAX-­‐RS  Annota5ons   @Path("/similar-skills") public class ColleagueFinderExtension { private static final ObjectMapper MAPPER = new ObjectMapper(); private final ColleagueFinder colleagueFinder; public ColleagueFinderExtension( @Context CypherExecutor cypherExecutor ) { this.colleagueFinder = new ColleagueFinder( cypherExecutor.getExecutionEngine() ); } @GET @Produces(MediaType.APPLICATION_JSON) @Path("/{name}") public Response getColleagues( @PathParam("name") String name ) throws IOException { String json = MAPPER .writeValueAsString( colleagueFinder.findColleaguesFor( name ) ); return Response.ok().entity( json ).build(); } }
  • 51. Map  HTTP  Request  to  Object  +  Method   @Path("/similar-skills") public class ColleagueFinderExtension { private static final ObjectMapper MAPPER = new ObjectMapper(); private final ColleagueFinder colleagueFinder; public ColleagueFinderExtension( @Context CypherExecutor cypherExecutor ) { this.colleagueFinder = new ColleagueFinder( cypherExecutor.getExecutionEngine() ); } @GET @Produces(MediaType.APPLICATION_JSON) @Path("/{name}") public Response getColleagues( @PathParam("name") String name ) throws IOException { String json = MAPPER .writeValueAsString( colleagueFinder.findColleaguesFor( name ) ); return Response.ok().entity( json ).build(); } } GET   /similar-­‐skills   /Ian  
  • 52. CypherExecutor  Injected  by  Server   Ensures   Execu5onEngine   reused  across   resource  instances   @Path("/similar-skills") public class ColleagueFinderExtension { private static final ObjectMapper MAPPER = new ObjectMapper(); private final ColleagueFinder colleagueFinder; public ColleagueFinderExtension( @Context CypherExecutor cypherExecutor ) { this.colleagueFinder = new ColleagueFinder( cypherExecutor.getExecutionEngine() ); } @GET @Produces(MediaType.APPLICATION_JSON) @Path("/{name}") public Response getColleagues( @PathParam("name") String name ) throws IOException { String json = MAPPER .writeValueAsString( colleagueFinder.findColleaguesFor( name ) ); return Response.ok().entity( json ).build(); } }
  • 53. Generate  and  Format  Response   @Path("/similar-skills") public class ColleagueFinderExtension { private static final ObjectMapper MAPPER = new ObjectMapper(); private final ColleagueFinder colleagueFinder; public ColleagueFinderExtension( @Context CypherExecutor cypherExecutor ) { this.colleagueFinder = new ColleagueFinder( cypherExecutor.getExecutionEngine() ); } @GET @Produces(MediaType.APPLICATION_JSON) @Path("/{name}") public Response getColleagues( @PathParam("name") String name ) throws IOException { String json = MAPPER .writeValueAsString( colleagueFinder.findColleaguesFor( name ) ); return Response.ok().entity( json ).build(); } }
  • 54. Extension  Test  Fixture   public class ColleagueFinderExtensionTest { private CommunityNeoServer server; @Before public void startServer() throws IOException { server = CommunityServerBuilder.server() .withThirdPartyJaxRsPackage( "org.neo4j.good_practices", "/colleagues" ) .build(); server.start(); ExampleGraph.populate( server.getDatabase().getGraph() ); } @After public void stopServer() { server.stop(); } }
  • 55. Build  and  Configure  Server   public class ColleagueFinderExtensionTest { private CommunityNeoServer server; @Before public void startServer() throws IOException { server = CommunityServerBuilder.server() .withThirdPartyJaxRsPackage( "org.neo4j.good_practices", "/colleagues" ) .build(); server.start(); ExampleGraph.populate( server.getDatabase().getGraph() ); } @After public void stopServer() { server.stop(); } }
  • 56. Start  Server   public class ColleagueFinderExtensionTest { private CommunityNeoServer server; @Before public void startServer() throws IOException { server = CommunityServerBuilder.server() .withThirdPartyJaxRsPackage( "org.neo4j.good_practices", "/colleagues" ) .build(); server.start(); ExampleGraph.populate( server.getDatabase().getGraph() ); } @After public void stopServer() { server.stop(); } }
  • 57. Populate  Database   public class ColleagueFinderExtensionTest { private CommunityNeoServer server; @Before public void startServer() throws IOException { server = CommunityServerBuilder.server() .withThirdPartyJaxRsPackage( "org.neo4j.good_practices", "/colleagues" ) .build(); server.start(); ExampleGraph.populate( server.getDatabase().getGraph() ); } @After public void stopServer() { server.stop(); } }
  • 58. CommunityServerBuilder   •  Programma5c  configura5on   <dependency> <groupId>org.neo4j.app</groupId> <artifactId>neo4j-server</artifactId> <version>${project.version}</version> <type>test-jar</type> </dependency>
  • 59. Tes5ng  Extension  Using  HTTP  Client   @Test public void shouldReturnColleaguesWithSimilarSkills() throws Exception { Client client = Client.create( new DefaultClientConfig() ); WebResource resource = client .resource( "http://localhost:7474/colleagues/similar-skills/Ian" ); ClientResponse response = resource .accept( MediaType.APPLICATION_JSON ) .get( ClientResponse.class ); List<Map<String, Object>> results = new ObjectMapper() .readValue(response.getEntity( String.class ), List.class ); // Assertions ...
  • 60. Create  HTTP  Client   @Test public void shouldReturnColleaguesWithSimilarSkills() throws Exception { Client client = Client.create( new DefaultClientConfig() ); WebResource resource = client .resource( "http://localhost:7474/colleagues/similar-skills/Ian" ); ClientResponse response = resource .accept( MediaType.APPLICATION_JSON ) .get( ClientResponse.class ); List<Map<String, Object>> results = new ObjectMapper() .readValue(response.getEntity( String.class ), List.class ); // Assertions ...
  • 61. Issue  Request   @Test public void shouldReturnColleaguesWithSimilarSkills() throws Exception { Client client = Client.create( new DefaultClientConfig() ); WebResource resource = client .resource( "http://localhost:7474/colleagues/similar-skills/Ian" ); ClientResponse response = resource .accept( MediaType.APPLICATION_JSON ) .get( ClientResponse.class ); List<Map<String, Object>> results = new ObjectMapper() .readValue(response.getEntity( String.class ), List.class ); // Assertions ...
  • 62. Parse  Response   @Test public void shouldReturnColleaguesWithSimilarSkills() throws Exception { Client client = Client.create( new DefaultClientConfig() ); WebResource resource = client .resource( "http://localhost:7474/colleagues/similar-skills/Ian" ); ClientResponse response = resource .accept( MediaType.APPLICATION_JSON ) .get( ClientResponse.class ); List<Map<String, Object>> results = new ObjectMapper() .readValue(response.getEntity( String.class ), List.class ); // Assertions ...
  • 63. Assert  Results   ... assertEquals( 200, response.getStatus() ); assertEquals( MediaType.APPLICATION_JSON, response.getHeaders().get( "Content-Type" ).get( 0 ) ); assertEquals( "Lucy", results.get( 0 ).get( "name" ) ); assertThat( (Iterable<String>) results.get( 0 ).get( "skills" ), hasItems( "Java", "Neo4j" ) ); }
  • 64. ts gy en lo i m no pl h m ec Co eo T N of Graph h Databases Thank  you   Twi9er:  @ianSrobinson   #neo4j         Ian Robinson, Jim Webber & Emil Eifrem github.com/iansrobinson/neo4j-­‐good-­‐prac5ces