quarkusio / quarkus

Quarkus: Supersonic Subatomic Java.
https://quarkus.io
Apache License 2.0
13.44k stars 2.58k forks source link

Provide a Neo4J async client example for update #13904

Open Solido opened 3 years ago

Solido commented 3 years ago

Description

The current documentation is very helpful providing an example for full async read using mutiny but an example on how to write would help a lot.

In my case I am stuck because simply replacing readtransaction() by writetransaction() will not work since rs.records never call the publisher since they are absent from the RxResult as opposed to a Read Transaction

tx.run(query, parameters)
Multi.createFrom().publisher(rs.records()).map { ... }

Thank you !

ghost commented 3 years ago

/cc @michael-simons

michael-simons commented 3 years ago

Hi @Solido Thanks for your kind feedback.

Those that example help you? https://github.com/michael-simons/neo4j-from-the-jvm-ecosystem/blob/master/quarkus-reactive/src/main/java/org/neo4j/examples/jvm/quarkus/reactive/movies/PeopleRepository.java#L29-L48

If so, it should be included in a similar fashion in the starter doc.

Solido commented 3 years ago

As a beginner any material is premium to help grasp concept and best practices.

Here is the kotlin translation from the official Quarkus neo4j example and I wonder

  1. why your update code is so different & use AtomicReference
  2. why replacing with writeTransaction makes rs.records not triggering the publisher (as opposed to your code which seems to trigger.publisher(session.writeTransaction(tx -> tx.run(query, parameters).records()))))
fun <T> read(query: String, action: Action<T>): Multi<T> =
        Multi.createFrom().resource(driver::rxSession) { rxs ->
            rxs.readTransaction { tx ->
                val rs = tx.run(query.trimIndent())
                Multi.createFrom().publisher(rs.records()).map {
                    action(it)
                }
            }
        }.withFinalizer(Consumer { Uni.createFrom().publisher<Void>(it.close()) })

Anyways Quarkus Team is so ... Reactive to provide help ;)

Thanks @michael-simons

michael-simons commented 3 years ago
  1. Because I didn't know the changed API ;) I'm gonna fix that
  2. I would probably need to see the call site.
Solido commented 3 years ago

Oh I just saw the root of your repo and implementation for every framework ! Great starter

My write method is naive replacement of read. WriteTransaction() instead of readTransactions() but strangely rs.records does not hold any result so Multi is not triggered.

Driver got the WRITE Mode and any basic query will fail but OK on the browser

MATCH (d:Descriptable {key:"k55"}) RETURN d

fun <T> write(query: String, parameters: Map<String, Any> = emptyMap(), action: Action<T>): Multi<T> =
        Multi.createFrom().resource(driver::rxSession) { rxs ->
            rxs.writeTransaction { tx ->
                val rs = tx.run(query, parameters)
                Multi.createFrom().publisher(rs.records()).map { action(it) }
            }
       }.withFinalizer(Consumer { Uni.createFrom().publisher<Void>(it.close()) })
michael-simons commented 3 years ago

I See that the createFrom().resource pattern is called to early… Fails on me as well … Need to investigate whether its on our side, quarkus or user :)

michael-simons commented 3 years ago

See my updated example.

With regard to write transaction we must use a hot stream:

Uni<Person> save(Person person) {

    var query = ""
                + "MERGE (p:Person {name: $name})\n"
                + "SET p.born = $born\n"
                + "RETURN p\n";
    var parameters = Map.<String, Object>of("name", person.getName(), "born", person.getBorn());

    return Multi
        .createFrom().resource(driver::rxSession, session -> session.writeTransaction(tx -> tx.run(query, parameters).records()))
        .withFinalizer(rxSession -> {
            return Uni.createFrom().publisher(rxSession.close());
        })
        .map(r -> asPerson(r.get("p").asNode()))
        .transform().toHotStream() // Otherwise the created uni behaves weird. I guess this because due to `on completion event, a {@code null} item is fired by the produces {@link Uni}` (See `toUni` on Multi.)
        .toUni();
}

Or don't transform the multi into a uni… That one behaves a bit odd. I do need a uni in my example however because I want to return a Uni<Response>

I would love to hear from @vietj or other folks having more knowledge than me about the subtle differences of Multi and Uni (Uni is not a publisher). Julien: The behavior I observe that the finalizer is executed before the actual inner Multi completes when I use toUni on the whole thing without converting it into a hot stream.

Apart from that, your code looks good to me…