15

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

Raxa
  • 372
  • 3
  • 7
  • 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 Jun 18 '14 at 08:35
  • @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 Feb 23 '16 at 22:02

3 Answers3

11

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.

fbiville
  • 8,407
  • 7
  • 51
  • 79
  • 2
    It's a shame the Neo4J documentation sells us the idea in 7.5 that "This means developers don’t have to resort to string building to create a query." Clearly this isn't the real motivation at all. It would be more sensible if the Cypher engine worked this stuff out and did replacements like the above on our behalf. Developers could then fully parameterise queries and as Neo4J improves over time, it could exploit this parameterisation where it can. – Herc Jan 19 '15 at 20:25
  • 2
    Is this still not supported today on Neo4J 4? I'm trying a similar query and getting the same error. – watery Jun 06 '20 at 12:18
10

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...

arpieb
  • 340
  • 3
  • 7
  • 1
    Great and simple answer. Genius! Just to complement this answer, if you want to also do the same for relationships you need to use the TYPE built-in function, eg: `MATCH (a)-[r]->(b) WHERE {relName}=TYPE(r) RETURN ID(r) AS relId, TYPE(r) AS relName` – artemisian Jan 06 '17 at 00:09
  • You can support multiple labels using the `any()` function. For example `MATCH (o) WHERE ANY (item IN labels(o) WHERE item IN $list_of_labels)` – John Sep 08 '17 at 18:53
  • 6
    but there is no optimization, it will be a full database scan with checks per node, while a lookup by label is a label-scan-store operation. you can construct your cypher query dynamically, and then .e.g. use apoc.cypher.run to execute it – Michael Hunger Sep 09 '17 at 20:49
  • Worked for me, though I had to get rid of the curly brackets querying Neo4J 4 from javascript, e.g. `MATCH (pOg) WHERE pOg.name=$pOgName AND $pOgLabel IN LABELS(pOg)` – watery Jun 06 '20 at 12:28
1

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.

Harry Scheuerle
  • 377
  • 2
  • 15