Skip to content

Added alias or field name as variable to Cypher.kt #172

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Mar 25, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 2 additions & 7 deletions core/src/main/kotlin/org/neo4j/graphql/Cypher.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,7 @@ package org.neo4j.graphql

import graphql.schema.GraphQLType

data class Cypher @JvmOverloads constructor(val query: String, val params: Map<String, Any?> = emptyMap(), var type: GraphQLType? = null) {
data class Cypher @JvmOverloads constructor(val query: String, val params: Map<String, Any?> = emptyMap(), var type: GraphQLType? = null, val variable: String) {
fun with(p: Map<String, Any?>) = this.copy(params = this.params + p)
fun escapedQuery() = query.replace("\"", "\\\"").replace("'", "\\'")

companion object {
@JvmStatic
val EMPTY = Cypher("")
}
}
}
3 changes: 1 addition & 2 deletions core/src/main/kotlin/org/neo4j/graphql/GraphQLExtensions.kt
Original file line number Diff line number Diff line change
Expand Up @@ -206,8 +206,7 @@ fun <T> GraphQLDirective.getArgument(argumentName: String, defaultValue: T?): T?
?: throw IllegalStateException("No default value for @${this.name}::$argumentName")
}

fun GraphQLFieldDefinition.cypherDirective(): Cypher? = getDirectiveArgument<String>(CYPHER, CYPHER_STATEMENT, null)
?.let { statement -> Cypher(statement) }
fun GraphQLFieldDefinition.cypherDirective(): String? = getDirectiveArgument<String>(CYPHER, CYPHER_STATEMENT, null)

fun Any.toJavaValue() = when (this) {
is Value<*> -> this.toJavaValue()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ abstract class BaseDataFetcher(val fieldDefinition: GraphQLFieldDefinition) : Pr
.withPrettyPrint(true)
.build()
).render(statement)
return Cypher(query, statement.parameters, fieldDefinition.type)
return Cypher(query, statement.parameters, fieldDefinition.type, variable = variable)
}

protected abstract fun generateCypher(variable: String, field: Field, env: DataFetchingEnvironment): Statement
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import org.neo4j.graphql.*
class CypherDirectiveHandler(
private val type: GraphQLFieldsContainer?,
private val isQuery: Boolean,
private val cypherDirective: Cypher,
private val cypherDirective: String,
fieldDefinition: GraphQLFieldDefinition)
: BaseDataFetcher(fieldDefinition) {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import org.neo4j.cypherdsl.core.Node
import org.neo4j.cypherdsl.core.StatementBuilder.TerminalExposesLimit
import org.neo4j.cypherdsl.core.StatementBuilder.TerminalExposesSkip
import org.neo4j.graphql.*
import org.neo4j.graphql.Cypher
import org.neo4j.graphql.parser.ParsedQuery
import org.neo4j.graphql.parser.QueryParser.parseArguments
import org.neo4j.graphql.parser.QueryParser.parseFilter
Expand Down Expand Up @@ -272,13 +271,13 @@ open class ProjectionBase {
return mapOf(*projections.toTypedArray())
}

fun cypherDirective(variable: String, fieldDefinition: GraphQLFieldDefinition, field: Field, cypherDirective: Cypher, thisValue: Any? = null): Expression {
fun cypherDirective(variable: String, fieldDefinition: GraphQLFieldDefinition, field: Field, cypherDirective: String, thisValue: Any? = null): Expression {
val suffix = if (fieldDefinition.type.isList()) "Many" else "Single"
val args = cypherDirectiveQuery(variable, fieldDefinition, field, cypherDirective, thisValue)
return call("apoc.cypher.runFirstColumn$suffix").withArgs(*args).asFunction()
}

fun cypherDirectiveQuery(variable: String, fieldDefinition: GraphQLFieldDefinition, field: Field, cypherDirective: Cypher, thisValue: Any? = null): Array<Expression> {
fun cypherDirectiveQuery(variable: String, fieldDefinition: GraphQLFieldDefinition, field: Field, cypherDirective: String, thisValue: Any? = null): Array<Expression> {
val args = mutableMapOf<String, Any?>()
if (thisValue != null) args["this"] = thisValue
field.arguments.forEach { args[it.name] = it.value }
Expand All @@ -287,7 +286,7 @@ open class ProjectionBase {
.forEach { args[it.name] = it.defaultValue }

val argParams = args.map { (name, _) -> "$$name AS $name" }.joinNonEmpty(", ")
val query = (if (argParams.isEmpty()) "" else "WITH $argParams ") + cypherDirective.query
val query = (if (argParams.isEmpty()) "" else "WITH $argParams ") + cypherDirective
val argExpressions = args.flatMap { (name, value) -> listOf(name, if (name == "this") value else queryParameter(value, variable, name)) }
return arrayOf(literalOf<String>(query), mapOf(*argExpressions.toTypedArray()))
}
Expand Down
5 changes: 2 additions & 3 deletions core/src/test/kotlin/DataFetcherInterceptorDemo.kt
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,11 @@ fun initBoundSchema(schema: String): GraphQLSchema {
val cypher = delegate.get(env)
return driver.session().use { session ->
val result = session.run(cypher.query, cypher.params.mapValues { toBoltValue(it.value, env.variables) })
val key = result.keys().stream().findFirst().orElse(null)
if (isListType(cypher.type)) {
result.list().map { record -> record.get(key).asObject() }
result.list().map { record -> record.get(cypher.variable).asObject() }

} else {
result.list().map { record -> record.get(key).asObject() }
result.list().map { record -> record.get(cypher.variable).asObject() }
.firstOrNull() ?: emptyMap<String, Any>()
}
}
Expand Down
9 changes: 6 additions & 3 deletions core/src/test/kotlin/GraphQLServer.kt
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import org.neo4j.graphql.*
import spark.Request
import spark.Response
import spark.Spark
import java.util.*

const val schema = """
type Person {
Expand Down Expand Up @@ -63,11 +64,13 @@ fun main() {
try {
// todo fix parameter mapping in translator
val result = it.run(cypher.query, Values.value(cypher.params))
if (cypher.type?.isList() == true) {
result.keys().map { key -> key to result.list().map { row -> row.get(key).asObject() } }.toMap(LinkedHashMap())
val value = if (cypher.type?.isList() == true) {
result.list().map { row -> row.get(cypher.variable).asObject() }
} else {
result.keys().map { key -> key to result.list().map { row -> row.get(key).asObject() }.firstOrNull() }.toMap(LinkedHashMap())
result.list().map { record -> record.get(cypher.variable).asObject() }
.firstOrNull() ?: emptyMap<String, Any>()
}
Collections.singletonMap(cypher.variable, value)
} catch (e: Exception) {
e.printStackTrace()
}
Expand Down
18 changes: 7 additions & 11 deletions core/src/test/kotlin/org/neo4j/graphql/utils/CypherTestSuite.kt
Original file line number Diff line number Diff line change
Expand Up @@ -154,19 +154,15 @@ class CypherTestSuite(fileName: String) : AsciiDocTestSuite(
.forEach { server.graph().execute(it) }
}

val (cypher, params, type) = result()
val (cypher, params, type, variable) = result()
val dbResult = server.graph().execute(cypher, params)

val values = dbResult.columns().map { key ->
key to dbResult.stream()
.map { it[key] }
.let {
when {
type?.isList() == true -> it.toList()
else -> it.findFirst().orElse(null)
}
}
}.toMap(LinkedHashMap())
val values = mutableMapOf(variable to dbResult.stream().map { it[variable] }.let {
when {
type?.isList() == true -> it.toList()
else -> it.findFirst().orElse(null)
}
})

if (response.code.isEmpty()) {
val actualCode = MAPPER.writerWithDefaultPrettyPrinter().writeValueAsString(values)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,12 @@ open class Neo4jConfiguration {
return driver.session().writeTransaction { tx ->
val boltParams = cypher.params.mapValues { toBoltValue(it.value, env.variables) }
val result = tx.run(cypher.query, boltParams)
val key = result.keys().stream().findFirst().orElse(null)
if (isListType(cypher.type)) {
result.list()
.map { record -> record.get(key).asObject() }
.map { record -> record.get(cypher.variable).asObject() }
} else {
result.list()
.map { record -> record.get(key).asObject() }
.map { record -> record.get(cypher.variable).asObject() }
.firstOrNull() ?: emptyMap<String, Any>()
}
}
Expand Down