13
votes

I have problems with parameter in cypher in Neo4J from Java. I run the the database embedded.

The code should be like this (GraphDB.cypher goes directly to the ExecutionEngine)

HashMap<String, Object> parameter = new HashMap<>();
parameter.put("theLabel1", "Group");
parameter.put("theRelation", "isMemberOf");
parameter.put("theLabel2", "Person");
GraphDB.cypher("MATCH (n1:{theLabel1})-[r:{theRelation}]->(n2:{theLabel2}) RETURN n1, r, n2", parameter);

but it ends in this exception

Exception in thread "main" Invalid input '{': expected whitespace or a label name (line 1, column 11)
"MATCH (n1:{theLabel1})-[r:{theRelation}]->(n2:{theLabel2}) RETURN n1, r, n2"

The documentation (and tutorial) tells to use the { } to cover the parameters, BUT this is also used as the cypher json notation for properties. @See http://docs.neo4j.org/chunked/milestone/tutorials-cypher-parameters-java.html

Is there another way to solve this issue rather than building the query string like this (or with other template methods)

GraphDB.cypher("MATCH (n:" + labelName + ")-[r:" + relationName + "]->...

This is needed because the target label can change and I want to reuse the code completly.

Thanks in advance.

[[EDITED AFTER GETTING A (sigh) NO AS ANSWER]]

Since this form of parameter is currently (2014.6) not supported, I will run a little replacer right before sending the query.

HashMap<String, Object> parameter = new HashMap<>();
parameter.put("theLabel1", "Group");
parameter.put("theRelation", "isMemberOf");
parameter.put("theLabel2", "Person");

parameter.put("aName", "Donald Duck");

GraphDB.cypher("MATCH (n1:#theLabel1#)-[r:#theRelation#]->(n2:#theLabel2#) WHERE n2.Name = {aName} RETURN n1, r, n2", parameter);

... with ...

public static ExecutionResult cypher(String query, Map<String, Object> params) {
    for (String key : params.keySet()) {
        query = query.replaceAll("#" + key + "#", String.valueOf(params.get(key)));
    }
    return params == null ? cypherEngine.execute(query) : cypherEngine.execute(query, params);
}

There can be a more readble

3
Well, yes, you can statically define query templates, but this is kinda out of Neo4j scope to provide this kind of features, don't you think?fbiville
@Raxa, your solution with "replacer" is very interesting. At first glance, your use of "#theLabel1#" seems static, thus did not address the true issue of specifying Cypher label with parameter "dynamically". But when considered as simply a place holder to be replaced by the match in parameter, it does achieve passing label dynamically. Not sure if there is any standard solution coming, but it is quite an interesting mitigation.Causality

3 Answers

11
votes

I am afraid this is not supported at the moment.

And it might for the very same reason than the one explained in this issue: https://github.com/neo4j/neo4j/pull/1542.

The idea behind parametrized queries is to re-use (cache) execution plans. If a node label or a relationship type varies, the execution plan wouldn't be the same at all, thus ruining the usefulness of execution plan caching.

10
votes

Just figured out a way to accomplish this as I was running into the same thing:

MATCH (n) WHERE {label} IN labels(n)

The labels() function returns all labels on a node, and the IN operator tests for existence in the list. Apparently Cypher allows this construct because it's leveraging a parameter in a variable field in the predicate. According to the Cypher docs, comparing labels or properties in WHERE clauses and direct property/label embeds in the node definition are optimized the same way, so shouldn't be a significant performance hit.

Not sure about an easy way to support multiple possible labels...

1
votes

Don't think of labels as the most important property for a document of information, instead, think of them as the schema for what property keys to populate in the "map". (the awful name for neo4j to reference the properties object)

For NER, entities that share the same merge query, by definition share the same parameters being set at run-time; therefore, they should share a label, which makes a label hardcoded, not parameterized.

You can still query by properties. Values that would not affect the types of relationships between nodes, or the property keys themselves, should not be labels.

You can also add labels to existing nodes, ideally as new properties are added to an existing node, an expansion of the node's definition. If you are creating relationships between nodes, that would be an ideal time to add labels based on the analysis of node properties, as long as the relationship type is somewhat unique to the labels of each node.