Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Extensibility for Java Developers in Neo4j

Extensibility for Java Developers in Neo4j

In this presentation, you will find out all the different ways you can extend Neo4j and easily integrate it with other popular technologies! From GraphQL to in-house applications to Spring Data, you can utilize your existing technology stack and plug it into Neo4j to maximize business value from your data as a graph of connected information.

Jennifer Reif

October 10, 2018
Tweet

More Decks by Jennifer Reif

Other Decks in Technology

Transcript

  1. Software Engineer, Neo4j - Developer - Blogger - Conference speaker

    - Continuous learner Hobbies: cats, coffee, and traveling :) [email protected] @JMHReif Who Am I?
  2. Database - specifically graph • Database: a structured set of

    data held in a computer, especially one that is accessible in various ways. • Relational? NoSQL? Graph? • Graph database: uses graph structures for semantic queries with nodes, edges and properties to represent and store data.
  3. Neo4j Fast Reliable No size limit Binary & HTTP protocol

    ACID transactions 2-4 M
 ops/s per core Clustering scale & HA Official Drivers Neo4j is a database
  4. Neo4j Native GraphDB Cypher Query Language Developer Workbench Extensible Procedures

    & Functions Schema Free Property Graph Model Graph Visualization Graph Storage Neo4j is a graph database
  5. Neo4j Developer Surface Native LanguageDrivers BOLT User- Defined Procedure 2000-2010

    0.x Embedded Java API 2010-2014 1.x REST 2014-2015 2.x Cypher over HTTP 2016 3.0.x Bolt, Official Language Drivers, User-Defined Procedures 2016 3.1.x User-Defined Functions 2017 3.2.x User-Defined Aggregation Functions
  6. Neo4j Developer Surface Native LanguageDrivers BOLT User- Defined Procedure 2000-2010

    0.x Embedded Java API 2010-2014 1.x REST 2014-2015 2.x Cypher over HTTP 2016 3.0.x Bolt, Official Language Drivers, User Defined Procedures 2016 3.1.x User Defined Functions 2017 3.2.x User Defined Aggregation Functions
  7. Interfaces to Neo4j • BOLT: • binary protocol / Cypher-only

    pipe • type system similar to Cypher • official drivers • HTTP: • familiar protocol • JSON (lossy) • community drivers / standard HTTP tooling • Embedded: • Java-only • core API • in-process
  8. Bolt Binary Protocol • High-performance protocol • Secure transport (auth/TLS)

    • Shared connections (pooling) • More inclusive data type system • Procedure functionality
  9. Java Driver import org.neo4j.driver.v1.*; Driver driver = GraphDatabase.driver( "bolt://localhost", AuthTokens.basic(

    "neo4j", "neo4j" ) ); Session session = driver.session(); session.run( "CREATE (a:Person {name:'Arthur', title:'King'})" ); StatementResult result = session.run( "MATCH (a:Person) WHERE a.name = 'Arthur' RETURN a.name AS name, a.title AS title" ); while ( result.hasNext() ) { Record record = result.next(); System.out.println( record.get( "title" ).asString() + " " + record.get("name").asString() ); } session.close(); driver.close(); github.com/neo4j/neo4j-java-driver / org.neo4j.driver:neo4j-java-driver:1.5.0
  10. Javascript Driver var neo4j = require('neo4j-driver').v1; var driver = neo4j.driver("bolt://localhost",

    neo4j.auth.basic("neo4j", "neo4j")); var session = driver.session(); session .run( "CREATE (a:Person {name:'Arthur', title:'King'})" ) .then( function() { return session.run( "MATCH (a:Person) WHERE a.name = 'Arthur' RETURN a.name AS name, a.title AS title" ) }) .then( function( result ) { console.log( result.records[0].get("title") + " " + result.records[0].get("name") ); session.close(); driver.close(); }) github.com/neo4j/neo4j-javascript-driver / npm install neo4j-driver
  11. Python Driver from neo4j.v1 import GraphDatabase, basic_auth driver = GraphDatabase.driver("bolt://localhost",

    auth=basic_auth("neo4j", "neo4j")) session = driver.session() session.run("CREATE (a:Person {name:'Arthur', title:'King'})") query = "MATCH (a:Person) WHERE a.name = 'Arthur' RETURN a.name AS name, a.title AS title" result = session.run(query) for record in result: print("%s %s" % (record["title"], record["name"])) session.close() github.com/neo4j/neo4j-python-driver / pip install neo4j-driver
  12. .NET Driver using Neo4j.Driver.V1; using (var driver = GraphDatabase.Driver("bolt://localhost", AuthTokens.Basic("neo4j",

    "neo4j"))) using (var session = driver.Session()) { session.Run("CREATE (a:Person {name:'Arthur', title:'King'})"); var result = session.Run("MATCH (a:Person) WHERE a.name = 'Arthur' RETURN a.name AS name, a.title AS title"); foreach (var record in result) { Output.WriteLine($"{record["title"].As<string>()} {record["name"].As<string>()}"); } } github.com/neo4j/neo4j-dotnet-driver / PM> Install-Package Neo4j.Driver-1.0.3
  13. Capabilities • Can use with SpringBoot • Annotation-based OGM for

    POJOs • Connect to Neo4j with Bolt, HTTP, and embedded • Interface-based repository support • Persistence lifecycle events
  14. @NodeEntity public class Person { @Id @GeneratedValue private Long id;

    private String name; private int born; @Relationship(type = "ACTED_IN") private List<Movie> movies = new ArrayList<>(); } @NodeEntity public class Movie { … @JsonIgnoreProperties("movie") @Relationship(type = "ACTED_IN", direction = Relationship.INCOMING) private List<Role> roles; } @RelationshipEntity(type = "ACTED_IN") public class Role { @Id @GeneratedValue private Long id; private List<String> roles = new ArrayList<>(); @StartNode private Person person; @EndNode private Movie movie; }
  15. public interface MovieRepository extends Neo4jRepository<Movie, Long> { //derived finder methods

    - auto-mapping Movie findByTitle(@Param("title") String title); Collection<Movie> findByTitleLike(@Param("title") String title); @Query("MATCH (m:Movie)<-[r:ACTED_IN]-(a:Person) RETURN m,r,a LIMIT {limit}") Collection<Movie> graph(@Param("limit") int limit); }
  16. @Service public class MovieService { private final MovieRepository movieRepository; public

    MovieService(MovieRepository movieRepository) { this.movieRepository = movieRepository; } @Transactional(readOnly = true) public Movie findByTitle(String title) { return movieRepository.findByTitle(title); } @Transactional(readOnly = true) public Collection<Movie> findByTitleLike(String title) { return movieRepository.findByTitleLike(title); } @Transactional(readOnly = true) public Map<String, Object> graph(int limit) { Collection<Movie> result = movieRepository.graph(limit); return toD3Format(result); } } https://neo4j.com/developer/spring-data-neo4j/
  17. Neo4j Developer Surface Native LanguageDrivers BOLT User- Defined Procedure 2000-2010

    0.x Embedded Java API 2010-2014 1.x REST 2014-2015 2.x Cypher over HTTP 2016 3.0.x Bolt, Official Language Drivers, User-Defined Procedures 2016 3.1.x User-Defined Functions 2017 3.2.x User-Defined Aggregation Functions
  18. public class FullTextIndex { @Context public GraphDatabaseService db; @Procedure( name

    = "example.search", mode = Procedure.Mode.READ ) public Stream<SearchHit> search( @Name("index") String index, 
 @Name("query") String query ) { if( !db.index().existsForNodes( index )) { return Stream.empty(); } return db.index().forNodes( index ).query( query ).stream()
 .map( SearchHit::new ); } public static class SearchHit { public final Node node; SearchHit(Node node) { this.node = node; } } }
  19. public class FullTextIndex { @Context public GraphDatabaseService db; @Procedure( name

    = "example.search", mode = Procedure.Mode.READ ) public Stream<SearchHit> search( @Name("index") String index, 
 @Name("query") String query ) { if( !db.index().existsForNodes( index )) { return Stream.empty(); } return db.index().forNodes( index ).query( query ).stream()
 .map( SearchHit::new ); } public static class SearchHit { public final Node node; SearchHit(Node node) { this.node = node; } } }
  20. public class FullTextIndex { @Context public GraphDatabaseService db; @Procedure( name

    = "example.search", mode = Procedure.Mode.READ ) public Stream<SearchHit> search( @Name("index") String index, 
 @Name("query") String query ) { if( !db.index().existsForNodes( index )) { return Stream.empty(); } return db.index().forNodes( index ).query( query ).stream()
 .map( SearchHit::new ); } public static class SearchHit { public final Node node; SearchHit(Node node) { this.node = node; } } }
  21. try ( Driver driver = GraphDatabase.driver("bolt://localhost", Config.build().toConfig()) ) { try

    ( Session session = driver.session() ) { String call = "CALL example.search('User',$query)"; Map<String,Object> params = singletonMap("query", "name:Brook*"); StatementResult result = session.run(call, params); while ( result.hasNext() ) { // process results } } }
  22. try ( Driver driver = GraphDatabase.driver("bolt://localhost", Config.build().toConfig()) ) { try

    ( Session session = driver.session() ) { String call = "CALL example.search('User',$query)"; Map<String,Object> params = singletonMap("query", "name:Brook*"); StatementResult result = session.run(call, params); while ( result.hasNext() ) { // process results } } }
  23. public class Join { @UserFunction @Description("example.join(['s1','s2',...], delimiter) - join the

    given strings with the given delimiter.") public String join( @Name(“strings") List<String> strings, @Name(value = "delimiter", defaultValue = ",") String delimiter) { if ( strings == null || delimiter == null ) { return null; } return String.join( delimiter, strings ); } }
  24. public class Join { @UserFunction @Description("example.join(['s1','s2',...], delimiter) - join the

    given strings with the given delimiter.") public String join( @Name("strings") List<String> strings, @Name(value = "delimiter", defaultValue = ",") String delimiter) { if ( strings == null || delimiter == null ) { return null; } return String.join( delimiter, strings ); } }
  25. public class Join { @UserFunction @Description("example.join(['s1','s2',...], delimiter) - join the

    given strings with the given delimiter.") public String join( @Name("strings") List<String> strings, @Name(value = "delimiter", defaultValue = ",") String delimiter) { if ( strings == null || delimiter == null ) { return null; } return String.join( delimiter, strings ); } }
  26. try ( Driver driver = GraphDatabase.driver( "bolt://localhost", 
 Config.build().toConfig() )

    ) { try ( Session session = driver.session() ) { String query = "RETURN example.join(['Hello','World']) AS result"; String result = session.run( query ) .single().get( "result" ).asString(); } }
  27. public class LongestString { @UserAggregationFunction @Description( "aggregates the longest string

    found" ) public LongStringAggregator longestString() { return new LongStringAggregator(); } public static class LongStringAggregator { private int longest; private String longestString; @UserAggregationUpdate public void findLongest( @Name( "string" ) String string ) { if ( string != null && string.length() > longest) { longest = string.length(); longestString = string; } } @UserAggregationResult public String result() { return longestString; } } }
  28. public class LongestString { @UserAggregationFunction @Description( "aggregates the longest string

    found" ) public LongStringAggregator longestString() { return new LongStringAggregator(); } public static class LongStringAggregator { private int longest; private String longestString; @UserAggregationUpdate public void findLongest( @Name( "string" ) String string ) { if ( string != null && string.length() > longest) { longest = string.length(); longestString = string; } } @UserAggregationResult public String result() { return longestString; } } }
  29. public class LongestString { @UserAggregationFunction @Description( "aggregates the longest string

    found" ) public LongStringAggregator longestString() { return new LongStringAggregator(); } public static class LongStringAggregator { private int longest; private String longestString; @UserAggregationUpdate public void findLongest( @Name( "string" ) String string ) { if ( string != null && string.length() > longest) { longest = string.length(); longestString = string; } } @UserAggregationResult public String result() { return longestString; } } }
  30. public class LongestString { @UserAggregationFunction @Description( "aggregates the longest string

    found" ) public LongStringAggregator longestString() { return new LongStringAggregator(); } public static class LongStringAggregator { private int longest; private String longestString; @UserAggregationUpdate public void findLongest( @Name( "string" ) String string ) { if ( string != null && string.length() > longest) { longest = string.length(); longestString = string; } } @UserAggregationResult public String result() { return longestString; } } }
  31. try ( Driver driver = GraphDatabase.driver("bolt://localhost", 
 Config.build().toConfig()) ) {

    try ( Session session = driver.session() ) { String query = "UNWIND ['abc', 'abcd', 'ab'] AS string " + "RETURN example.longestString(string) AS result"; String result = session.run(query).single().get("result").asString(); } }
  32. apoc.load.jdbc WITH "jdbc:mysql://localhost:3306/northwind?user=root" AS url CALL apoc.load.jdbc(url,"products") 
 YIELD row

    MERGE (p:Product {id: row.ProductID}) SET p.name = row.ProductName, p.unitPrice = row.UnitPrice
  33. WITH "jdbc:mysql://localhost:3306/northwind?user=root" AS url CALL apoc.load.jdbc(url,"products") 
 YIELD row MERGE

    (p:Product {id: row.ProductID}) SET p.name = row.ProductName, p.unitPrice = row.UnitPrice Execute procedure
  34. Apply Cypher transformation WITH "jdbc:mysql://localhost:3306/northwind?user=root" AS url CALL apoc.load.jdbc(url,"products") 


    YIELD row MERGE (p:Product {id: row.ProductID}) SET p.name = row.ProductName, p.unitPrice = row.UnitPrice
  35. apoc.load.xml CALL apoc.load.xml('http://overpass.osm.rambler.ru/cgi/xapi_meta *[bbox=11.54,48.14,11.543,48.145]') YIELD value UNWIND value["_children"] AS child

    WITH child WHERE child["_type"] = "node" WITH child.id AS id, 
 child.lat AS latitude, 
 child.lon AS longitude, 
 child["user"] AS userName MERGE (point:Point {id: id}) SET point.latitude = latitude, 
 point.longitude = longitude MERGE (user:User {name: userName}) MERGE (user)-[:EDITED]->(point)
  36. Execute procedure CALL apoc.load.xml('http://overpass.osm.rambler.ru/cgi/xapi_meta? *[bbox=11.54,48.14,11.543,48.145]') YIELD value UNWIND value["_children"] AS

    child WITH child WHERE child["_type"] = "node" WITH child.id AS id, 
 child.lat AS latitude, 
 child.lon AS longitude, 
 child["user"] AS userName MERGE (point:Point {id: id}) SET point.latitude = latitude, 
 point.longitude = longitude MERGE (user:User {name: userName}) MERGE (user)-[:EDITED]->(point)
  37. UNWIND the array of elements CALL apoc.load.xml('http://overpass.osm.rambler.ru/cgi/xapi_meta? *[bbox=11.54,48.14,11.543,48.145]') YIELD value

    UNWIND value["_children"] AS child WITH child WHERE child["_type"] = "node" WITH child.id AS id, 
 child.lat AS latitude, 
 child.lon AS longitude, 
 child["user"] AS userName MERGE (point:Point {id: id}) SET point.latitude = latitude, 
 point.longitude = longitude MERGE (user:User {name: userName}) MERGE (user)-[:EDITED]->(point)
  38. Filter rows CALL apoc.load.xml('http://overpass.osm.rambler.ru/cgi/xapi_meta? *[bbox=11.54,48.14,11.543,48.145]') YIELD value UNWIND value["_children"] AS

    child WITH child WHERE child["_type"] = "node" WITH child.id AS id, 
 child.lat AS latitude, 
 child.lon AS longitude, 
 child["user"] AS userName MERGE (point:Point {id: id}) SET point.latitude = latitude, 
 point.longitude = longitude MERGE (user:User {name: userName}) MERGE (user)-[:EDITED]->(point)
  39. Apply Cypher transformation CALL apoc.load.xml('http://overpass.osm.rambler.ru/cgi/xapi_meta? *[bbox=11.54,48.14,11.543,48.145]') YIELD value UNWIND value["_children"]

    AS child WITH child WHERE child["_type"] = "node" WITH child.id AS id, 
 child.lat AS latitude, 
 child.lon AS longitude, 
 child["user"] AS userName MERGE (point:Point {id: id}) SET point.latitude = latitude, 
 point.longitude = longitude MERGE (user:User {name: userName}) MERGE (user)-[:EDITED]->(point)
  40. apoc.load.json WITH "https://api.stackexchange.com/2.2/questions? pagesize=100&order=desc&sort=creation&tagged=neo4j&site=stackoverflow&filter=!5-i6Zw8Y)4W7vpy91PMYsKM- k9yzEsSC1_Uxlf" AS url CALL apoc.load.json(url) YIELD

    value UNWIND value.items AS q MERGE (question:Question {id:q.question_id}) 
 ON CREATE SET question.title = q.title, 
 question.share_link = q.share_link, 
 question.favorite_count = q.favorite_count MERGE (owner:User {id:q.owner.user_id}) 
 ON CREATE SET owner.display_name = q.owner.display_name
 MERGE (owner)-[:ASKED]->(question) FOREACH (tagName IN q.tags | 
 MERGE (tag:Tag {name:tagName}) MERGE (question)-[:TAGGED]->(tag))
 FOREACH (a IN q.answers | MERGE (question)<-[:ANSWERS]-(answer:Answer {id:a.answer_id}) MERGE (answerer:User {id:a.owner.user_id}) 
 ON CREATE SET answerer.display_name = a.owner.display_name 
 MERGE (answer)<-[:PROVIDED]-(answerer))
  41. WITH "https://api.stackexchange.com/2.2/questions?pagesize=100&order=desc&sort=creation&tagged=neo4j&site=stackoverflow&filter=!5- i6Zw8Y)4W7vpy91PMYsKM-k9yzEsSC1_Uxlf" AS url CALL apoc.load.json(url) YIELD value UNWIND

    value.items AS q MERGE (question:Question {id:q.question_id}) 
 ON CREATE SET question.title = q.title, 
 question.share_link = q.share_link, 
 question.favorite_count = q.favorite_count MERGE (owner:User {id:q.owner.user_id}) 
 ON CREATE SET owner.display_name = q.owner.display_name
 MERGE (owner)-[:ASKED]->(question) FOREACH (tagName IN q.tags | 
 MERGE (tag:Tag {name:tagName}) MERGE (question)-[:TAGGED]->(tag))
 FOREACH (a IN q.answers | MERGE (question)<-[:ANSWERS]-(answer:Answer {id:a.answer_id}) MERGE (answerer:User {id:a.owner.user_id}) 
 ON CREATE SET answerer.display_name = a.owner.display_name 
 MERGE (answer)<-[:PROVIDED]-(answerer)) Use FOREACH for arrays within a row
  42. apoc.refactor.mergeNodes MATCH (n:Person) WITH n.email AS email, collect(n) as people

    WHERE size(people) > 1 CALL apoc.refactor.mergeNodes(people) YIELD node RETURN node
  43. apoc.refactor.mergeNodes MATCH (n:Person) WITH n.email AS email, collect(n) as people

    WHERE size(people) > 1 CALL apoc.refactor.mergeNodes(people) YIELD node RETURN node
  44. Run large scale updates CALL apoc.periodic.iterate( 'MATCH (n:Person) RETURN n',

    'SET n.name = n.firstName + " " + n.lastName',
 {batchSize:10000, parallel:true})
  45. How similar do two strings sound? CALL apoc.text.phoneticDelta(
 'Hello Mr

    Rabbit', 'Hello Mr Ribbit') 
 // will return '4' (very similar)
  46. Timestamp to Date RETURN apoc.date.format(
 1427253359, 
 's', 
 'yyyy/MM/dd

    HH:mm:ss'
 ) 
 // will return "2015/03/25 03:15:59"
  47. Functionality • Still in development! • Stream transaction event handler

    events to a Kafka topic • Read Kafka records from given topic(s) • Treat records as input parameters • Uses configured Cypher statements to merge into the graph
  48. What Do I Need? • Configuration properties • Set up

    node-patterns to send to Kafka • Choose Labels and properties to include/exclude • Patterns separated by semicolon
  49. conf/neo4j.conf com.graphaware.runtime.enabled=true
 
 #ES becomes the module ID:
 com.graphaware.module.ES. 2=com.graphaware.module.es.ElasticSearchModuleBootstrapper


    
 #URI of Elasticsearch
 com.graphaware.module.ES.uri=localhost
 
 #Port of Elasticsearch
 com.graphaware.module.ES.port=9201
  50. Pass in the new state MATCH (p:Person {name: "Marco"}) WITH

    p CALL graph.versioner.update(p, {address: 'Via Roma 12'}) YIELD node RETURN node
  51. Add nodes to spatial index MATCH (d:District) WITH collect(d) AS

    districts CALL spatial.addNodes('geom', districts) YIELD node RETURN count(*)
  52. Query nodes by location CALL spatial.withinDistance('geom',
 {latitude: 37.563440, longitude: -122.322265},

    1) 
 YIELD node AS d WITH d, d.wkt AS wkt, d.state AS state, d.district AS district LIMIT 1 MATCH (d)<-[:REPRESENTS]-(l:Legislator) MATCH (l)-[:SERVES_ON]->(c:Committee) MATCH (c)<-[:REFERRED_TO]-(b:Bill) MATCH (b)-[:DEALS_WITH]->(s:Subject) RETURN *
  53. Finds nodes within 1km CALL spatial.withinDistance('geom',
 {latitude: 37.563440, longitude: -122.322265},

    1) 
 YIELD node AS d WITH d, d.wkt AS wkt, d.state AS state, d.district AS district LIMIT 1 MATCH (d)<-[:REPRESENTS]-(l:Legislator) MATCH (l)-[:SERVES_ON]->(c:Committee) MATCH (c)<-[:REFERRED_TO]-(b:Bill) MATCH (b)-[:DEALS_WITH]->(s:Subject) RETURN *
  54. Continue with the rest of the query CALL spatial.withinDistance('geom',
 {latitude:

    37.563440, longitude: -122.322265}, 1) 
 YIELD node AS d WITH d, d.wkt AS wkt, d.state AS state, d.district AS district LIMIT 1 MATCH (d)<-[:REPRESENTS]-(l:Legislator) MATCH (l)-[:SERVES_ON]->(c:Committee) MATCH (c)<-[:REFERRED_TO]-(b:Bill) MATCH (b)-[:DEALS_WITH]->(s:Subject) RETURN *
  55. Annotating text CREATE (n:News) SET n.text = "Scores of people

    were already lying dead or injured inside a crowded Orlando nightclub, and the police had spent hours trying to connect with the gunman and end the situation without further violence. But when Omar Mateen threatened to set off explosives, the police decided to act, and pushed their way through a wall to end the bloody standoff."
  56. Annotating text MATCH (n:News) CALL ga.nlp.annotate({text: n.text, id: id(n)}) YIELD

    result MERGE (n)-[:HAS_ANNOTATED_TEXT]->(result) RETURN result
  57. Execute procedure MATCH (n:News) CALL ga.nlp.annotate({text: n.text, id: id(n)}) YIELD

    result MERGE (n)-[:HAS_ANNOTATED_TEXT]->(result) RETURN result
  58. Relate to text node MATCH (n:News) CALL ga.nlp.annotate({text: n.text, id:

    id(n)}) YIELD result MERGE (n)-[:HAS_ANNOTATED_TEXT]->(result) RETURN result
  59. • Single Source Short Path • All Pairs SSP •

    Parallel BFS / DFS • Minimum Weight Spanning Tree • Shortest Path • A* • Yen’s K-shortest paths • Random Walk • Strongly-Connected Components • Union Find / WCC • Label Propagation • Louvain • Triangle-Count / Clustering Coefficent • PageRank (baseline) • Betweeness • Closeness • Degree • Harmonic (new!) https://neo4j.com/docs/graph-algorithms/current/
  60. Usage 1.Call as Cypher procedure 2.Pass in specification (Label, Prop,

    Query) and configuration 3.Execute and return results A. ~.stream variant returns (a lot) of results CALL algo.<name>.stream('Label','TYPE', {conf}) YIELD nodeId, score B. non-stream variant writes results to graph and returns statistics
 CALL algo.<name>('Label','TYPE', {conf})
  61. Cypher Projection Pass in Cypher statement for node and relationship

    lists. 
 CALL algo.<name>( 'MATCH ... RETURN id(n)', 'MATCH (n)-->(m) RETURN id(n) as source, id(m) as target', 
 {graph:'cypher'})
  62. What’s In It? • Linear regression (Lauren Shin) • Multiple

    linear regression (Lauren Shin) • DeepGL/DeepWalk (Mark Needham) • …more coming soon!
  63. var viz; function draw() { var config = { container_id:

    "viz", server_url: "bolt://localhost:7687", server_user: "neo4j", server_password: "sorts-swims-burglaries", labels: { "Character": { "caption": "name", "size": "pagerank", "community": "community" } }, relationships: { "INTERACTS": { "thickness": "weight", "caption": false } }, initial_cypher: "MATCH (n)-[r:INTERACTS]->(m) RETURN *" }; viz = new NeoVis.default(config); viz.render(); }
  64. var viz; function draw() { var config = { container_id:

    "viz", server_url: "bolt://localhost:7687", server_user: "neo4j", server_password: "sorts-swims-burglaries", labels: { "Character": { "caption": "name", "size": "pagerank", "community": "community" } }, relationships: { "INTERACTS": { "thickness": "weight", "caption": false } }, initial_cypher: "MATCH (n)-[r:INTERACTS]->(m) RETURN *" }; viz = new NeoVis.default(config); viz.render(); }
  65. var viz; function draw() { var config = { container_id:

    "viz", server_url: "bolt://localhost:7687", server_user: "neo4j", server_password: "sorts-swims-burglaries", labels: { "Character": { "caption": "name", "size": "pagerank", "community": "community" } }, relationships: { "INTERACTS": { "thickness": "weight", "caption": false } }, initial_cypher: "MATCH (n)-[r:INTERACTS]->(m) RETURN *" }; viz = new NeoVis.default(config); viz.render(); }
  66. What is it? GraphQL is a query language for your

    API, and a server-side runtime for executing queries by using a type system you define for your data.
  67. What is it? GraphQL is a query language for your

    API, and a server- side runtime for executing queries by using a type system you define for your data.
  68. What is it? GraphQL is a query language for your

    API, and a server-side runtime for executing queries by using a type system you define for your data.
  69. What is it? GraphQL is a query language for your

    API, and a server-side runtime for executing queries by using a type system you define for your data. type Planet {
 name: String
 climate: String
 } type Character {
 name: String
 friends: [Character]
 homeWorld: Planet
 species: Species
 } type Species {
 name: String
 lifespan: Int
 origin: Planet
 }
  70. CALL graphql.idl('
 type Movie { title: String! released: Int actors:

    [Person] @relation(name:"ACTED_IN",direction:IN) } type Person { name: String! born: Int movies: [Movie] @relation(name:"ACTED_IN") }' )
  71. WITH '{ Person(born: 1961) { name, born } }' as

    query, {} as params CALL graphql.execute(query,params) 
 YIELD result UNWIND result.Person as p RETURN p.name, p.born
  72. import {neo4jgraphql} from 'neo4j-graphql-js'; const resolvers = { Query: {

    Movie(object, params, ctx, resolveInfo) { return neo4jgraphql(object, params, ctx, resolveInfo); } } };
  73. How many did we cover? • Bolt drivers: https://neo4j.com/developer/language-guides/ •

    User-defined procs/funcs: https://neo4j.com/developer/procedures-functions/ • APOC: https://github.com/neo4j-contrib/neo4j-apoc-procedures • Neo4j/Kafka: https://github.com/neo4j-contrib/neo4j-streams • Neo4j/RDF: https://github.com/jbarrasa/neosemantics • Neo4j/Elasticsearch: https://github.com/graphaware/neo4j-to-elasticsearch • Versioned graphs: https://github.com/h-omer/neo4j-versioner-core • Spatial: https://github.com/neo4j-contrib/spatial • NLP: https://github.com/graphaware/neo4j-nlp • Graph Algorithms: https://github.com/neo4j-contrib/neo4j-graph-algorithms • ML models: https://github.com/neo4j-graph-analytics/ml-models • Graph Visualization: https://github.com/neo4j-contrib/neovis.js • GraphQL/GRANDstack: https://github.com/neo4j-graphql