Add Kotlin extension supporting reified types inside the Neo4j clients.
Adds extensions for `Neo4jClient`, `ReactiveNeo4jClient` and `PreparedQuery`. In the case of `Neo4jClient`, a delegation has been added as well, that turns the Java 8 `Optional` into a Kotlin `nullable`. Also, `fetchAs` can be skipped altogether when the result needs mapping anyway.
This commit is contained in:
@@ -125,7 +125,7 @@
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jetbrains.kotlin</groupId>
|
||||
<artifactId>kotlin-stdlib</artifactId>
|
||||
<artifactId>kotlin-stdlib-jdk8</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
|
||||
@@ -41,9 +41,6 @@ import org.springframework.lang.Nullable;
|
||||
/**
|
||||
* Definition of a modern Neo4j client.
|
||||
*
|
||||
* TODO Create examples how to use the callbacks etc. with Springs TransactionTemplate to deal with rollbacks etc.
|
||||
* TODO database selection
|
||||
*
|
||||
* @author Gerrit Meier
|
||||
* @author Michael J. Simons
|
||||
* @since 1.0
|
||||
@@ -51,6 +48,8 @@ import org.springframework.lang.Nullable;
|
||||
@API(status = API.Status.STABLE, since = "1.0")
|
||||
public interface Neo4jClient {
|
||||
|
||||
// TODO Create examples how to use the callbacks etc. with Springs TransactionTemplate to deal with rollbacks etc.
|
||||
|
||||
LogAccessor cypherLog = new LogAccessor(LogFactory.getLog("org.neo4j.springframework.data.cypher"));
|
||||
|
||||
static Neo4jClient create(Driver driver) {
|
||||
|
||||
@@ -18,13 +18,19 @@
|
||||
*/
|
||||
package org.neo4j.springframework.data.core
|
||||
|
||||
import org.neo4j.driver.Record
|
||||
import org.neo4j.driver.types.TypeSystem
|
||||
import java.util.*
|
||||
import java.util.function.BiFunction
|
||||
|
||||
/**
|
||||
* Extension for [Neo4jClient.RunnableSpec.in] providing an `inDatabase` alias since `in` is a reserved keyword in Kotlin.
|
||||
*
|
||||
* @author Michael J. Simons
|
||||
* @since 1.0
|
||||
*/
|
||||
fun Neo4jClient.RunnableSpec.inDatabase(targetDatabase: String): Neo4jClient.RunnableSpecTightToDatabase = `in`(targetDatabase)
|
||||
fun Neo4jClient.RunnableSpec.inDatabase(targetDatabase: String): Neo4jClient.RunnableSpecTightToDatabase =
|
||||
`in`(targetDatabase)
|
||||
|
||||
/**
|
||||
* Extension for [Neo4jClient.OngoingDelegation.in] providing an `inDatabase` alias since `in` is a reserved keyword in Kotlin.
|
||||
@@ -32,4 +38,49 @@ fun Neo4jClient.RunnableSpec.inDatabase(targetDatabase: String): Neo4jClient.Run
|
||||
* @author Michael J. Simons
|
||||
* @since 1.0
|
||||
*/
|
||||
fun <T : Any?> Neo4jClient.OngoingDelegation<T>.inDatabase(targetDatabase: String): Neo4jClient.RunnableDelegation<T> = `in`(targetDatabase)
|
||||
fun <T : Any?> Neo4jClient.OngoingDelegation<T>.inDatabase(targetDatabase: String): Neo4jClient.RunnableDelegation<T> =
|
||||
`in`(targetDatabase)
|
||||
|
||||
/**
|
||||
* An implementation of a fetch spec that replaces Java's Optional with a nullable.
|
||||
* @author Michael J. Simons
|
||||
*/
|
||||
class DelegatingFetchSpec<T : Any>(private val delegate: Neo4jClient.RecordFetchSpec<Optional<T>, Collection<T>, T>) : Neo4jClient.RecordFetchSpec<T?, Collection<T>, T> {
|
||||
override fun one(): T? = delegate.one().orElse(null)
|
||||
|
||||
override fun first(): T = delegate.first().orElse(null)
|
||||
|
||||
override fun all(): Collection<T> = delegate.all()
|
||||
}
|
||||
|
||||
/**
|
||||
* An implementation of a mapping spec that replaces Java's Optional with a nullable.
|
||||
* @author Michael J. Simons
|
||||
*/
|
||||
class DelegatingMappingSpec<T : Any>(private val delegate: Neo4jClient.MappingSpec<Optional<T>, Collection<T>, T>) : Neo4jClient.MappingSpec<T?, Collection<T>, T> {
|
||||
override fun mappedBy(mappingFunction: BiFunction<TypeSystem, Record, T>): Neo4jClient.RecordFetchSpec<T?, Collection<T>, T> =
|
||||
DelegatingFetchSpec(delegate.mappedBy(mappingFunction))
|
||||
|
||||
override fun one(): T? = delegate.one().orElse(null)
|
||||
|
||||
override fun first(): T = delegate.first().orElse(null)
|
||||
|
||||
override fun all(): Collection<T> = delegate.all()
|
||||
}
|
||||
|
||||
/**
|
||||
* Extension for [Neo4jClient.RunnableSpecTightToDatabase.fetchAs] leveraging reified type parameters.
|
||||
* @author Michael J. Simons
|
||||
* @since 1.0
|
||||
*/
|
||||
inline fun <reified T : Any> Neo4jClient.RunnableSpecTightToDatabase.fetchAs(): Neo4jClient.MappingSpec<T?, Collection<T>, T>
|
||||
= DelegatingMappingSpec(fetchAs(T::class.java))
|
||||
|
||||
/**
|
||||
* Extension for [Neo4jClient.RunnableSpecTightToDatabase.mappedBy] leveraging reified type parameters and removing
|
||||
* the need for an explicit `fetchAs`.
|
||||
* @author Michael J. Simons
|
||||
* @since 1.0
|
||||
*/
|
||||
inline fun <reified T : Any> Neo4jClient.RunnableSpecTightToDatabase.mappedBy(noinline mappingFunction: (TypeSystem, Record) -> T): Neo4jClient.RecordFetchSpec<T?, Collection<T>, T>
|
||||
= DelegatingFetchSpec(fetchAs(T::class.java).mappedBy(mappingFunction))
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright (c) 2019 "Neo4j,"
|
||||
* Neo4j Sweden AB [https://neo4j.com]
|
||||
*
|
||||
* This file is part of Neo4j.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.neo4j.springframework.data.core
|
||||
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
/**
|
||||
* Helper class for [PreparedQuery.queryFor] that removes the need of adding `::class.java` manually.
|
||||
*
|
||||
* @author Michael J. Simons
|
||||
* @since 1.0
|
||||
*/
|
||||
class PreparedQueryFactory<T : Any>(val c: KClass<T>) {
|
||||
fun withCypherQuery(cypherQuery: String): PreparedQuery.OptionalBuildSteps<T> = c.javaObjectType.let { PreparedQuery.queryFor(it) }
|
||||
.withCypherQuery(cypherQuery)
|
||||
}
|
||||
@@ -18,13 +18,19 @@
|
||||
*/
|
||||
package org.neo4j.springframework.data.core
|
||||
|
||||
import org.neo4j.driver.Record
|
||||
import org.neo4j.driver.types.TypeSystem
|
||||
import reactor.core.publisher.Flux
|
||||
import reactor.core.publisher.Mono
|
||||
|
||||
/**
|
||||
* Extension for [ReactiveNeo4jClient.ReactiveRunnableSpec.in] providing an `inDatabase` alias since `in` is a reserved keyword in Kotlin.
|
||||
*
|
||||
* @author Michael J. Simons
|
||||
* @since 1.0
|
||||
*/
|
||||
fun ReactiveNeo4jClient.ReactiveRunnableSpec.inDatabase(targetDatabase: String): ReactiveNeo4jClient.ReactiveRunnableSpecTightToDatabase = `in`(targetDatabase)
|
||||
fun ReactiveNeo4jClient.ReactiveRunnableSpec.inDatabase(targetDatabase: String): ReactiveNeo4jClient.ReactiveRunnableSpecTightToDatabase =
|
||||
`in`(targetDatabase)
|
||||
|
||||
/**
|
||||
* Extension for [ReactiveNeo4jClient.OngoingReactiveDelegation.in] providing an `inDatabase` alias since `in` is a reserved keyword in Kotlin.
|
||||
@@ -32,4 +38,22 @@ fun ReactiveNeo4jClient.ReactiveRunnableSpec.inDatabase(targetDatabase: String):
|
||||
* @author Michael J. Simons
|
||||
* @since 1.0
|
||||
*/
|
||||
fun <T : Any?> ReactiveNeo4jClient.OngoingReactiveDelegation<T>.inDatabase(targetDatabase: String): ReactiveNeo4jClient.ReactiveRunnableDelegation<T> = `in`(targetDatabase)
|
||||
fun <T : Any?> ReactiveNeo4jClient.OngoingReactiveDelegation<T>.inDatabase(targetDatabase: String): ReactiveNeo4jClient.ReactiveRunnableDelegation<T>
|
||||
= `in`(targetDatabase)
|
||||
|
||||
/**
|
||||
* Extension for [ReactiveNeo4jClient.RunnableSpecTightToDatabase.fetchAs] leveraging reified type parameters.
|
||||
* @author Michael J. Simons
|
||||
* @since 1.0
|
||||
*/
|
||||
inline fun <reified T : Any> ReactiveNeo4jClient.ReactiveRunnableSpecTightToDatabase.fetchAs(): Neo4jClient.MappingSpec<Mono<T>, Flux<T>, T>
|
||||
= fetchAs(T::class.java)
|
||||
|
||||
/**
|
||||
* Extension for [ReactiveNeo4jClient.RunnableSpecTightToDatabase.mappedBy] leveraging reified type parameters and removing
|
||||
* the need for an explicit `fetchAs`.
|
||||
* @author Michael J. Simons
|
||||
* @since 1.0
|
||||
*/
|
||||
inline fun <reified T : Any> ReactiveNeo4jClient.ReactiveRunnableSpecTightToDatabase.mappedBy(noinline mappingFunction: (TypeSystem, Record) -> T): Neo4jClient.RecordFetchSpec<Mono<T>, Flux<T>, T>
|
||||
= fetchAs(T::class.java).mappedBy(mappingFunction)
|
||||
|
||||
@@ -31,6 +31,7 @@ import org.neo4j.driver.Transaction;
|
||||
import org.neo4j.driver.Values;
|
||||
import org.neo4j.springframework.data.config.AbstractNeo4jConfig;
|
||||
import org.neo4j.springframework.data.integration.shared.KotlinPerson;
|
||||
import org.neo4j.springframework.data.integration.shared.KotlinRepository;
|
||||
import org.neo4j.springframework.data.repository.config.EnableNeo4jRepositories;
|
||||
import org.neo4j.springframework.data.test.Neo4jExtension.Neo4jConnectionSupport;
|
||||
import org.neo4j.springframework.data.test.Neo4jIntegrationTest;
|
||||
|
||||
@@ -46,4 +46,15 @@ class Neo4jClientExtensionsTest {
|
||||
|
||||
verify(exactly = 1) { ongoingDelegation.`in`("foobar") }
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `RunnableSpecTightToDatabase#fetchAs() extension should call its Java counterpart`() {
|
||||
|
||||
val runnableSpec = mockk<Neo4jClient.RunnableSpecTightToDatabase>(relaxed = true)
|
||||
|
||||
val mappingSpec: Neo4jClient.RecordFetchSpec<String?, Collection<String>, String> =
|
||||
runnableSpec.mappedBy { _, record -> "Foo" };
|
||||
|
||||
verify(exactly = 1) { runnableSpec.fetchAs(String::class.java) }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright (c) 2019 "Neo4j,"
|
||||
* Neo4j Sweden AB [https://neo4j.com]
|
||||
*
|
||||
* This file is part of Neo4j.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.neo4j.springframework.data.core
|
||||
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
/**
|
||||
* @author Michael J. Simons
|
||||
*/
|
||||
class PreparedQueryExtensionsTest {
|
||||
|
||||
@Test
|
||||
fun `PreparedQueryFactory call its Java counterpart`() {
|
||||
|
||||
val preparedQuery = PreparedQueryFactory(String::class)
|
||||
.withCypherQuery("RETURN 'Hallo'").
|
||||
build();
|
||||
|
||||
assertThat(preparedQuery.resultType).isEqualTo(String::class.java)
|
||||
}
|
||||
}
|
||||
@@ -21,6 +21,8 @@ package org.neo4j.springframework.data.core
|
||||
import io.mockk.mockk
|
||||
import io.mockk.verify
|
||||
import org.junit.jupiter.api.Test
|
||||
import reactor.core.publisher.Flux
|
||||
import reactor.core.publisher.Mono
|
||||
|
||||
/**
|
||||
* @author Michael J. Simons
|
||||
@@ -46,4 +48,15 @@ class ReactiveNeo4jClientExtensionsTest {
|
||||
|
||||
verify(exactly = 1) { ongoingDelegation.`in`("foobar") }
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `ReactiveRunnableDelegation#fetchAs() extension should call its Java counterpart`() {
|
||||
|
||||
val runnableSpec = mockk<ReactiveNeo4jClient.ReactiveRunnableSpecTightToDatabase>(relaxed = true)
|
||||
|
||||
val mappingSpec : Neo4jClient.MappingSpec<Mono<String>, Flux<String>, String> =
|
||||
runnableSpec.fetchAs();
|
||||
|
||||
verify(exactly = 1) { runnableSpec.fetchAs(String::class.java) }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
* Copyright (c) 2019 "Neo4j,"
|
||||
* Neo4j Sweden AB [https://neo4j.com]
|
||||
*
|
||||
* This file is part of Neo4j.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.neo4j.springframework.data.integration.imperative
|
||||
|
||||
import org.assertj.core.api.Assertions
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.junit.jupiter.api.AfterEach
|
||||
import org.junit.jupiter.api.BeforeEach
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.neo4j.driver.Driver
|
||||
import org.neo4j.driver.Values
|
||||
import org.neo4j.springframework.data.config.AbstractNeo4jConfig
|
||||
import org.neo4j.springframework.data.core.Neo4jClient
|
||||
import org.neo4j.springframework.data.core.fetchAs
|
||||
import org.neo4j.springframework.data.core.mappedBy
|
||||
import org.neo4j.springframework.data.test.Neo4jExtension
|
||||
import org.neo4j.springframework.data.test.Neo4jIntegrationTest
|
||||
import org.springframework.beans.factory.annotation.Autowired
|
||||
import org.springframework.context.annotation.Bean
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import org.springframework.transaction.annotation.EnableTransactionManagement
|
||||
|
||||
/**
|
||||
* Integration tests for using the Neo4j client in a Kotlin program.
|
||||
*
|
||||
* @author Michael J. Simons
|
||||
*/
|
||||
@Neo4jIntegrationTest
|
||||
class Neo4jClientKotlinInteropIT @Autowired constructor(
|
||||
private val driver: Driver,
|
||||
private val neo4jClient: Neo4jClient
|
||||
) {
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
private lateinit var neo4jConnectionSupport: Neo4jExtension.Neo4jConnectionSupport
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
fun prepareData() {
|
||||
|
||||
driver.session().use {
|
||||
val bands = mapOf(
|
||||
Pair("Queen", listOf("Brian", "Roger", "John", "Freddie")),
|
||||
Pair("Die Ärzte", listOf("Farin", "Rod", "Bela"))
|
||||
)
|
||||
|
||||
bands.forEach { b, m ->
|
||||
val summary = it.run("""
|
||||
CREATE (b:Band {name: ${'$'}band})
|
||||
WITH b
|
||||
UNWIND ${'$'}names AS name CREATE (n:Member {name: name}) <- [:HAS_MEMBER] - (b)
|
||||
""".trimIndent(), Values.parameters("band", b, "names", m)).summary()
|
||||
assertThat(summary.counters().nodesCreated()).isGreaterThan(0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
fun purgeData() {
|
||||
|
||||
driver.session().use { it.run("MATCH (n) DETACH DELETE n").consume() }
|
||||
}
|
||||
|
||||
data class Artist(val name: String)
|
||||
|
||||
data class Band(val name: String, val member: Collection<Artist>)
|
||||
|
||||
@Test
|
||||
fun `The Neo4j client should be usable from idiomatic Kotlin code`() {
|
||||
|
||||
val dieAerzte = neo4jClient
|
||||
.query(" MATCH (b:Band {name: \$name}) - [:HAS_MEMBER] -> (m) RETURN b as band, collect(m.name) as members")
|
||||
.bind("Die Ärzte").to("name")
|
||||
.mappedBy { _, r ->
|
||||
val members = r["members"].asList { v -> Artist(v.asString()) }
|
||||
Band(r["band"]["name"].asString(), members)
|
||||
}
|
||||
.one()
|
||||
|
||||
assertThat(dieAerzte).isNotNull
|
||||
assertThat(dieAerzte!!.member).hasSize(3)
|
||||
|
||||
if (neo4jClient.query("MATCH (n:IDontExists) RETURN id(n)").fetchAs<Long>().one() != null) {
|
||||
Assertions.fail<String>("The record does not exist, the optional had to be null")
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableTransactionManagement
|
||||
open class Config : AbstractNeo4jConfig() {
|
||||
|
||||
@Bean
|
||||
override fun driver(): Driver {
|
||||
return neo4jConnectionSupport.driver
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
* Copyright (c) 2019 "Neo4j,"
|
||||
* Neo4j Sweden AB [https://neo4j.com]
|
||||
*
|
||||
* This file is part of Neo4j.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.neo4j.springframework.data.integration.reactive
|
||||
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.junit.jupiter.api.AfterEach
|
||||
import org.junit.jupiter.api.BeforeEach
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.neo4j.driver.Driver
|
||||
import org.neo4j.driver.Values
|
||||
import org.neo4j.springframework.data.config.AbstractReactiveNeo4jConfig
|
||||
import org.neo4j.springframework.data.core.ReactiveNeo4jClient
|
||||
import org.neo4j.springframework.data.core.fetchAs
|
||||
import org.neo4j.springframework.data.core.mappedBy
|
||||
import org.neo4j.springframework.data.test.Neo4jExtension
|
||||
import org.neo4j.springframework.data.test.Neo4jIntegrationTest
|
||||
import org.springframework.beans.factory.annotation.Autowired
|
||||
import org.springframework.context.annotation.Bean
|
||||
import org.springframework.context.annotation.Configuration
|
||||
import org.springframework.transaction.annotation.EnableTransactionManagement
|
||||
import reactor.test.StepVerifier
|
||||
|
||||
/**
|
||||
* Integration tests for using the Neo4j client in a Kotlin program.
|
||||
*
|
||||
* @author Michael J. Simons
|
||||
*/
|
||||
@Neo4jIntegrationTest
|
||||
class ReactiveNeo4jClientKotlinInteropIT @Autowired constructor(
|
||||
private val driver: Driver,
|
||||
private val neo4jClient: ReactiveNeo4jClient
|
||||
) {
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
private lateinit var neo4jConnectionSupport: Neo4jExtension.Neo4jConnectionSupport
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
fun prepareData() {
|
||||
driver.session().use {
|
||||
val bands = mapOf(
|
||||
Pair("Queen", listOf("Brian", "Roger", "John", "Freddie")),
|
||||
Pair("Die Ärzte", listOf("Farin", "Rod", "Bela"))
|
||||
)
|
||||
|
||||
bands.forEach { b, m ->
|
||||
val summary = it.run("""
|
||||
CREATE (b:Band {name: ${'$'}band})
|
||||
WITH b
|
||||
UNWIND ${'$'}names AS name CREATE (n:Member {name: name}) <- [:HAS_MEMBER] - (b)
|
||||
""".trimIndent(), Values.parameters("band", b, "names", m)).summary()
|
||||
assertThat(summary.counters().nodesCreated()).isGreaterThan(0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
fun purgeData() {
|
||||
|
||||
driver.session().use { it.run("MATCH (n) DETACH DELETE n").consume() }
|
||||
}
|
||||
|
||||
data class Artist(val name: String)
|
||||
|
||||
data class Band(val name: String, val member: Collection<Artist>)
|
||||
|
||||
@Test
|
||||
fun `The reactive Neo4j client should be usable from idiomatic Kotlin code`() {
|
||||
|
||||
val queen = neo4jClient
|
||||
.query("MATCH (b:Band {name: \$name}) - [:HAS_MEMBER] -> (m) RETURN b as band, collect(m.name) as members")
|
||||
.bind("Queen").to("name")
|
||||
.mappedBy { _, r ->
|
||||
val members = r["members"].asList { v -> Artist(v.asString()) }
|
||||
Band(r["band"]["name"].asString(), members)
|
||||
}.one()
|
||||
|
||||
StepVerifier.create(queen)
|
||||
.expectNextMatches { it.name == "Queen" && it.member.size == 4 }
|
||||
.verifyComplete()
|
||||
|
||||
StepVerifier.create(neo4jClient.query("MATCH (n:IDontExists) RETURN id(n)").fetchAs<Long>().one())
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableTransactionManagement
|
||||
open class Config : AbstractReactiveNeo4jConfig() {
|
||||
|
||||
@Bean
|
||||
override fun driver(): Driver {
|
||||
return neo4jConnectionSupport.driver
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,9 +17,8 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.neo4j.springframework.data.integration.kotlin
|
||||
package org.neo4j.springframework.data.integration.shared
|
||||
|
||||
import org.neo4j.springframework.data.integration.shared.KotlinPerson
|
||||
import org.neo4j.springframework.data.repository.Neo4jRepository
|
||||
|
||||
/**
|
||||
Reference in New Issue
Block a user