I'm attempting to use Memgraph in my application by connecting to it using the Neo4j Java driver. I have a constraint that nodes of a certain label must have a certain property. When I try to create a node without that property, the create fails (as verified by a separate read query); however, the query and transaction in which I attempt to create the node give no indication of failure. Running the same create query in Memgraph Lab gives a clear error message. Is there a way for me to get a similar message using the driver, so that I can know if the create failed without needing to do a separate read?
Here is a straightforward Kotlin main function that demonstrates the problem. I know Kotlin isn't the most common of languages, but hopefully it's legible enough.
import org.neo4j.driver.AuthTokens
import org.neo4j.driver.GraphDatabase
import org.neo4j.driver.Query
import org.neo4j.driver.Record
import org.neo4j.driver.Session
import org.neo4j.driver.summary.ResultSummary
fun main() {
// setup: delete all nodes (so that I can run this repeatedly) and create a constraint on n:Foo
val driver = GraphDatabase.driver("bolt://localhost:7687", AuthTokens.basic("myUsername", "myPassword")) // replace with the correct auth for your Memgraph database.
driver.session().run("MATCH (n) \n DETACH DELETE n;")
driver.session().run("CREATE CONSTRAINT ON (n:Foo) ASSERT EXISTS (n.bar);").consume()
// attempt to create n:Foo with bar so that I know what a success looks like
lateinit var successfulRecord: Record
lateinit var successfulSummary: ResultSummary
driver.session().use { session: Session ->
session.beginTransaction().run {
val query = Query("CREATE (node:Foo {bar: 3}) \n RETURN node")
val result = run(query)
successfulRecord = result.single()
successfulSummary = result.consume()
commit()
close()
}
}
// attempt to create n:Foo without bar. I want this to throw an error or give me a result indicating failure
lateinit var failureRecord: Record
lateinit var failureSummary: ResultSummary
driver.session().use { session: Session ->
session.beginTransaction().run {
val query = Query("CREATE (node:Foo) \n RETURN node")
val result = run(query)
failureRecord = result.single()
failureSummary = result.consume()
commit() //commit() doesn't return anything
close()
}
// I know of no way to get info from the session on whether the CREATE succeeded or not
}
// This is where I would like to see a piece of information that is meaningfully different between the two
println(successfulRecord)
println(failureRecord)
println()
println(successfulSummary)
println(failureSummary)
// Read from the db to make sure that the successful one was successful and the bad one failed.
driver.session().use { session: Session ->
session.beginTransaction().run {
val query = Query("MATCH (node:Foo) \n RETURN node")
val result = run(query)
val nodeList = result.list()
// These assertions correctly pass; the node without bar was not created and the node with bar was created
assert(nodeList.size == 1)
assert(nodeList[0].get(0).asMap()["bar"] == 3L)
}
}
}
It prints the following:
Record<{node: node<59>}>
Record<{node: node<60>}>
InternalResultSummary{query=Query{text='CREATE (node:Foo {bar: 3})
RETURN node', parameters={}}, serverInfo=InternalServerInfo{address='localhost:7687', version='Neo4j/4.3.0'}, databaseInfo=InternalDatabaseInfo{name='null'}, queryType=WRITE_ONLY, counters=InternalSummaryCounters{nodesCreated=1, nodesDeleted=0, relationshipsCreated=0, relationshipsDeleted=0, propertiesSet=0, labelsAdded=1, labelsRemoved=0, indexesAdded=0, indexesRemoved=0, constraintsAdded=0, constraintsRemoved=0, systemUpdates=0}, plan=null, profile=null, notifications=[], resultAvailableAfter=-1, resultConsumedAfter=-1}
InternalResultSummary{query=Query{text='CREATE (node:Foo)
RETURN node', parameters={}}, serverInfo=InternalServerInfo{address='localhost:7687', version='Neo4j/4.3.0'}, databaseInfo=InternalDatabaseInfo{name='null'}, queryType=WRITE_ONLY, counters=InternalSummaryCounters{nodesCreated=1, nodesDeleted=0, relationshipsCreated=0, relationshipsDeleted=0, propertiesSet=0, labelsAdded=1, labelsRemoved=0, indexesAdded=0, indexesRemoved=0, constraintsAdded=0, constraintsRemoved=0, systemUpdates=0}, plan=null, profile=null, notifications=[], resultAvailableAfter=-1, resultConsumedAfter=-1}
Process finished with exit code 0
which does not show any difference between the successful creation and the failed creation.
driver.session().run("CREATE (node:Foo) RETURN node")
. This did throw an error (org.neo4j.driver.exceptions.ClientException: Unable to commit due to existence constraint violation on :Foo(bar)
). It's nice that it successfully gave clear feedback that way; However, running all my code as auto-commit isn't feasible for me because of the role that Memgraph will play in my application. Is there a way to get similar behavior with a managed or unmanaged transaction that does not have auto-commit?Versions:
This was indeed a bug with Memgraph, not with my code. The Memgraph team has worked on it, and I have confirmed that this bug is fixed in version 2.13.0 of Memgraph.