Neo4j delete a node property with neo4j-jdbc - neo4j

I have an edge with sevral properties. I would like to keep the edge but remove only the name property.
My java Cypher is this :
public static final String DELETE_EDGE_PROPERTY_QUERY = //
"MATCH ()-[r]->() where id(r) = {1} REMOVE r.{2} RETURN r"; //
It works on cypher console but doesn't work on jdbc.
I got this error :
processing failed; nested exception is org.springframework.jdbc.UncategorizedSQLException: PreparedStatementCallback; uncategorized SQLException for SQL [MATCH ()-[r]->() where id(r) = {1} REMOVE r.{2} RETURN r]; SQL state [null]; error code [0]; Some errors occurred :
[Neo.ClientError.Statement.SyntaxError]:Invalid input '{': expected an identifier, whitespace, a function name or a property key name (line 1, column 45 (offset: 44))
"MATCH ()-[r]->() where id(r) = {1} REMOVE r.{2} RETURN r"
`
Any suggestions?
Thanks
EDIT
I couldn't do with jdbc template.
so I have used String replacement:
Setting the property to NULL will delete the property
(http://www.baeldung.com/java-neo4j)
Solution :
Java :
String deleteQuery = String.format(DELETE_EDGE_PROPERTY_QUERY, property);
plantJdbcTemplate.update(deleteQuery, edgeId);
cypher :
public static final String DELETE_EDGE_PROPERTY_QUERY = //
"MATCH ()-[r]->() where id(r) = {1} SET r.%s = NULL RETURN r";

This not a neo4j-jdbc issue, it's related to the design of Neo4j about parameterized query.
On a query you can parameterized all data you want, except:
labels
relationship types
properties
If you want to do it in Cypher, there are a lot of helpers in the APOC plugin.

Related

Neo4j cypher return Path with a specific format

I have a cypher query to return the shortest Path between two nodes.
I am using Java JdbcTemplate.
MATCH (nodeA),(nodeB), p = shortestPath((nodeA)-[*..15]-(nodeB)) " //
+ "WHERE ID(nodeA) = {1} and ID(nodeB) = {2} RETURN nodes(p) as nodes " //
+ ",relationships(p) as edges
My problem is that I have a Util method that do the mapping of the jdbctempate result. It only works when my cypher returns nodes suchs as :
RETURN { id : id(node), labels : labels(node), data: node } as node
Is there a way to make my initial cypher return the result in that format?
NODES returns a list of nodes, so you can use UNWIND to unpack the list into a set of rows. So in your example...
MATCH (nodeA),(nodeB), p = shortestPath((nodeA)-[*..15]-(nodeB))
WHERE ID(nodeA) = {1} AND ID(nodeB) = {2}
UNWIND nodes(p) as n
RETURN { id : id(n), labels : labels(n), data: n} as node, relationships(p) as edges
You can use WITH + COLLECT to fold the nodes back into a list, and then perform the same operation on relationships if you need.

Unknown function 'org.neo4j.examples.join' on neo4j

I am trying to creating user defined procedure.
i got the sample project from this github repository https://github.com/neo4j-examples/neo4j-procedure-template. i created the jar file and moved to /var/lib/neo4j/plugins
i am getting the unknown function error
If anyone knows about it help me please
also i restarted my neo4j also
neo4j> MATCH (p: Person) WHERE p.age = 36 RETURN org.neo4j.examples.join(collect(p.names));
Unknown function 'org.neo4j.examples.join' (line 1, column 44 (offset: 43))
"MATCH (p: Person) WHERE p.age = 36 RETURN org.neo4j.examples.join(collect(p.names));"
If you look at the comment on the java method for this function, you will see into the description annotation, that to call it you should do : example.join(...) and NOT org.neo4j.examples.join.
This function is under the java package example, and the name of the function is join, so it gives you example.join
You can define manually what is the way to call a function by adding a name attribut into the UserFunction annotation. Like this :
#UserFunction( "test.nodeList" )
public List<Object> nodeList()
{
Result result = db.execute( "MATCH (n) RETURN n LIMIT 1" );
Object node = result.next().get( "n" );
return Collections.singletonList( node );
}
And call it in cypher like this :
RETURN test.toSet(['a', 'b'])

Neo4j SDN4 repository method with custom query and composite entity

I have the following SDN 4 entity:
#NodeEntity
public class Decision {
#Relationship(type = CONTAINS, direction = Relationship.INCOMING)
private Set<Decision> parentDecisions;
...
}
I'd like to find this entity by id and return all its parentDecisions with a following SDN4 repository method and the custom Cypher query:
MATCH (d:Decision) WHERE d.id = {decisionId} OPTIONAL MATCH (d)<-[rdp:CONTAINS]-(parentD:Decision) RETURN d, rdp, parentD
Decision getById(#Param("decisionId") Long decisionId);
right now in case of existing parentD it fails with the following exception:
java.lang.RuntimeException: Result not of expected size. Expected 1 row but found 3
at org.neo4j.ogm.session.delegates.ExecuteQueriesDelegate.queryForObject(ExecuteQueriesDelegate.java:73)
at org.neo4j.ogm.session.Neo4jSession.queryForObject(Neo4jSession.java:382)
at sun.reflect.GeneratedMethodAccessor111.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
How to properly implement this logic with SDN4 repository method and the custom Cypher query ?
UPDATED
I tested approach suggested by frant.hartm. Unfortunately it still doesn't work.
For example the following SDN4 repository method correctly returns 2 Decision:
#Query("MATCH (d:Decision)-[drd:FOLLOWS]->(following:Decision) WHERE d.id = {decisionId} RETURN following")
List<Decision> test(#Param("decisionId") Long decisionId);
but the next one:
#Query("MATCH (d:Decision) WHERE d.id = {decisionId} OPTIONAL MATCH (d)-[rdf:FOLLOWS]->(followD:Decision) RETURN d as decision, collect(rdf), collect(followD) ")
DecisionHolder test1(#Param("decisionId") Long decisionId);
returns only main decision (holder.getDecision()) but with empty collection of followD (holder.getDecision().getFollowDecisions()) Decision.
This is Decision entity:
#NodeEntity
public class Decision {
private static final String FOLLOWS = "FOLLOWS";
#Relationship(type = FOLLOWS, direction = Relationship.INCOMING)
private Set<Decision> followDecisions;
#Relationship(type = FOLLOWS, direction = Relationship.INCOMING)
public Set<Decision> getFollowDecisions() {
return followDecisions;
}
#Relationship(type = FOLLOWS, direction = Relationship.INCOMING)
public void setFollowDecisions(Set<Decision> followDecisions) {
this.followDecisions = followDecisions;
}
....
}
What am I doing wrong ?
The core issue is that your query returns multiple Decision instances (under different variables - d and parentD), so it can't just arbitrarily chose one. Use #QueryResult to extract desired value. Also use COLLECT to get a single result.
#QueryResult
public class DecisionHolder {
Decision d;
}
MATCH (d:Decision) WHERE d.id = {decisionId} " +
"OPTIONAL MATCH (d)<-[rdp:CONTAINS]-(parentD:Decision) " +
"RETURN d, collect(rdp), collect(parentD)")
DecisionHolder getById(#Param("decisionId") Long decisionId);
Update:
For the relationships to map correctly relationship entities and simple relationships with same type must not be mixed.
If you have a simple relationship and relationship entity with same type
when they represent same relationship type use relationship entity class in your relationship definition (e.g. Set<ParentDecisionRelationship> parentDecisions)
when they represent different types rename one of them

Neo4j: Conditional return/IF clause/String manipulation

This is in continuation of Neo4j: Listing node labels
I am constructing a dynamic MATCH statement to return the hierarchy structure & use the output as a Neo4j JDBC input to query the data from a java method:
MATCH p=(:Service)<-[*]-(:Anomaly)
WITH head(nodes(p)) AS Service, p, count(p) AS cnt
RETURN DISTINCT Service.company_id, Service.company_site_id,
"MATCH srvhier=(" +
reduce(labels = "", n IN nodes(p) | labels + labels(n)[0] +
"<-[:BELONGS_TO]-") + ") WHERE Service.company_id = {1} AND
Service.company_site_id = {2} AND Anomaly.name={3} RETURN " +
reduce(labels = "", n IN nodes(p) | labels + labels(n)[0] + ".name,");
The output is as follows:
MATCH srvhier=(Service<-[:BELONGS_TO]-Category<-[:BELONGS_TO]-SubService<-
[:BELONGS_TO]-Assets<-[:BELONGS_TO]-Anomaly<-[:BELONGS_TO]-) WHERE
Service.company_id = {1} and Service.company_site_id = {21} and
Anomaly.name={3} RETURN Service.name, Category.name, SubService.name,
Assets.name, Anomaly.name,
The problem I am seeing:
The "BELONGS_TO" gets appended to my last node
Line 2: Assets<-[:BELONGS_TO]-Anomaly**<-[:BELONGS_TO]-**
Are there string functions (I have looked at Substring..) that can be used to remove it? Or can I use a CASE statement with condition n=cnt to append "BELONGS_TO"?
The same problem persists with my last line:
Line 5: Assets.name,Anomaly.name**,** - the additional "," that I need to eliminate.
Thanks.
I think you need to introduce a case statement into the reduce clause something like this snippet below. If the node isn't the last element of the collection then append the "<-[:BELONGS_TO]-" relationship. If it is the last element then don't append it.
...
reduce(labels = "", n IN nodes(p) |
CASE
WHEN n <> nodes(p)[length(nodes(p))-1] THEN
labels + labels(n)[0] + "<-[:BELONGS_TO]-"
ELSE
labels + labels(n)[0]
END
...
Cypher has a substring function that works basically like you'd expect. An example: here's how you'd return everything but the last three characters of a string:
return substring("hello", 0, length("hello")-3);
(That returns "he")
So you could use substring to trim the last separator off of your query that you don't want.
But I don't understand why you're building your query in such a complex way; you're using cypher to write cypher (which is OK) but (and I don't understand your data model 100%) it seems to me like there's probably an easier way to write this query.

How do I extract results individually from an ExecutionResult?

I have the following java code snippet that demonstrates the problem. The error I receive is also included below.
It correctly pulls the correct set, but I am having trouble printing.
I'm using the org.neo4j.graphdb.Node node. Is this the wrong class?
If not, how do I obtain the results movieid, avgrating and movie_title from the ExecutionEngine?
Java Code
GraphDatabaseService db = new GraphDatabaseFactory().newEmbeddedDatabase(DB_PATH);
ExecutionEngine engine = new ExecutionEngine(db);
String cypherQuery = "MATCH (n)-[r:RATES]->(m) \n"
+ "RETURN m.movieid as movieid, avg(toFloat(r.rating)) as avgrating, m.title as movie_title \n"
+ "ORDER BY avgrating DESC \n"
+ "LIMIT 20;";
ExecutionResult result = engine.execute(cypher);
for (Map<String, Object> row : result) {
Node x = (Node) row.get("movie_title");
for (String prop : x.getPropertyKeys()) {
System.out.println(prop + ": " + x.getProperty(prop));
}
}
Error
Exception in thread "main" java.lang.ClassCastException: java.lang.String cannot be cast to org.neo4j.graphdb.Node
at beans.RecommendationBean.queryMoviesWithCypher(RecommendationBean.java:194)
at beans.RecommendationBean.main(RecommendationBean.java:56)
Node x = (Node) row.get("movie_title");
...looks to be the culprit.
In your Cypher statement, you return m.title as movie_title, i.e. you're returning a node property (in this case, a string), and, in the offending line, you're trying to cast that string result as a Node.
If you want Cypher to return a series of nodes you can iterate through, try returning m (the whole node) instead of just individual properties and aggregates, e.g.
"...RETURN m AS movie;"
...
Node x = (Node) row.get("movie");
Etc.

Resources