0

env:

  1. Neo4j-4.4.x
  2. neo4j-jdbc-driver 4.0.9

Image that I execute the following cypher to get a ‘teacher’ node and a ‘student’ node, the relationship between them is ‘educate’, and the relationship has a year attribute.

cypher:

merge (subject:Teacher{teacher_id: 'teacher_001'})  
merge (object:Student{student_id:'student_001'})  
merge (subject)-[relation:educate]->(object) set relation.year='3'  return subject,relation,object

figure-1:
enter image description here

Why does my following approach with the help of parameters maps not work?

@Test
public void testDeleteEdgeWithProperties_001() {
    Driver driver = GraphDatabase.driver("neo4j://localhost:7687", AuthTokens.basic("neo4j", "neo4j123"));
    Session session = driver.session();
    Transaction transaction = session.beginTransaction();

    String relationModelType = "educate";
    String subjectModelType = "Teacher";
    String subjectKey = "teacher_id";
    String subjectId = "teacher_001";
    String objectModelType = "Student";
    String objectKey ="student_id";
    String objectId = "student_001";

    Map<String, Object> properties = new HashMap<>();
    properties.put("year","3");
    Map<String, Object> params = new HashMap<>();
    params.put("relation_prop", properties == null ? new HashMap<>() : properties);

    // 删除
    String conditionDeleteCypher = " WHERE relation = $relation_prop";
    String cypher =  " MATCH (subject:" + subjectModelType + "{" + subjectKey + ": '" + subjectId + "'}) " +
            " MATCH (object:" + objectModelType + "{" + objectKey + ":'" + objectId + "'}) " +
            " MATCH (subject)-[relation:" + relationModelType +"]->(object) " +
            conditionDeleteCypher +
            " DELETE relation";

    System.out.println(cypher);
    transaction.run(cypher, params);
    transaction.commit();
    transaction.close();
}

The cypher printed by the above method is as follows:

MATCH (subject:Teacher{teacher_id: 'teacher_001'})  MATCH (object:Student{student_id:'student_001'})  MATCH (subject)-[relation:educate]->(object)  WHERE relation = $relation_prop DELETE relation

However, the above cypher can be executed successfully in the neo4j browser (see figure-2 below). What's wrong with me? Appreciate any helpful responses

figure-2:
enter image description here

I tried several ways to set the parameter, but none of them worked.

String conditionDeleteCypher = " WHERE relation = $relation_prop";
String conditionDeleteCypher = " WHERE relation =+ $relation_prop";
String conditionDeleteCypher = " WHERE relation += $relation_prop";

The @Test code I provided is completely executable, no error message. After execution, the neo4j browser can still find the target relationship, indicating that the method was executed but did not take effect.

enter image description here

Mark Rotteveel
  • 100,966
  • 191
  • 140
  • 197
  • any error message ? Try to run an updated version of `https://github.com/neo4j/neo4j-documentation/blob/dev/cypher/cypher-docs/src/main/java/org/neo4j/cypher/example/JavaQuery.java` with your own code. – BendaThierry.com May 28 '23 at 09:06
  • 1. same as SQL applies to Cypher: https://neo4j.com/developer/kb/protecting-against-cypher-injection/ 2... you can do this, but you have to `WHERE relation.blah = $paramBlah AND relation.foo = $pararmFoo AND relation.bar = $paramBar`..then you "bind" a `Map.of("paramBlah", x, "paramFoo", y, "paramBar", z)` ... to your query – xerx593 May 28 '23 at 13:06
  • but actually with [bind parameters](https://neo4j.com/docs/cypher-manual/current/syntax/parameters/), we cannot (dynamically) set: 1. attribute names 2. Node types 3. Relation types ..if we want these dynamic, we have to pre-process the query (with regard of (malicious)"injection") – xerx593 May 28 '23 at 13:15
  • but maybe the actual (tiny) problem is, that you think `relation.year = "3"` but indeed it is `relation.year = 3` (string vs number) – xerx593 May 28 '23 at 13:32
  • 1
    So actually: `properties.put("year", 3);` and not `"3"` !?;) – xerx593 May 28 '23 at 13:37
  • Yes @xerx593, there I think it is too his tiny problem ;) – BendaThierry.com May 28 '23 at 18:08
  • so `3` fixed it !? :) – xerx593 May 28 '23 at 18:18
  • @xerx593 I used "3" when I wrote. But I'll still try 3, thanks a lot – Arthur_Morgan May 29 '23 at 02:04

2 Answers2

0

You can check the official Neo4J java documentation with many examples. Especially these one :

Map<String,Object> params = new HashMap<>();
params.put( "name", "Johan" );

String query =
"MATCH (n:Person)" + "\n" +
"WHERE n.name = $name" + "\n" +
"RETURN n";

Result result = transaction.execute( query, params );

And make your minimum use case lighter removing any useless code.

BendaThierry.com
  • 2,080
  • 1
  • 15
  • 17
  • 1. I have seen the document you mentioned, the difference is that the example given in the document directly uses the specific variable name, and my writing method is to use the entire parameter map 2. The @Test code I provided is completely executable, no error, but it cannot achieve my purpose – Arthur_Morgan May 28 '23 at 09:33
0

The query generated by your code, is logically incorrect:

MATCH (subject:Teacher{teacher_id: 'teacher_001'})  
MATCH (object:Student{student_id:'student_001'})  
MATCH (subject)-[relation:educate]->(object)  WHERE relation = $relation_prop 
DELETE relation

In this query, you are comparing the whole Neo4j Relationship object, to a query object, which is like this:

{
  relation_prop: {
     year: "3"
  }
}

This won't work because of two reasons:

  1. relation_prop is not a valid relation property.
  2. There are a lot of other internal properties, part of a relationship.

In the query you ran on Neo4j Browser, you are only checking the value of the year property. To fix it you can try this:

@Test
public void testDeleteEdgeWithProperties_001() {
    Driver driver = GraphDatabase.driver("neo4j://localhost:7687", AuthTokens.basic("neo4j", "neo4j123"));
    Session session = driver.session();
    Transaction transaction = session.beginTransaction();

    String relationModelType = "educate";
    String subjectModelType = "Teacher";
    String subjectKey = "teacher_id";
    String subjectId = "teacher_001";
    String objectModelType = "Student";
    String objectKey ="student_id";
    String objectId = "student_001";

    Map<String, Object> properties = new HashMap<>();
    properties.put("year","3");

    // 删除
    String conditionDeleteCypher = " WHERE relation.year = $year";
    String cypher =  " MATCH (subject:" + subjectModelType + "{" + subjectKey + ": '" + subjectId + "'}) " +
            " MATCH (object:" + objectModelType + "{" + objectKey + ":'" + objectId + "'}) " +
            " MATCH (subject)-[relation:" + relationModelType +"]->(object) " +
            conditionDeleteCypher +
            " DELETE relation";

    System.out.println(cypher);
    transaction.run(cypher, properties);
    transaction.commit();
    transaction.close();
}
Charchit Kapoor
  • 8,934
  • 2
  • 8
  • 24
  • Your answer is a workaround ,very appreciate it . – Arthur_Morgan May 29 '23 at 10:54
  • 1
    It's not a workaround, it's the correct solution. If you want to select relationships by properties you have to spell them out in dot notation or use `properties(rel) = $propertiesMap` (which is not as efficient) – Michael Hunger May 29 '23 at 12:05