Slide 1

Slide 1 text

Neo4j SDN⚡RX „What the hell, it’s 2020 and you’re writing a new object mapping framework?“ 15th May 2020 Michael Simons introducing

Slide 2

Slide 2 text

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

Slide 3

Slide 3 text

About Neo4j

Slide 4

Slide 4 text

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

Slide 5

Slide 5 text

Who are the folks working on the object mapping things?

Slide 6

Slide 6 text

Neo4j SDN⚡ RX at Spring I/O Bridge 2020 by @rotnroll666 Team SDN+OGM (Object Graph Mapping) 6

Slide 7

Slide 7 text

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

Slide 8

Slide 8 text

Some history

Slide 9

Slide 9 text

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

Slide 10

Slide 10 text

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;

Slide 11

Slide 11 text

Tracking musical data (at Spring I/O 2017)

Slide 12

Slide 12 text

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

Slide 13

Slide 13 text

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

Slide 14

Slide 14 text

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…

Slide 15

Slide 15 text

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…

Slide 16

Slide 16 text

Neo4j SDN⚡ RX at Spring I/O Bridge 2020 by @rotnroll666 14 https://github.com/michael-simons/bootiful-music Backed by Neo4j

Slide 17

Slide 17 text

Neo4j SDN⚡ RX at Spring I/O Bridge 2020 by @rotnroll666 15 https://github.com/michael-simons/bootiful-music Backed by Neo4j

Slide 18

Slide 18 text

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

Slide 19

Slide 19 text

Neo4j SDN⚡RX

Slide 20

Slide 20 text

What do we want?

Slide 21

Slide 21 text

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

Slide 22

Slide 22 text

Neo4j SDN⚡ RX at Spring I/O Bridge 2020 by @rotnroll666 20 A composable stack

Slide 23

Slide 23 text

Neo4j SDN⚡ RX at Spring I/O Bridge 2020 by @rotnroll666 21 Spring Boot starter revisited org.neo4j.driver!#groupId> neo4j-java-driver-spring-boot-starter!#artifactId> 4.0.1.1!#version> !#dependency> Gives you: • Dependencies • spring.factories • Configuration properties • Automatic configuration

Slide 24

Slide 24 text

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

Slide 25

Slide 25 text

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); } }

Slide 26

Slide 26 text

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!

Slide 27

Slide 27 text

Neo4j SDN⚡ RX at Spring I/O Bridge 2020 by @rotnroll666 org.neo4j.springframework.data!#groupId> spring-data-neo4j-rx-spring-boot-starter!#artifactId> 1.0.1!#version> !#dependency> 25 Similar starter for SDN/RX

Slide 28

Slide 28 text

Moving upwards on abstraction levels

Slide 29

Slide 29 text

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 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()); } }

Slide 30

Slide 30 text

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 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.

Slide 31

Slide 31 text

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 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

Slide 32

Slide 32 text

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 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

Slide 33

Slide 33 text

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 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 actorsAndRoles; !& Full immutable class, works also with @Data, !& Kotlin and JDK 14 records public MovieEntity(String title, String description, Map actorsAndRoles ) { this.title = title; this.description = description; this.actorsAndRoles = actorsAndRoles; } }

Slide 34

Slide 34 text

Neo4j SDN⚡ RX at Spring I/O Bridge 2020 by @rotnroll666 public interface MovieRepository extends ReactiveNeo4jRepository {} @Service public class MovieService { private final MovieRepository repository; public MovieService(MovieRepository repository) { this.repository = repository; } @Transactional public Flux 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

Slide 35

Slide 35 text

And more…

Slide 36

Slide 36 text

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

Slide 37

Slide 37 text

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

Slide 38

Slide 38 text

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");

Slide 39

Slide 39 text

Available now

Slide 40

Slide 40 text

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

Slide 41

Slide 41 text

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)

Slide 42

Slide 42 text

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

Slide 43

Slide 43 text

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