diff --git a/spring-data-neo4j/pom.xml b/spring-data-neo4j/pom.xml
index da8b69fad..ad36913a8 100644
--- a/spring-data-neo4j/pom.xml
+++ b/spring-data-neo4j/pom.xml
@@ -125,7 +125,7 @@
org.jetbrains.kotlin
- kotlin-stdlib
+ kotlin-stdlib-jdk8
true
diff --git a/spring-data-neo4j/src/main/java/org/neo4j/springframework/data/core/Neo4jClient.java b/spring-data-neo4j/src/main/java/org/neo4j/springframework/data/core/Neo4jClient.java
index cd75eb2b4..25f4aa206 100644
--- a/spring-data-neo4j/src/main/java/org/neo4j/springframework/data/core/Neo4jClient.java
+++ b/spring-data-neo4j/src/main/java/org/neo4j/springframework/data/core/Neo4jClient.java
@@ -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) {
diff --git a/spring-data-neo4j/src/main/kotlin/org/neo4j/springframework/data/core/Neo4jClientExtensions.kt b/spring-data-neo4j/src/main/kotlin/org/neo4j/springframework/data/core/Neo4jClientExtensions.kt
index 53b432672..40d828ebd 100644
--- a/spring-data-neo4j/src/main/kotlin/org/neo4j/springframework/data/core/Neo4jClientExtensions.kt
+++ b/spring-data-neo4j/src/main/kotlin/org/neo4j/springframework/data/core/Neo4jClientExtensions.kt
@@ -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 Neo4jClient.OngoingDelegation.inDatabase(targetDatabase: String): Neo4jClient.RunnableDelegation = `in`(targetDatabase)
+fun Neo4jClient.OngoingDelegation.inDatabase(targetDatabase: String): Neo4jClient.RunnableDelegation =
+ `in`(targetDatabase)
+
+/**
+ * An implementation of a fetch spec that replaces Java's Optional with a nullable.
+ * @author Michael J. Simons
+ */
+class DelegatingFetchSpec(private val delegate: Neo4jClient.RecordFetchSpec, Collection, T>) : Neo4jClient.RecordFetchSpec, T> {
+ override fun one(): T? = delegate.one().orElse(null)
+
+ override fun first(): T = delegate.first().orElse(null)
+
+ override fun all(): Collection = delegate.all()
+}
+
+/**
+ * An implementation of a mapping spec that replaces Java's Optional with a nullable.
+ * @author Michael J. Simons
+ */
+class DelegatingMappingSpec(private val delegate: Neo4jClient.MappingSpec, Collection, T>) : Neo4jClient.MappingSpec, T> {
+ override fun mappedBy(mappingFunction: BiFunction): Neo4jClient.RecordFetchSpec, 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 = delegate.all()
+}
+
+/**
+ * Extension for [Neo4jClient.RunnableSpecTightToDatabase.fetchAs] leveraging reified type parameters.
+ * @author Michael J. Simons
+ * @since 1.0
+ */
+inline fun Neo4jClient.RunnableSpecTightToDatabase.fetchAs(): Neo4jClient.MappingSpec, 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 Neo4jClient.RunnableSpecTightToDatabase.mappedBy(noinline mappingFunction: (TypeSystem, Record) -> T): Neo4jClient.RecordFetchSpec, T>
+ = DelegatingFetchSpec(fetchAs(T::class.java).mappedBy(mappingFunction))
diff --git a/spring-data-neo4j/src/main/kotlin/org/neo4j/springframework/data/core/PreparedQueryExtensions.kt b/spring-data-neo4j/src/main/kotlin/org/neo4j/springframework/data/core/PreparedQueryExtensions.kt
new file mode 100644
index 000000000..fc050ca3d
--- /dev/null
+++ b/spring-data-neo4j/src/main/kotlin/org/neo4j/springframework/data/core/PreparedQueryExtensions.kt
@@ -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(val c: KClass) {
+ fun withCypherQuery(cypherQuery: String): PreparedQuery.OptionalBuildSteps = c.javaObjectType.let { PreparedQuery.queryFor(it) }
+ .withCypherQuery(cypherQuery)
+}
diff --git a/spring-data-neo4j/src/main/kotlin/org/neo4j/springframework/data/core/ReactiveNeo4jClientExtensions.kt b/spring-data-neo4j/src/main/kotlin/org/neo4j/springframework/data/core/ReactiveNeo4jClientExtensions.kt
index a7f1ab6bb..864d37e54 100644
--- a/spring-data-neo4j/src/main/kotlin/org/neo4j/springframework/data/core/ReactiveNeo4jClientExtensions.kt
+++ b/spring-data-neo4j/src/main/kotlin/org/neo4j/springframework/data/core/ReactiveNeo4jClientExtensions.kt
@@ -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 ReactiveNeo4jClient.OngoingReactiveDelegation.inDatabase(targetDatabase: String): ReactiveNeo4jClient.ReactiveRunnableDelegation = `in`(targetDatabase)
+fun ReactiveNeo4jClient.OngoingReactiveDelegation.inDatabase(targetDatabase: String): ReactiveNeo4jClient.ReactiveRunnableDelegation
+ = `in`(targetDatabase)
+
+/**
+ * Extension for [ReactiveNeo4jClient.RunnableSpecTightToDatabase.fetchAs] leveraging reified type parameters.
+ * @author Michael J. Simons
+ * @since 1.0
+ */
+inline fun ReactiveNeo4jClient.ReactiveRunnableSpecTightToDatabase.fetchAs(): Neo4jClient.MappingSpec, Flux, 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 ReactiveNeo4jClient.ReactiveRunnableSpecTightToDatabase.mappedBy(noinline mappingFunction: (TypeSystem, Record) -> T): Neo4jClient.RecordFetchSpec, Flux, T>
+ = fetchAs(T::class.java).mappedBy(mappingFunction)
diff --git a/spring-data-neo4j/src/test/java/org/neo4j/springframework/data/integration/kotlin/KotlinIT.java b/spring-data-neo4j/src/test/java/org/neo4j/springframework/data/integration/kotlin/KotlinIT.java
index d44348c71..d6437f94e 100644
--- a/spring-data-neo4j/src/test/java/org/neo4j/springframework/data/integration/kotlin/KotlinIT.java
+++ b/spring-data-neo4j/src/test/java/org/neo4j/springframework/data/integration/kotlin/KotlinIT.java
@@ -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;
diff --git a/spring-data-neo4j/src/test/kotlin/org/neo4j/springframework/data/core/Neo4jClientExtensionsTest.kt b/spring-data-neo4j/src/test/kotlin/org/neo4j/springframework/data/core/Neo4jClientExtensionsTest.kt
index 0c84724af..8949d4f5f 100644
--- a/spring-data-neo4j/src/test/kotlin/org/neo4j/springframework/data/core/Neo4jClientExtensionsTest.kt
+++ b/spring-data-neo4j/src/test/kotlin/org/neo4j/springframework/data/core/Neo4jClientExtensionsTest.kt
@@ -46,4 +46,15 @@ class Neo4jClientExtensionsTest {
verify(exactly = 1) { ongoingDelegation.`in`("foobar") }
}
-}
\ No newline at end of file
+
+ @Test
+ fun `RunnableSpecTightToDatabase#fetchAs() extension should call its Java counterpart`() {
+
+ val runnableSpec = mockk(relaxed = true)
+
+ val mappingSpec: Neo4jClient.RecordFetchSpec, String> =
+ runnableSpec.mappedBy { _, record -> "Foo" };
+
+ verify(exactly = 1) { runnableSpec.fetchAs(String::class.java) }
+ }
+}
diff --git a/spring-data-neo4j/src/test/kotlin/org/neo4j/springframework/data/core/PreparedQueryExtensionsTest.kt b/spring-data-neo4j/src/test/kotlin/org/neo4j/springframework/data/core/PreparedQueryExtensionsTest.kt
new file mode 100644
index 000000000..eb1014765
--- /dev/null
+++ b/spring-data-neo4j/src/test/kotlin/org/neo4j/springframework/data/core/PreparedQueryExtensionsTest.kt
@@ -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)
+ }
+}
diff --git a/spring-data-neo4j/src/test/kotlin/org/neo4j/springframework/data/core/ReactiveNeo4jClientExtensionsTest.kt b/spring-data-neo4j/src/test/kotlin/org/neo4j/springframework/data/core/ReactiveNeo4jClientExtensionsTest.kt
index 85f72a511..fbde43f8a 100644
--- a/spring-data-neo4j/src/test/kotlin/org/neo4j/springframework/data/core/ReactiveNeo4jClientExtensionsTest.kt
+++ b/spring-data-neo4j/src/test/kotlin/org/neo4j/springframework/data/core/ReactiveNeo4jClientExtensionsTest.kt
@@ -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") }
}
-}
\ No newline at end of file
+
+ @Test
+ fun `ReactiveRunnableDelegation#fetchAs() extension should call its Java counterpart`() {
+
+ val runnableSpec = mockk(relaxed = true)
+
+ val mappingSpec : Neo4jClient.MappingSpec, Flux, String> =
+ runnableSpec.fetchAs();
+
+ verify(exactly = 1) { runnableSpec.fetchAs(String::class.java) }
+ }
+}
diff --git a/spring-data-neo4j/src/test/kotlin/org/neo4j/springframework/data/integration/imperative/Neo4jClientKotlinInteropIT.kt b/spring-data-neo4j/src/test/kotlin/org/neo4j/springframework/data/integration/imperative/Neo4jClientKotlinInteropIT.kt
new file mode 100644
index 000000000..e5bda3ba7
--- /dev/null
+++ b/spring-data-neo4j/src/test/kotlin/org/neo4j/springframework/data/integration/imperative/Neo4jClientKotlinInteropIT.kt
@@ -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)
+
+ @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().one() != null) {
+ Assertions.fail("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
+ }
+ }
+}
diff --git a/spring-data-neo4j/src/test/kotlin/org/neo4j/springframework/data/integration/reactive/ReactiveNeo4jClientKotlinInteropIT.kt b/spring-data-neo4j/src/test/kotlin/org/neo4j/springframework/data/integration/reactive/ReactiveNeo4jClientKotlinInteropIT.kt
new file mode 100644
index 000000000..df9645281
--- /dev/null
+++ b/spring-data-neo4j/src/test/kotlin/org/neo4j/springframework/data/integration/reactive/ReactiveNeo4jClientKotlinInteropIT.kt
@@ -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)
+
+ @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().one())
+ .verifyComplete();
+ }
+
+ @Configuration
+ @EnableTransactionManagement
+ open class Config : AbstractReactiveNeo4jConfig() {
+
+ @Bean
+ override fun driver(): Driver {
+ return neo4jConnectionSupport.driver
+ }
+ }
+}
diff --git a/spring-data-neo4j/src/test/kotlin/org/neo4j/springframework/data/integration/kotlin/KotlinRepository.kt b/spring-data-neo4j/src/test/kotlin/org/neo4j/springframework/data/integration/shared/KotlinRepository.kt
similarity index 86%
rename from spring-data-neo4j/src/test/kotlin/org/neo4j/springframework/data/integration/kotlin/KotlinRepository.kt
rename to spring-data-neo4j/src/test/kotlin/org/neo4j/springframework/data/integration/shared/KotlinRepository.kt
index 786a945b8..baf791bff 100644
--- a/spring-data-neo4j/src/test/kotlin/org/neo4j/springframework/data/integration/kotlin/KotlinRepository.kt
+++ b/spring-data-neo4j/src/test/kotlin/org/neo4j/springframework/data/integration/shared/KotlinRepository.kt
@@ -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
/**