Skip to content

Commit

Permalink
Resolve cypher query deprecations
Browse files Browse the repository at this point in the history
  • Loading branch information
MarcelKonrad authored and manuelprinz committed Feb 12, 2025
1 parent 98e6c38 commit 5f6a7ef
Show file tree
Hide file tree
Showing 53 changed files with 348 additions and 214 deletions.
4 changes: 3 additions & 1 deletion common/neo4j-dsl/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
@file:Suppress("UnstableApiUsage")

plugins {
id("org.orkg.gradle.kotlin")
id("org.orkg.gradle.spring-library")
}

dependencies {
api("org.neo4j.driver:neo4j-java-driver")
api("org.neo4j:neo4j-cypher-dsl")
api("org.springframework.data:spring-data-commons")
api("org.springframework.data:spring-data-neo4j")
api("org.springframework:spring-context")
api("org.apiguardian:apiguardian-api")
}

testing {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import org.neo4j.cypherdsl.core.ResultStatement
import org.neo4j.cypherdsl.core.Statement
import org.neo4j.cypherdsl.core.StatementBuilder.BuildableStatement
import org.neo4j.cypherdsl.core.StatementBuilder.TerminalExposesSkip
import org.neo4j.cypherdsl.core.renderer.Configuration
import org.neo4j.cypherdsl.core.renderer.Renderer
import org.neo4j.driver.Record
import org.neo4j.driver.summary.ResultSummary
import org.neo4j.driver.types.TypeSystem
Expand All @@ -21,36 +23,45 @@ import org.springframework.data.domain.Pageable
import org.springframework.data.neo4j.core.Neo4jClient

interface QueryCache {
fun getOrPut(key: Any, valueSupplier: () -> Statement): Statement
fun getOrPut(key: Any, valueSupplier: () -> ConfigurationAwareStatement): ConfigurationAwareStatement

object Uncached : QueryCache {
override fun getOrPut(key: Any, valueSupplier: () -> Statement): Statement =
override fun getOrPut(key: Any, valueSupplier: () -> ConfigurationAwareStatement): ConfigurationAwareStatement =
valueSupplier()
}
}

data class CypherQueryBuilder(
data class CypherQueryBuilderFactory(
private val configuration: Configuration,
private val neo4jClient: Neo4jClient,
private val queryCache: QueryCache = DefaultQueryCache
) {
fun <T : Statement> withQuery(
query: () -> BuildableStatement<T>
): SingleQueryBuilder.ExposesWithParameterAndFetchAsAndRun =
SingleQueryBuilderImpl.WithParameterAndFetchAsAndRunBuilder(neo4jClient, queryCache, query)

fun <T> withCommonQuery(commonQuery: () -> T): PagedQueryBuilder.ExposesPagedWithQuery<T> =
PagedQueryBuilderImpl.PagedWithQueryBuilder(neo4jClient, queryCache, commonQuery)
fun newBuilder(queryCache: QueryCache = DefaultQueryCache): CypherQueryBuilder =
CypherQueryBuilder(configuration, neo4jClient, queryCache)

companion object {
private object DefaultQueryCache : QueryCache {
private val cache: MutableMap<Any, Statement> = mutableMapOf()
private val cache: MutableMap<Any, ConfigurationAwareStatement> = mutableMapOf()

override fun getOrPut(key: Any, valueSupplier: () -> Statement): Statement =
override fun getOrPut(key: Any, valueSupplier: () -> ConfigurationAwareStatement): ConfigurationAwareStatement =
cache.getOrPut(key, valueSupplier)
}
}
}

data class CypherQueryBuilder(
private val configuration: Configuration,
private val neo4jClient: Neo4jClient,
private val queryCache: QueryCache
) {
fun <T : Statement> withQuery(
query: () -> BuildableStatement<T>
): SingleQueryBuilder.ExposesWithParameterAndFetchAsAndRun =
SingleQueryBuilderImpl.WithParameterAndFetchAsAndRunBuilder(configuration, neo4jClient, queryCache, query)

fun <T> withCommonQuery(commonQuery: () -> T): PagedQueryBuilder.ExposesPagedWithQuery<T> =
PagedQueryBuilderImpl.PagedWithQueryBuilder(configuration, neo4jClient, queryCache, commonQuery)
}

object SingleQueryBuilder {
interface ExposesWithParameters {
fun withParameters(vararg parameters: Pair<String, Any?>): ExposesFetchAsAndRun =
Expand Down Expand Up @@ -101,14 +112,15 @@ object SingleQueryBuilder {

class SingleQueryBuilderImpl {
data class WithParameterAndFetchAsAndRunBuilder<T : Statement>(
private val configuration: Configuration,
private val neo4jClient: Neo4jClient,
private val queryCache: QueryCache,
private val query: () -> BuildableStatement<T>
) : SingleQueryBuilder.ExposesWithParameterAndFetchAsAndRun {
override fun withParameters(
parameters: Map<String, Any?>
): SingleQueryBuilder.ExposesFetchAsAndRun =
FetchAsAndRunBuilder(neo4jClient, queryCache, query, parameters)
FetchAsAndRunBuilder(configuration, neo4jClient, queryCache, query, parameters)

override fun <T : Any> fetchAs(targetClass: KClass<T>): SingleQueryBuilder.ExposesMappedByAndFetch<T> =
withParameters(emptyMap()).fetchAs(targetClass)
Expand All @@ -118,35 +130,40 @@ class SingleQueryBuilderImpl {
}

data class FetchAsAndRunBuilder<T : Statement>(
private val configuration: Configuration,
private val neo4jClient: Neo4jClient,
private val queryCache: QueryCache,
private val query: () -> BuildableStatement<T>,
private val parameters: Map<String, Any?>
) : SingleQueryBuilder.ExposesFetchAsAndRun {
override fun <T : Any> fetchAs(targetClass: KClass<T>): SingleQueryBuilder.ExposesMappedByAndFetch<T> =
MappedByBuilderAndFetch(neo4jClient, queryCache, query, parameters, targetClass)
MappedByBuilderAndFetch(configuration, neo4jClient, queryCache, query, parameters, targetClass)

override fun run(): ResultSummary =
neo4jClient.query(queryCache.getOrPut(query) { query().build() }.cypher)
neo4jClient.query(queryCache.getOrPut(cacheKey()) { query().build(configuration) }.cypher)
.bindAll(parameters)
.run()

private fun cacheKey(): Pair<() -> BuildableStatement<T>, Configuration> = query to configuration
}

data class MappedByBuilderAndFetch<T : Statement, R : Any>(
private val configuration: Configuration,
private val neo4jClient: Neo4jClient,
private val queryCache: QueryCache,
private val query: () -> BuildableStatement<T>,
private val parameters: Map<String, Any?>,
private val targetClass: KClass<R>
) : SingleQueryBuilder.ExposesMappedByAndFetch<R> {
override fun mappedBy(mappingFunction: (TypeSystem, Record) -> R): SingleQueryBuilder.ExposesFetch<R> =
FetchBuilder(neo4jClient, queryCache, query, parameters, targetClass, mappingFunction)
FetchBuilder(configuration, neo4jClient, queryCache, query, parameters, targetClass, mappingFunction)

override fun fetch(): Neo4jClient.RecordFetchSpec<R> =
FetchBuilder(neo4jClient, queryCache, query, parameters, targetClass).fetch()
FetchBuilder(configuration, neo4jClient, queryCache, query, parameters, targetClass).fetch()
}

data class FetchBuilder<T : Statement, R : Any>(
private val configuration: Configuration,
private val neo4jClient: Neo4jClient,
private val queryCache: QueryCache,
private val query: () -> BuildableStatement<T>,
Expand All @@ -155,10 +172,12 @@ class SingleQueryBuilderImpl {
private val mappingFunction: ((TypeSystem, Record) -> R)? = null
) : SingleQueryBuilder.ExposesFetch<R> {
override fun fetch(): Neo4jClient.RecordFetchSpec<R> =
neo4jClient.query(queryCache.getOrPut(query) { query().build() }.cypher)
neo4jClient.query(queryCache.getOrPut(cacheKey()) { query().build(configuration) }.cypher)
.bindAll(parameters)
.fetchAs(targetClass.java)
.let { if (mappingFunction != null) it.mappedBy(mappingFunction) else it }

private fun cacheKey(): Pair<() -> BuildableStatement<T>, Configuration> = query to configuration
}
}

Expand Down Expand Up @@ -218,17 +237,19 @@ object PagedQueryBuilder {

class PagedQueryBuilderImpl {
data class PagedWithQueryBuilder<T>(
private val configuration: Configuration,
private val neo4jClient: Neo4jClient,
private val queryCache: QueryCache,
private val commonQuery: () -> T
) : PagedQueryBuilder.ExposesPagedWithQuery<T> {
override fun withQuery(
query: (T) -> TerminalExposesSkip
): PagedQueryBuilder.ExposesPagedWithCountQuery<T> =
PagedWithCountQueryBuilder(neo4jClient, queryCache, commonQuery, query)
PagedWithCountQueryBuilder(configuration, neo4jClient, queryCache, commonQuery, query)
}

data class PagedWithCountQueryBuilder<T>(
private val configuration: Configuration,
private val neo4jClient: Neo4jClient,
private val queryCache: QueryCache,
private val commonQuery: () -> T,
Expand All @@ -237,10 +258,11 @@ class PagedQueryBuilderImpl {
override fun withCountQuery(
countQuery: (T) -> BuildableStatement<ResultStatement>
): PagedQueryBuilder.ExposesPagedWithParametersAndFetchAs =
PagedWithParametersAndFetchAsBuilder(neo4jClient, queryCache, commonQuery, query, countQuery)
PagedWithParametersAndFetchAsBuilder(configuration, neo4jClient, queryCache, commonQuery, query, countQuery)
}

data class PagedWithParametersAndFetchAsBuilder<T>(
private val configuration: Configuration,
private val neo4jClient: Neo4jClient,
private val queryCache: QueryCache,
private val commonQuery: () -> T,
Expand All @@ -250,7 +272,7 @@ class PagedQueryBuilderImpl {
override fun withParameters(
parameters: Map<String, Any?>
): PagedQueryBuilder.ExposesPagedFetchAs =
PagedFetchAsBuilder(neo4jClient, queryCache, commonQuery, query, countQuery, parameters)
PagedFetchAsBuilder(configuration, neo4jClient, queryCache, commonQuery, query, countQuery, parameters)

override fun <T : Any> fetchAs(
targetClass: KClass<T>
Expand All @@ -259,6 +281,7 @@ class PagedQueryBuilderImpl {
}

data class PagedFetchAsBuilder<T>(
private val configuration: Configuration,
private val neo4jClient: Neo4jClient,
private val queryCache: QueryCache,
private val commonQuery: () -> T,
Expand All @@ -270,11 +293,12 @@ class PagedQueryBuilderImpl {
targetClass: KClass<R>
): PagedQueryBuilder.ExposesPagedMappedByAndFetch<R> =
PagedMappedByAndFetchBuilder(
neo4jClient, queryCache, commonQuery, query, countQuery, parameters, targetClass
configuration, neo4jClient, queryCache, commonQuery, query, countQuery, parameters, targetClass
)
}

data class PagedMappedByAndFetchBuilder<T, R : Any>(
private val configuration: Configuration,
private val neo4jClient: Neo4jClient,
private val queryCache: QueryCache,
private val commonQuery: () -> T,
Expand All @@ -288,17 +312,17 @@ class PagedQueryBuilderImpl {
mappingFunction: (TypeSystem, Record) -> R
): PagedQueryBuilder.ExposesPagedFetchAndCount<R> =
PagedMappedByAndFetchBuilder(
neo4jClient, queryCache, commonQuery, query, countQuery, parameters, targetClass, mappingFunction
configuration, neo4jClient, queryCache, commonQuery, query, countQuery, parameters, targetClass, mappingFunction
)

override fun fetch(pageable: Pageable, appendSort: Boolean): Page<R> {
val contentQuery = queryCache.getOrPut(commonQuery to query) {
val contentQuery = queryCache.getOrPut(cacheKey(query)) {
query(commonQuery())
.skip(parameter("skip"))
.limit(parameter("limit"))
.build()
.build(configuration)
}
val content = neo4jClient.query(if (appendSort) contentQuery.cypher.sortedWith(pageable.sort) else contentQuery.cypher)
val content = neo4jClient.query(contentQuery.cypher.let { if (appendSort) it.sortedWith(pageable.sort) else it })
.bindAll(parameters + ("skip" to pageable.offset) + ("limit" to pageable.pageSize) + contentQuery.catalog.parameters)
.fetchAs(targetClass.java)
.let { if (mappingFunction != null) it.mappedBy(mappingFunction) else it }
Expand All @@ -308,9 +332,9 @@ class PagedQueryBuilderImpl {
}

override fun count(): Long {
val countQuery = queryCache.getOrPut(commonQuery to countQuery) {
val countQuery = queryCache.getOrPut(cacheKey(countQuery)) {
countQuery(commonQuery())
.build()
.build(configuration)
}
val count = neo4jClient.query(countQuery.cypher)
.bindAll(parameters + countQuery.catalog.parameters)
Expand All @@ -319,5 +343,20 @@ class PagedQueryBuilderImpl {
.orElse(0)
return count
}

private fun cacheKey(query: Any) =
Triple(commonQuery, query, configuration)
}
}

data class ConfigurationAwareStatement(
private val statement: Statement,
private val configuration: Configuration
) : Statement by statement {
private val queryString by lazy { Renderer.getRenderer(configuration).render(statement) }

override fun getCypher(): String = synchronized(this) { queryString }
}

private fun <T : Statement> BuildableStatement<T>.build(configuration: Configuration): ConfigurationAwareStatement =
ConfigurationAwareStatement(build(), configuration)
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package org.orkg.common.neo4jdsl.configuration

import org.orkg.common.neo4jdsl.CypherQueryBuilderFactory
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.data.neo4j.core.Neo4jClient
import org.neo4j.cypherdsl.core.renderer.Configuration as CypherConfiguration

@Configuration
class CypherQueryBuilderConfiguration {
@Bean
fun cypherQueryBuilderFactory(
configuration: CypherConfiguration,
neo4jClient: Neo4jClient
): CypherQueryBuilderFactory =
CypherQueryBuilderFactory(configuration, neo4jClient)
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,15 @@ import org.neo4j.cypherdsl.core.Cypher.anyNode
import org.neo4j.cypherdsl.core.Cypher.count
import org.neo4j.cypherdsl.core.Cypher.match
import org.neo4j.cypherdsl.core.Cypher.name
import org.neo4j.cypherdsl.core.Statement
import org.neo4j.cypherdsl.core.renderer.Configuration
import org.neo4j.driver.summary.ResultSummary
import org.orkg.common.testing.fixtures.MockkBaseTest
import org.springframework.data.domain.PageRequest
import org.springframework.data.domain.Sort
import org.springframework.data.neo4j.core.Neo4jClient

internal class CypherQueryBuilderUnitTest : MockkBaseTest {
private val configuration: Configuration = Configuration.defaultConfig()
private val neo4jClient: Neo4jClient = mockk()
private val unboundRunnableSpec: Neo4jClient.UnboundRunnableSpec = mockk()
private val runnableSpec: Neo4jClient.RunnableSpec = mockk()
Expand All @@ -26,9 +27,9 @@ internal class CypherQueryBuilderUnitTest : MockkBaseTest {
private val longMappingSpec: Neo4jClient.MappingSpec<Long> = mockk()

class SimpleQueryCache : QueryCache {
private val cache: MutableMap<Any, Statement> = mutableMapOf()
private val cache: MutableMap<Any, ConfigurationAwareStatement> = mutableMapOf()

override fun getOrPut(key: Any, valueSupplier: () -> Statement): Statement =
override fun getOrPut(key: Any, valueSupplier: () -> ConfigurationAwareStatement): ConfigurationAwareStatement =
cache.getOrPut(key, valueSupplier)

val size: Int get() = cache.size
Expand All @@ -43,7 +44,7 @@ internal class CypherQueryBuilderUnitTest : MockkBaseTest {
every { runnableSpec.run() } returns resultSummary

val runQuery = {
CypherQueryBuilder(neo4jClient, queryCache)
CypherQueryBuilder(configuration, neo4jClient, queryCache)
.withQuery {
val node = anyNode().named("node")
match(node).returning(count(node))
Expand Down Expand Up @@ -74,7 +75,7 @@ internal class CypherQueryBuilderUnitTest : MockkBaseTest {
every { longMappingSpec.one() } returns Optional.of(1L)

val runQuery = {
CypherQueryBuilder(neo4jClient, queryCache)
CypherQueryBuilder(configuration, neo4jClient, queryCache)
.withCommonQuery {
match(anyNode().named("node"))
}
Expand Down Expand Up @@ -104,14 +105,16 @@ internal class CypherQueryBuilderUnitTest : MockkBaseTest {

@Test
fun `paged query builder does not add order by clause when unsorted`() {
val queryCache = SimpleQueryCache()

every { neo4jClient.query(any<String>()) } returns unboundRunnableSpec
every { unboundRunnableSpec.bindAll(any()) } returns runnableSpec
every { runnableSpec.fetchAs(String::class.java) } returns stringMappingSpec
every { stringMappingSpec.all() } returns listOf("result")
every { runnableSpec.fetchAs(Long::class.java) } returns longMappingSpec
every { longMappingSpec.one() } returns Optional.of(1L)

CypherQueryBuilder(neo4jClient)
CypherQueryBuilder(configuration, neo4jClient, queryCache)
.withCommonQuery {
match(anyNode().named("node"))
}
Expand All @@ -135,6 +138,8 @@ internal class CypherQueryBuilderUnitTest : MockkBaseTest {

@Test
fun `paged query builder generates order by clause correctly`() {
val queryCache = SimpleQueryCache()

every { neo4jClient.query(any<String>()) } returns unboundRunnableSpec
every { unboundRunnableSpec.bindAll(any()) } returns runnableSpec
every { runnableSpec.fetchAs(String::class.java) } returns stringMappingSpec
Expand All @@ -144,7 +149,7 @@ internal class CypherQueryBuilderUnitTest : MockkBaseTest {

val sort = Sort.by("property1").ascending().and(Sort.by("property2").descending())

CypherQueryBuilder(neo4jClient)
CypherQueryBuilder(configuration, neo4jClient, queryCache)
.withCommonQuery {
match(anyNode().named("node"))
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,6 @@ dependencies {
api(project(":graph:graph-ports-output"))
api("org.neo4j.driver:neo4j-java-driver")
api("org.neo4j:neo4j-cypher-dsl")
implementation(project(":common:neo4j-dsl"))
api(project(":common:neo4j-dsl"))
implementation("dev.forkhandles:values4k")
}
Loading

0 comments on commit 5f6a7ef

Please sign in to comment.