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

Introducing Neo4j SDN⚡️RX

Michael Simons
May 15, 2020
420

Introducing Neo4j SDN⚡️RX

Michael Simons

May 15, 2020
Tweet

Transcript

  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

    View Slide

  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

    View Slide

  3. About Neo4j

    View Slide

  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

    View Slide

  5. Who are the folks working on
    the object mapping things?

    View Slide

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

    View Slide

  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

    View Slide

  8. Some history

    View Slide

  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

    View Slide

  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;

    View Slide

  11. Tracking musical data (at Spring I/O 2017)

    View Slide

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

    View Slide

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

    View Slide

  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…

    View Slide

  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…

    View Slide

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

    View Slide

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

    View Slide

  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

    View Slide

  19. Neo4j SDN⚡RX

    View Slide

  20. What do we want?

    View Slide

  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

    View Slide

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

    View Slide

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

    View Slide

  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

    View Slide

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

    View Slide

  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!

    View Slide

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

    View Slide

  28. Moving upwards on
    abstraction levels

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  35. And more…

    View Slide

  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

    View Slide

  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

    View Slide

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

    View Slide

  39. Available now

    View Slide

  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

    View Slide

  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)

    View Slide

  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

    View Slide

  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

    View Slide