Introducing Neo4j SDN⚡️RX

20492a196bb034ad3aa7e05e593fede9?s=47 Michael Simons
May 15, 2020
130

Introducing Neo4j SDN⚡️RX

20492a196bb034ad3aa7e05e593fede9?s=128

Michael Simons

May 15, 2020
Tweet

Transcript

  1. 1.

    Neo4j SDN⚡RX „What the hell, it’s 2020 and you’re writing

    a new object mapping framework?“ 15th May 2020 Michael Simons introducing
  2. 2.

    Neo4j SDN⚡ RX at Spring I/O Bridge 2020 by @rotnroll666

    Agenda • About Neo4j • Who are the folks working on the Object mapping things? • Some history • SDN/RX • What do we want? • New building blocks for Spring Boot • Neo4j Client, Template and Repositories • Cypher DSL 2
  3. 4.

    Ecosystem Neo4j Professional Services 300+ partners 47,000 group members 61,000

    trained engineers 3.5M downloads Mindset “Graph Thinking” is all about considering connections in data as important as the data itself. Native Graph Platform Neo4j is an internet-scale, native graph database which executes connected workloads faster than any other database management system. Neo4j 4
  4. 6.

    Neo4j SDN⚡ RX at Spring I/O Bridge 2020 by @rotnroll666

    Team SDN+OGM (Object Graph Mapping) 6
  5. 7.

    Neo4j SDN⚡ RX at Spring I/O Bridge 2020 by @rotnroll666

    • Neo4j since July 2018 • Java Champion • Co-Founder and current lead of Java User Group EuregJUG • Author (Spring Boot 2 und Arc42 by example) About me 7 First contact to Neo4j through
  6. 9.

    Neo4j SDN⚡ RX at Spring I/O Bridge 2020 by @rotnroll666

    9 It’s a kind of magic? Custom Spring Boot Starter Michael Simons @rotnroll666
  7. 10.

    WITH previous_month AS (SELECT p.track_id, count(*) as cnt, dense_rank() over(order

    by count(*) desc) as position FROM plays p WHERE trunc(p.played_on, 'DD') BETWEEN date'2016-04-01' and date'2016-04-30' GROUP BY p.track_id), current_month AS (SELECT p.track_id, count(*) as cnt, dense_rank() over(order by count(*) desc) as position FROM plays p WHERE trunc(p.played_on, 'DD') BETWEEN date'2016-05-01' and date'2016-05-31' GROUP BY p.track_id) SELECT a.artist !" ' - ' !" t.name !" ' (' !" t.album !" ')' as label, current_month.cnt, previous_month.position - current_month.position as change FROM tracks t JOIN artists a on a.id = t.artist_id JOIN current_month current_month on current_month.track_id = t.id LEFT OUTER join previous_month on previous_month.track_id = t.id ORDER BY current_month.cnt desc, label asc FETCH FIRST 20 ROWS ONLY;
  8. 14.

    Neo4j SDN⚡ RX at Spring I/O Bridge 2020 by @rotnroll666

    12 Different approaches • Just query what’s needed • Use special database functions • Work on aggregates • Use analytic queries • Or graph queries Some examples to follow…
  9. 15.

    Neo4j SDN⚡ RX at Spring I/O Bridge 2020 by @rotnroll666

    13 © https://www.enerko-informatik.de/projekte/ihl/ © https://www.enerko-informatik.de/produkte/ensupply/ Both backed by a relational database…
  10. 16.

    Neo4j SDN⚡ RX at Spring I/O Bridge 2020 by @rotnroll666

    14 https://github.com/michael-simons/bootiful-music Backed by Neo4j
  11. 17.

    Neo4j SDN⚡ RX at Spring I/O Bridge 2020 by @rotnroll666

    15 https://github.com/michael-simons/bootiful-music Backed by Neo4j
  12. 18.

    Neo4j SDN⚡ RX at Spring I/O Bridge 2020 by @rotnroll666

    16 Current Spring Data Neo4 + OGM • start.spring.io gives you SDN and OGM • 3 different modes available (Embedded, HTTP and native) • Custom queries are possible but go all through the OGM abstraction • Some building parts stack vertically, some horizontally ➡ Confusing options for beginners
  13. 21.

    Neo4j SDN⚡ RX at Spring I/O Bridge 2020 by @rotnroll666

    19 What do we want? • A composable stack • Nicely integrated in Spring Boot • Access at all abstraction levels • Raw queries (outside application level transactions) • Raw queries (inside application level transactions) • Plus ad-hoc mapping • Mapping • Repository abstraction • Reactive (and imperative) database access
  14. 23.

    Neo4j SDN⚡ RX at Spring I/O Bridge 2020 by @rotnroll666

    21 Spring Boot starter revisited <dependency> <groupId>org.neo4j.driver!#groupId> <artifactId>neo4j-java-driver-spring-boot-starter!#artifactId> <version>4.0.1.1!#version> !#dependency> Gives you: • Dependencies • spring.factories • Configuration properties • Automatic configuration
  15. 24.

    Neo4j SDN⚡ RX at Spring I/O Bridge 2020 by @rotnroll666

    @ConfigurationProperties(prefix = "org.neo4j.driver") public class Neo4jDriverProperties { public static class Authentication { /** The login of the user connecting to the database.!% private String username; /** The password of the user connecting to the database. !% private String password; /** The realm to connect to.!% private String realm; } /** The uri this driver should connect to. The driver supports bolt or neo4j as schemes.!% private URI uri; /** The authentication the driver is supposed to use. Maybe null. !% private Authentication authentication = new Authentication(); } 22 Property mapping
  16. 25.

    Neo4j SDN⚡ RX at Spring I/O Bridge 2020 by @rotnroll666

    23 Automatic configuration for the Driver @Configuration(proxyBeanMethods = false) @AutoConfigureBefore(Neo4jDataAutoConfiguration.class) @ConditionalOnClass(Driver.class) @EnableConfigurationProperties(Neo4jDriverProperties.class) @Import({ DriverConfiguration.class, AdditionalDataConfiguration.class }) public class Neo4jDriverAutoConfiguration { } @Configuration(proxyBeanMethods = false) class DriverConfiguration { @Bean @ConditionalOnMissingBean(Driver.class) @ConditionalOnProperty(prefix = "org.neo4j.driver", name = "uri") Driver neo4jDriver(final Neo4jDriverProperties driverProperties) { final AuthToken authToken = driverProperties.getAuthentication().asAuthToken(); final Config config = driverProperties.asDriverConfig(); return GraphDatabase.driver(driverProperties.getUri(), authToken, config); } }
  17. 26.

    Neo4j SDN⚡ RX at Spring I/O Bridge 2020 by @rotnroll666

    24 Automatic configuration for the Driver • Spring Boot 2.2 got enhanced to recognise the presence of a Driver-Bean!
  18. 27.

    Neo4j SDN⚡ RX at Spring I/O Bridge 2020 by @rotnroll666

    <dependency> <groupId>org.neo4j.springframework.data!#groupId> <artifactId>spring-data-neo4j-rx-spring-boot-starter!#artifactId> <version>1.0.1!#version> !#dependency> 25 Similar starter for SDN/RX
  19. 29.

    Neo4j SDN⚡ RX at Spring I/O Bridge 2020 by @rotnroll666

    27 @Autowired org.neo4j.driver.Driver @RestController public class MoviesController { private final Driver driver; public MoviesController(Driver driver) { this.driver = driver; } @GetMapping(path = "/movies", produces = MediaType.TEXT_EVENT_STREAM_VALUE) public Flux<String> getMovieTitles() { !& Using an implicit, driver managed transaction return Flux.usingWhen( Mono.fromSupplier(driver!'rxSession), s !( Flux.from(s.run("MATCH (m:Movie) RETURN m ORDER BY m.name ASC").records()), RxSession!'close ).map(r !( r.get("m").asNode().get("title").asString()); } }
  20. 30.

    Neo4j SDN⚡ RX at Spring I/O Bridge 2020 by @rotnroll666

    27 @Autowired org.neo4j.driver.Driver @RestController public class MoviesController { private final Driver driver; public MoviesController(Driver driver) { this.driver = driver; } @GetMapping(path = "/movies", produces = MediaType.TEXT_EVENT_STREAM_VALUE) public Flux<String> getMovieTitles() { !& Using an implicit, driver managed transaction return Flux.usingWhen( Mono.fromSupplier(driver!'rxSession), s !( Flux.from(s.run("MATCH (m:Movie) RETURN m ORDER BY m.name ASC").records()), RxSession!'close ).map(r !( r.get("m").asNode().get("title").asString()); } } Based on open standards: Reactive Streams. Can be used with RxJava2, too.
  21. 31.

    Neo4j SDN⚡ RX at Spring I/O Bridge 2020 by @rotnroll666

    @Service public class MovieService { private final ReactiveNeo4jClient client; public MovieService(ReactiveNeo4jClient client) { this.client = client; } !& Fully integrated with Springs declarative !& or explicit transactions (via TransactionalOperator or TransactionTemplate) @Transactional public Flux<String> getMovieTitles() { return client.query("MATCH (m:Movie) RETURN m ORDER BY m.name ASC") .fetchAs(String.class) .mappedBy((typeSystem, record) !( record.get("m").asNode().get("title").asString()) .all(); } } 28 @Autowired org.neo4j.s…k.data.core.ReactiveNeo4jClient
  22. 32.

    Neo4j SDN⚡ RX at Spring I/O Bridge 2020 by @rotnroll666

    @Service public class MovieService { private final ReactiveNeo4jClient client; public MovieService(ReactiveNeo4jClient client) { this.client = client; } !& Fully integrated with Springs declarative !& or explicit transactions (via TransactionalOperator or TransactionTemplate) @Transactional public Flux<String> getMovieTitles() { return client.query("MATCH (m:Movie) RETURN m ORDER BY m.name ASC") .fetchAs(String.class) .mappedBy((typeSystem, record) !( record.get("m").asNode().get("title").asString()) .all(); } } 28 @Autowired org.neo4j.s…k.data.core.ReactiveNeo4jClient
  23. 33.

    Neo4j SDN⚡ RX at Spring I/O Bridge 2020 by @rotnroll666

    29 @Autowired org.neo4j.s…k.data.core.ReactiveNeo4jTemplate @Service public class MovieService { private final ReactiveNeo4jTemplate template; public MovieService(ReactiveNeo4jTemplate template) { this.template = template; } @Transactional public Flux<String> getMovieTitles() { !& Overload with custom query available return template.findAll(MovieEntity.class) .map(MovieEntity!'getTitle); } } @Node("Movie") class MovieEntity { private @Id final String title; private @Property("tagline") final String description; @Relationship(type = "ACTED_IN", direction = INCOMING) private final Map<PersonEntity, Roles> actorsAndRoles; !& Full immutable class, works also with @Data, !& Kotlin and JDK 14 records public MovieEntity(String title, String description, Map<PersonEntity, Roles> actorsAndRoles ) { this.title = title; this.description = description; this.actorsAndRoles = actorsAndRoles; } }
  24. 34.

    Neo4j SDN⚡ RX at Spring I/O Bridge 2020 by @rotnroll666

    public interface MovieRepository extends ReactiveNeo4jRepository<MovieEntity, String> {} @Service public class MovieService { private final MovieRepository repository; public MovieService(MovieRepository repository) { this.repository = repository; } @Transactional public Flux<String> getMovieTitles() { !& Custom query via @Query on a method !& For simple cases, use derived query methods !& repository.findAllByOrderByTitle return repository.findAll() .map(MovieEntity!'getTitle); !& For the first time with Spring Data and Neo4j, query by Example !& return repository.findAll(Example.of(new MovieEntity("The Matrix", null))); } } 30 @Autowired org.neo4j.s…k.data.core.ReactiveNeo4jRepository
  25. 36.

    Neo4j SDN⚡ RX at Spring I/O Bridge 2020 by @rotnroll666

    32 Non-functional concerns • Compose only vertically • Less surprises • No smart guess, better fail fast • „When in Spring land, do as the Spring Data team does“ • Well defined extension points for • Custom queries • Adhoc mappings
  26. 37.

    Neo4j SDN⚡ RX at Spring I/O Bridge 2020 by @rotnroll666

    33 No more string concatenation: CypherDSL • Started as internal API • Heavily inspired by both jOOQ and Spring Data JDBC • Compile-time checked query construction • Used internally for every query generated
  27. 38.

    Neo4j SDN⚡ RX at Spring I/O Bridge 2020 by @rotnroll666

    34 No more string concatenation: CypherDSL var tom = node("Person").named("tom").properties("name", literalOf("Tom Hanks")); var coActors = anyNode("coActors"); var cocoActors = anyNode("cocoActors"); var strength = count(asterisk()).as("Strength"); var statement = Cypher .match( tom.relationshipTo(anyNode("m"), "ACTED_IN").relationshipFrom(coActors, "ACTED_IN"), coActors.relationshipTo(anyNode("m2"), "ACTED_IN").relationshipFrom(cocoActors, "ACTED_IN") ) .where(not(tom.relationshipTo(anyNode(), "ACTED_IN").relationshipFrom(cocoActors, "ACTED_IN"))) .and(tom.isNotEqualTo(cocoActors)) .returning( cocoActors.property("name").as("Recommended"), strength ).orderBy(strength.asName().descending()) .build(); assertThat(cypherRenderer.render(statement)) .isEqualTo("" + "MATCH " + "(tom:`Person` {name: 'Tom Hanks'})-[:`ACTED_IN`]"#(m)"$[:`ACTED_IN`]-(coActors), " + "(coActors)-[:`ACTED_IN`]"#(m2)"$[:`ACTED_IN`]-(cocoActors) " + "WHERE (NOT (tom)-[:`ACTED_IN`]"#()"$[:`ACTED_IN`]-(cocoActors) AND tom "% cocoActors) " + "RETURN cocoActors.name AS Recommended, count(*) AS Strength ORDER BY Strength DESC");
  28. 40.

    Neo4j SDN⚡ RX at Spring I/O Bridge 2020 by @rotnroll666

    36 mkdir bootiful-music !) cd bootiful-music npm install -g generator-jhipster curl -L https:!&r.neo4j.com/hipster-music -o bootiful-music.jh jhipster import-jdl bootiful-music.jh docker-compose -f src/main/docker/neo4j.yml up -d ./mvnw https://medium.com/neo4j/two-hip-friends-jhipster-and-neo4j-774639e9693
  29. 41.

    Neo4j SDN⚡ RX at Spring I/O Bridge 2020 by @rotnroll666

    37 An OSS group effort across companies • Team Drivers at Neo4j • Shoutout to Zhen and Nigel (@technige) • Spring Data Team at VMWare • Special thanks to Mark (@mp911de) • Spring Boot Team at VMWare • Special thanks to Stéphane (@snicoll) • And also to • Sergei (@bsideup) at VMWare • Frederik (@atomfrede) (JHipster)
  30. 42.

    Neo4j SDN⚡ RX at Spring I/O Bridge 2020 by @rotnroll666

    38 Available now • Works with Neo4j 3.4+ • Reactive database access needs 4.0 • Multi-database feature needs Neo4j Enterprise Edition • Works with Neo4j Aura https://neo4j.com/aura/ • Provides Neo4j support in JHipster https://www.jhipster.tech • Coordinates: • Driver starter: https://github.com/neo4j/sdn-rx • SDN/RX: https://github.com/neo4j/sdn-rx
  31. 43.

    Neo4j SDN⚡ RX at Spring I/O Bridge 2020 by @rotnroll666

    39 What to chose for a new project? • Used already by customers • 1.0.0 released in April • Will eventually replace SDN+OGM • Good choice for new projects • When it moves back under Spring Coordinates, packages will change • More effort to migrate existing SDN+OGM code • Different packages • More opinionated behaviour