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

Neo4j Extensions

Neo4j Extensions

Each and every tool or product leaves something lacking for a particular functionality or use case, and developers are often required to build their own solutions to fit the need and write extra code each time a new gap presents itself. However, some technologies make it easier than others to write code for extensibility. Learn how Neo4j (the leading graph database) allows developers access to additional libraries for extending core functionality and integrating with familiar technologies. Find out how you can use added Java libraries for solutions to common problems, write custom procedures and functions for certain edge cases, import and export various types of data, connect to applications in nearly any programming language, refactor your data, and explore areas of data science or ML. Simple integrations with vendor tools can allow developers to create beautiful visualizations, enhance monitoring, allow data searching, and move data by utilizing existing pieces of the development process to expand the capabilities of those tools. Find out how extensions can increase existing functionality and how you can continue to expand it.

Jennifer Reif

July 19, 2018
Tweet

More Decks by Jennifer Reif

Other Decks in Technology

Transcript

  1. 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
  2. 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
  3. 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
  4. 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; } } }
  5. 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; } } }
  6. 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; } } }
  7. 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 } } }
  8. 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 } } }
  9. 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 ); } }
  10. 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 ); } }
  11. 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 ); } }
  12. 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(); } }
  13. 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; } } }
  14. 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; } } }
  15. 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; } } }
  16. 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; } } }
  17. 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(); } }
  18. 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
  19. 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
  20. 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
  21. 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)
  22. 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)
  23. 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)
  24. 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)
  25. 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)
  26. 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))
  27. 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
  28. 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
  29. 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
  30. Run large scale updates CALL apoc.periodic.iterate( 'MATCH (n:Person) RETURN n',

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

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

    HH:mm:ss'
 ) 
 // will return "2015/03/25 03:15:59"
  33. 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 conf/neo4j.conf
  34. MATCH (p:Person {name: "Marco"}) WITH p CALL graph.versioner.update(p, {address: 'Via

    Roma 12'}) YIELD node RETURN node Pass in the new state
  35. 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 * Query nodes by location
  36. 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 * Finds nodes within 1km
  37. 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 * Continue with the rest of the query
  38. 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." Annotating text
  39. MATCH (n:News) CALL ga.nlp.annotate({text: n.text, id: id(n)}) YIELD result MERGE

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

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

    (n)-[:HAS_ANNOTATED_TEXT]->(result) RETURN result Relate to text node
  42. Single Source Short Path All Pairs SSP Parallel BFS /

    DFS Strongly Connected Components Union Find / WCC Label Propagation Louvain Triangle-Count / Clustering Coefficent PageRank (baseline) Betweeness Closeness Degree
  43. 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(); }
  44. 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(); }
  45. 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(); }
  46. 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. What is it?
  47. 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. What is it?
  48. 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. What is it?
  49. 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. What is it? type Planet {
 name: String
 climate: String
 } type Character {
 name: String
 friends: [Character]
 homeWorld: Planet
 species: Species
 } type Species {
 name: String
 lifespan: Int
 origin: Planet
 }
  50. 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") }' )
  51. 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
  52. import {neo4jgraphql} from 'neo4j-graphql-js'; const resolvers = { Query: {

    Movie(object, params, ctx, resolveInfo) { return neo4jgraphql(object, params, ctx, resolveInfo); } } };