Skip to content

Commit 1f712b6

Browse files
committed
Add support for neo4j 5.x
resolves #281
1 parent f37f1de commit 1f712b6

File tree

19 files changed

+103
-64
lines changed

19 files changed

+103
-64
lines changed

core/pom.xml

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
<dependency>
1919
<groupId>org.neo4j.driver</groupId>
2020
<artifactId>neo4j-java-driver</artifactId>
21-
<version>4.4.9</version>
21+
<version>5.2.0</version>
2222
<scope>test</scope>
2323
</dependency>
2424
<dependency>
@@ -35,9 +35,14 @@
3535
</dependency>
3636
<dependency>
3737
<groupId>org.neo4j.procedure</groupId>
38-
<artifactId>apoc</artifactId>
38+
<artifactId>apoc-core</artifactId>
39+
<version>${neo4j-apoc.version}</version>
40+
<scope>test</scope>
41+
</dependency>
42+
<dependency>
43+
<groupId>org.neo4j.procedure</groupId>
44+
<artifactId>apoc-common</artifactId>
3945
<version>${neo4j-apoc.version}</version>
40-
<classifier>all</classifier>
4146
<scope>test</scope>
4247
</dependency>
4348
<dependency>
@@ -68,12 +73,12 @@
6873
<dependency>
6974
<groupId>org.neo4j</groupId>
7075
<artifactId>neo4j-cypher-dsl</artifactId>
71-
<version>2022.7.2</version>
76+
<version>2022.8.1</version>
7277
</dependency>
7378
<dependency>
7479
<groupId>ch.qos.logback</groupId>
7580
<artifactId>logback-classic</artifactId>
76-
<version>1.4.0</version>
81+
<version>1.4.5</version>
7782
<scope>test</scope>
7883
</dependency>
7984
<dependency>
@@ -110,7 +115,7 @@
110115
<dependency>
111116
<groupId>org.slf4j</groupId>
112117
<artifactId>slf4j-api</artifactId>
113-
<version>2.0.0</version>
118+
<version>2.0.5</version>
114119
</dependency>
115120
</dependencies>
116121
</dependencyManagement>

core/src/main/kotlin/org/neo4j/graphql/GraphQLExtensions.kt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package org.neo4j.graphql
22

3+
import graphql.GraphQLContext
34
import graphql.Scalars
45
import graphql.language.*
56
import graphql.language.TypeDefinition
@@ -94,6 +95,7 @@ fun GraphQLFieldsContainer.label(): String = when {
9495
?.getDirective(DirectiveConstants.RELATION)
9596
?.getArgument(RELATION_NAME)?.argumentValue?.value?.toJavaValue()?.toString()
9697
?: this.name
98+
9799
else -> name
98100
}
99101

@@ -232,6 +234,11 @@ fun DataFetchingEnvironment.typeAsContainer() = this.fieldDefinition.type.inner(
232234
?: throw IllegalStateException("expect type of field ${this.logField()} to be GraphQLFieldsContainer, but was ${this.fieldDefinition.type.name()}")
233235

234236
fun DataFetchingEnvironment.logField() = "${this.parentType.name()}.${this.fieldDefinition.name}"
237+
fun DataFetchingEnvironment.queryContext(): QueryContext = this.graphQlContext.get(QueryContext.KEY) ?: QueryContext()
238+
fun GraphQLContext.setQueryContext(ctx: QueryContext): GraphQLContext {
239+
this.put(QueryContext.KEY, ctx)
240+
return this
241+
}
235242

236243
val TypeInt = TypeName("Int")
237244
val TypeFloat = TypeName("Float")

core/src/main/kotlin/org/neo4j/graphql/QueryContext.kt

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package org.neo4j.graphql
22

3+
import org.neo4j.cypherdsl.core.renderer.Dialect
4+
35
data class QueryContext @JvmOverloads constructor(
46
/**
57
* if true the <code>__typename</code> will always be returned for interfaces, no matter if it was queried or not
@@ -9,7 +11,9 @@ data class QueryContext @JvmOverloads constructor(
911
/**
1012
* If set alternative approaches for query translation will be used
1113
*/
12-
var optimizedQuery: Set<OptimizationStrategy>? = null
14+
var optimizedQuery: Set<OptimizationStrategy>? = null,
15+
16+
var neo4jDialect: Dialect = Dialect.NEO4J_5
1317

1418
) {
1519
enum class OptimizationStrategy {
@@ -18,4 +22,8 @@ data class QueryContext @JvmOverloads constructor(
1822
*/
1923
FILTER_AS_MATCH
2024
}
25+
26+
companion object {
27+
const val KEY = "Neo4jGraphQLQueryContext"
28+
}
2129
}

core/src/main/kotlin/org/neo4j/graphql/Translator.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import graphql.schema.GraphQLSchema
66

77
class Translator(val schema: GraphQLSchema) {
88

9-
class CypherHolder(val cyphers :MutableList<Cypher> = mutableListOf())
9+
class CypherHolder(val cyphers: MutableList<Cypher> = mutableListOf())
1010

1111
private val gql: GraphQL = GraphQL.newGraphQL(schema).build()
1212

@@ -17,7 +17,7 @@ class Translator(val schema: GraphQLSchema) {
1717
val executionInput = ExecutionInput.newExecutionInput()
1818
.query(query)
1919
.variables(params)
20-
.context(ctx)
20+
.graphQLContext(mapOf(QueryContext.KEY to ctx))
2121
.localContext(cypherHolder)
2222
.build()
2323
val result = gql.execute(executionInput)

core/src/main/kotlin/org/neo4j/graphql/handler/BaseDataFetcher.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,12 @@ abstract class BaseDataFetcher(schemaConfig: SchemaConfig) : ProjectionBase(sche
2525
val variable = field.aliasOrName().decapitalize()
2626
prepareDataFetcher(env.fieldDefinition, env.parentType)
2727
val statement = generateCypher(variable, field, env)
28-
28+
val dialect = env.queryContext().neo4jDialect
2929
val query = Renderer.getRenderer(Configuration
3030
.newConfig()
3131
.withIndentStyle(Configuration.IndentStyle.TAB)
3232
.withPrettyPrint(true)
33+
.withDialect(dialect)
3334
.build()
3435
).render(statement)
3536

core/src/main/kotlin/org/neo4j/graphql/handler/QueryHandler.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,11 +107,12 @@ class QueryHandler private constructor(schemaConfig: SchemaConfig) : BaseDataFet
107107
val (propertyContainer, match) = when {
108108
type.isRelationType() -> anyNode().relationshipTo(anyNode(), type.label()).named(variable)
109109
.let { rel -> rel to match(rel) }
110+
110111
else -> node(type.label()).named(variable)
111112
.let { node -> node to match(node) }
112113
}
113114

114-
val ongoingReading = if ((env.getContext() as? QueryContext)?.optimizedQuery?.contains(QueryContext.OptimizationStrategy.FILTER_AS_MATCH) == true) {
115+
val ongoingReading = if (env.queryContext().optimizedQuery?.contains(QueryContext.OptimizationStrategy.FILTER_AS_MATCH) == true) {
115116

116117
OptimizedFilterHandler(type, schemaConfig).generateFilterQuery(variable, fieldDefinition, env.arguments, match, propertyContainer, env.variables)
117118

core/src/main/kotlin/org/neo4j/graphql/handler/projection/ProjectionBase.kt

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,7 @@ open class ProjectionBase(
217217
values.size > 1 -> values.mapIndexed { index, value -> handleLogicalOperator(value, "${classifier}${index + 1}", variables) }
218218
else -> values.map { value -> handleLogicalOperator(value, "", variables) }
219219
}
220+
220221
else -> emptyList()
221222
}
222223
}
@@ -267,9 +268,8 @@ open class ProjectionBase(
267268
projections.addAll(pro)
268269
subQueries += sub
269270
}
270-
if (nodeType is GraphQLInterfaceType
271-
&& !handledFields.contains(TYPE_NAME)
272-
&& (env.getContext() as? QueryContext)?.queryTypeOfInterfaces == true
271+
if ((nodeType is GraphQLInterfaceType
272+
&& !handledFields.contains(TYPE_NAME)) && env.queryContext().queryTypeOfInterfaces
273273
) {
274274
// for interfaces the typename is required to determine the correct implementation
275275
val (pro, sub) = projectField(propertyContainer, variable, TYPE_NAME_SELECTED_FIELD, nodeType, env, variableSuffix)
@@ -340,6 +340,7 @@ open class ProjectionBase(
340340
schemaConfig.useTemporalScalars && fieldDefinition.isNeo4jTemporalType() -> {
341341
projections += getNeo4jTypeConverter(fieldDefinition).projectField(variable, field, "")
342342
}
343+
343344
isObjectField -> {
344345
if (fieldDefinition.isNeo4jType()) {
345346
projections += projectNeo4jObjectType(variable, field, fieldDefinition)
@@ -349,9 +350,11 @@ open class ProjectionBase(
349350
subQueries += sub
350351
}
351352
}
353+
352354
fieldDefinition.isNativeId() -> {
353355
projections += id(anyNode(variable))
354356
}
357+
355358
else -> {
356359
val dynamicPrefix = fieldDefinition.dynamicPrefix()
357360
when {
@@ -368,6 +371,7 @@ open class ProjectionBase(
368371
)
369372
.asFunction()
370373
}
374+
371375
field.aliasOrName() != fieldDefinition.propertyName() -> {
372376
projections += variable.property(fieldDefinition.propertyName())
373377
}
@@ -473,6 +477,7 @@ open class ProjectionBase(
473477
?: "")), variableSuffix, field.selectionSet)
474478
propertyContainer.project(projectionEntries) to subQueries
475479
}
480+
476481
is Relationship -> projectNodeFromRichRelationship(parent, fieldDefinition, variable, field, env)
477482
else -> throw IllegalArgumentException("${propertyContainer.javaClass.name} cannot be handled for field ${fieldDefinition.name} of type ${parent.name}")
478483
}
@@ -535,6 +540,7 @@ open class ProjectionBase(
535540
val label = nodeType.getRelevantFieldDefinition(relInfo.endField)!!.type.innerName()
536541
node(label).named("$childVariable${relInfo.endField.capitalize()}") to relInfo.endField
537542
}
543+
538544
else -> node(nodeType.name).named(childVariableName) to null
539545
}
540546

core/src/test/kotlin/DataFetcherInterceptorDemo.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,11 @@ package demo
33
import graphql.GraphQL
44
import graphql.schema.*
55
import org.intellij.lang.annotations.Language
6+
import org.neo4j.cypherdsl.core.renderer.Dialect
67
import org.neo4j.driver.AuthTokens
78
import org.neo4j.driver.Driver
89
import org.neo4j.driver.GraphDatabase
9-
import org.neo4j.graphql.Cypher
10-
import org.neo4j.graphql.DataFetchingInterceptor
11-
import org.neo4j.graphql.SchemaBuilder
10+
import org.neo4j.graphql.*
1211
import java.math.BigDecimal
1312
import java.math.BigInteger
1413

@@ -18,6 +17,7 @@ fun initBoundSchema(schema: String): GraphQLSchema {
1817

1918
val dataFetchingInterceptor = object : DataFetchingInterceptor {
2019
override fun fetchData(env: DataFetchingEnvironment, delegate: DataFetcher<Cypher>): Any {
20+
env.graphQlContext.setQueryContext(QueryContext(neo4jDialect = Dialect.DEFAULT))
2121
val (cypher, params, type, variable) = delegate.get(env)
2222
return driver.session().use { session ->
2323
val result = session.run(cypher, params.mapValues { toBoltValue(it.value) })

core/src/test/kotlin/GraphQLServer.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ import org.neo4j.driver.GraphDatabase
1717
import org.neo4j.driver.Values
1818
import org.neo4j.graphql.*
1919
import java.net.InetSocketAddress
20-
import kotlin.streams.toList
2120

2221

2322
const val schema = """
@@ -83,10 +82,11 @@ fun main() {
8382
schema.execute(query).let { println(mapper.writeValueAsString(it));it }
8483
} else {
8584
try {
85+
val queryContext = QueryContext(optimizedQuery = setOf(QueryContext.OptimizationStrategy.FILTER_AS_MATCH))
8686
schema.execute(ExecutionInput
8787
.newExecutionInput()
8888
.query(query)
89-
.context(QueryContext(optimizedQuery = setOf(QueryContext.OptimizationStrategy.FILTER_AS_MATCH)))
89+
.graphQLContext(mapOf(QueryContext.KEY to queryContext))
9090
.variables(params(payload))
9191
.build())
9292
} catch (e: OptimizedQueryException) {

core/src/test/resources/filter-tests.adoc

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2431,7 +2431,7 @@ RETURN person {
24312431
[source,cypher]
24322432
----
24332433
MATCH (person:Person)
2434-
WHERE distance(person.location, point($filterPersonLocationDistance.point)) = $filterPersonLocationDistance.distance
2434+
WHERE point.distance(person.location, point($filterPersonLocationDistance.point)) = $filterPersonLocationDistance.distance
24352435
RETURN person {
24362436
.name
24372437
} AS person
@@ -2480,7 +2480,7 @@ RETURN person {
24802480
[source,cypher]
24812481
----
24822482
MATCH (person:Person)
2483-
WHERE distance(person.location, point($filterPersonLocationDistanceLt.point)) < $filterPersonLocationDistanceLt.distance
2483+
WHERE point.distance(person.location, point($filterPersonLocationDistanceLt.point)) < $filterPersonLocationDistanceLt.distance
24842484
RETURN person {
24852485
.name
24862486
} AS person
@@ -2529,7 +2529,7 @@ RETURN person {
25292529
[source,cypher]
25302530
----
25312531
MATCH (person:Person)
2532-
WHERE distance(person.location, point($filterPersonLocationDistanceLte.point)) <= $filterPersonLocationDistanceLte.distance
2532+
WHERE point.distance(person.location, point($filterPersonLocationDistanceLte.point)) <= $filterPersonLocationDistanceLte.distance
25332533
RETURN person {
25342534
.name
25352535
} AS person
@@ -2578,7 +2578,7 @@ RETURN person {
25782578
[source,cypher]
25792579
----
25802580
MATCH (person:Person)
2581-
WHERE distance(person.location, point($filterPersonLocationDistanceGt.point)) > $filterPersonLocationDistanceGt.distance
2581+
WHERE point.distance(person.location, point($filterPersonLocationDistanceGt.point)) > $filterPersonLocationDistanceGt.distance
25822582
RETURN person {
25832583
.name
25842584
} AS person
@@ -2627,7 +2627,7 @@ RETURN person {
26272627
[source,cypher]
26282628
----
26292629
MATCH (person:Person)
2630-
WHERE distance(person.location, point($filterPersonLocationDistanceGte.point)) >= $filterPersonLocationDistanceGte.distance
2630+
WHERE point.distance(person.location, point($filterPersonLocationDistanceGte.point)) >= $filterPersonLocationDistanceGte.distance
26312631
RETURN person {
26322632
.name
26332633
} AS person

0 commit comments

Comments
 (0)