Neo4j variable as property name - neo4j

I'm trying to use a variable as a property name in neo4j
MATCH(e: Event {id: "123"})
MATCH(u: User {id: "456"})
MERGE (u)-[a:ACTION]->(e)
ON MATCH
SET e[a.t] = e[a.t] - 1
This gives the error:
Invalid input '[': expected an identifier character, whitespace, '{', 'x/X', node labels, a property map, a relationship pattern, '.', '(', '=' or "+=" (line 5, column 6 (offset: 96))
"SET e[a.t] = e[a.t] - 1"
While according to this forum: it should be possible.
Neo4j forum
What am I doing wrong?

Whenever we want to create a relationship dynamically, and set its properties dynamically, we should use apoc.merge.relationship function. In this function you can dynamically provide startNode, endNode, properties to set onMatch and onCreate for the relationship and some other params as well.
For your use case, you can try something like this:
MATCH(e: Event {id: "123"})
MATCH(u: User {id: "456"})
OPTIONAL MATCH (u)-[rel:ACTION]->(e)
CALL apoc.merge.relationship(
u,
"ACTION",
{},
{},
e,
apoc.map.setKey({}, toString(rel.t), rel.t - 1)
)
YIELD rel
RETURN rel;
Also, note that we have to fetch the relationship using OPTIONAL MATCH so that we can access its properties when calling the apoc function. Finally, you will also need to install APOC library for the above query to work.

The example as you pointed out in the forum uses the node['property'] form in the WHERE clause. It does not work when updating the property like you did.
For example, below query works
match (n:Actor)
where n['name'] = "Neo"
set n.age = 50
return n
Result:
n
(0:Actor {age:50, name:"Neo"})
Query took 14 ms and returned 1 rows.
Updated the graph - set 1 property
BUT THIS WILL NOT WORK:
set n['age'] = 50

Related

Returning multiple columns

Hi All newbie here in Neo4J,
I am trying to return keys or properties using the following simple query in the neo4J broswer environment.
MATCH (n:movies),(m:people)
RETURN properties(n,m)
What I am trying to achieve is to return the properties for both the movies and people nodes
However, I would always get an error
Too many parameters for function 'properties' (line 2, column 9 (offset: 36))
" RETURN properties(n,m)"
I have tried,
MATCH (n:movies),(m:people)
RETURN properties(k) in [n,m]
The error I would get
Variable `k` not defined (line 2, column 20 (offset: 47))
" RETURN properties(k) in [n,m]"
I am trying to pass a list here into k but NEO4J is not permitting me to do so. Is it even possible to pass a list into the function properties() ??
Thank you in advance.
The properties function takes exactly one node or a relationship as input.
MATCH (n:movies),(m:people) RETURN properties(n), properties(m)
will create a Cartesian Product.
i.e. If you have five movies and ten people, you will get a result of all 50 combinations.
If you aren't looking for a cartesian product, you would have to define a specific pattern or restrict the MATCH clause further.
If you want just the individual properties without combining them, consider Union.
MATCH (n:Movie)
RETURN properties(n) as `Properties`
UNION ALL
MATCH (m:Person)
RETURN properties(m) as `Properties`
Why am I using aliases for a seemingly simple query? To avoid this:
All sub queries in an UNION must have the same column names (line 3,
column 1 (offset: 39))
For working with lists:
The collect function lets you create/construct a list from the results while UNWIND expands a list into a sequence of rows.
properties() only takes one argument, you can try
MATCH (n:movies),(m:people) RETURN properties(n) as prop_n, properties(m) as prop_m
or more optimal query would be
MATCH (n:movies) optional match (m:people) RETURN properties(n) as prop_n, properties(m) as prop_m
MATCH (n:movies),(m:people)
RETURN properties(k) in [n,m]
since you have not defined k so you are getting the error. Also according to doc properites() takes "An expression that returns a relationship, a node, or a map" as an argument. Your query is not supported.

cypher - relationship traversal vs ordering by property

graph snippet
I have a Shape with a list of Points.
I have the following requirements:
1) retrieve an ordered set of Points;
2) insert/remove a Point and preserve the order of the rest of the Points
I can achieve this by:
A) Point has a sequence integer property that could be used to order;
B) Add a :NEXT relationship between each Point to create a linked list.
I'm new to Neo4j so not sure which approach is preferable to satisfy the requirements?
For the first requirement, I wrote the following queries and found the performance for the traversal to be poor but Im sure its a badly constructed query:
//A) 146 ms
Match (s:Shape {id: "1-700-y11-1.1.I"})-[:POINTS]->(p:Point)
return p
order by p.sequence;
//B) Timeout! Bad query I know, but dont know the right way to go about it!
Match path=(s:Shape {id: "1-700-y11-1.1.I"})-[:POINTS]->(p1:Point)-[:NEXT*]->(p2:Point)
return collect(p1, p2);
To get ordered list of points use a slightly modified version of the second query:
Match path=(s:Shape {id: "1-700-y11-1.1.I"})-[:POINTS]->(P1:Point)
-[:NEXT*]-> (P2:Point)
WHERE NOT (:Point)-[:NEXT]->(P1) AND
NOT (P2)-[:NEXT]->(:Point)
RETURN TAIL( NODES( path) )
And for example query to delete:
WITH "id" as pointToDelete
MATCH (P:Point {id: pointToDelete})
OPTIONAL MATCH (Prev:Point)-[:NEXT]->(P)
OPTIONAL MATCH (P)-[:NEXT]->(Next:Point)
FOREACH (x in CASE WHEN Prev IS NOT NULL THEN [1] ELSE [] END |
MERGE (Prev)-[:NEXT]->(Next)
)
DETACH DELETE P

Neo4j load data relations with unknown labels

I have 4 Labels (A, B, C, D). All of them have a single Property {id}.
Now I have a file with relations which I would like to load. Every row has this structure:
{id_1}, {type_of_relations}, {id_2}
How can I create the relations?
My non-working guess is:
USING PERIODIC COMMIT
LOAD CSV WITH HEADERS FROM "file:/data.csv" AS line
FIELDTERMINATOR ','
MATCH (a:A{id:line.id_1} OR a:B{id:line.id_1} OR a:C{id:line.id_1} OR a:D{id:line.id_1})
MATCH (b:A{id:line.id_2} OR b:B{id:line.id_2} OR b:C{id:line.id_2} OR b:D{id:line.id_2})
MERGE (a)-[:line.type_of_relations]->(b)
You cannot parameterize the relationship type in Cypher.
However, you can do this using the apoc.create.relationship procedure in Neo4j apoc procedures:
USING PERIODIC COMMIT
LOAD CSV WITH HEADERS FROM "file:///file.csv" AS row
MATCH (a) WHERE a.id = row.id_1
MATCH (b) WHERE b.id = row.id_2
CALL apoc.create.relationship(a, row.type_of_relations, {}, b) YIELD rel
RETURN count(*) AS num
The procedure takes a parameter for the relationship type which allows for creating dynamic relationship types.
I don't think you can do that. For a number of reasons.
create (f:bar {name:'NewUserA'})
create (f:foo {name:'NewUserA'})
match (f:foo {name:'NewUserA'} or f:bar {name:'NewUserA'}) return f;
Code
Invalid input 'o': expected whitespace, comment, ')' or a relationship pattern (line 1, column 32 (offset: 31))
"match (f:foo {name:'NewUserA'} or f:bar {name:'NewUserA'}) return f".
So there is a problem on the match at any rate.
If the id is globally unique then you can ignore the label and just match on the id. that will take care of your 'or' problem.
match (f) where f.name='NewUserA' match (t) where t.name='NewUserA' return f,t
would give you the nodes.
That being said, when coding parameterized queries RELATIONSHIP_TYPE is one of the items you cannot parameterize. From the docs:
5.5. Parameters
[..]
Parameters can not be used as for property names, relationship types and labels, since these patterns are part of the query structure that is compiled into a query plan.
[..]
So you may need to look to ways of building your MERGE as a string somewhere else (awk is your friend) and then running that in the shell.

Neo4j indices slow when querying across 2 labels

I've got a graph where each node has label either A or B, and an index on the id property for each label:
CREATE INDEX ON :A(id);
CREATE INDEX ON :B(id);
In this graph, I want to find the node(s) with id "42", but I don't know a-priori the label. To do this I am executing the following query:
MATCH (n {id:"42"}) WHERE (n:A OR n:B) RETURN n;
But this query takes 6 seconds to complete. However, doing either of:
MATCH (n:A {id:"42"}) RETURN n;
MATCH (n:B {id:"42"}) RETURN n;
Takes only ~10ms.
Am I not formulating my query correctly? What is the right way to formulate it so that it takes advantage of the installed indices?
Here is one way to use both indices. result will be a collection of matching nodes.
OPTIONAL MATCH (a:B {id:"42"})
OPTIONAL MATCH (b:A {id:"42"})
RETURN
(CASE WHEN a IS NULL THEN [] ELSE [a] END) +
(CASE WHEN b IS NULL THEN [] ELSE [b] END)
AS result;
You should use PROFILE to verify that the execution plan for your neo4j environment uses the NodeIndexSeek operation for both OPTIONAL MATCH clauses. If not, you can use the USING INDEX clause to give a hint to Cypher.
You should use UNION to make sure that both indexes are used. In your question you almost had the answer.
MATCH (n:A {id:"42"}) RETURN n
UNION
MATCH (n:B {id:"42"}) RETURN n
;
This will work. To check your query use profile or explain before your query statement to check if the indexes are used .
Indexes are formed and and used via a node label and property, and to use them you need to form your query the same way. That means queries w/out a label will scan all nodes with the results you got.

How to use max() on a collection?

I have a dataset that looks something like this:
CREATE (n {name:'main', val:3}) -[:r]-> ({name:'sub1', val:2}), (n)-[:r]->({name:'sub2', val:1})
Now, I need to find the maximum value for 'val' for all nodes that are connected to the node named 'main' (including 'main' too). So, in this case the answer is
3.
Since the node named 'main' may not have any subnodes, I used OPTIONAL MATCH to find the subnodes, then combine all the vals found into a list and call max() on it, like so:
MATCH (n {name:'main'})
OPTIONAL MATCH (n)-[:r]->(subs)
RETURN max(n.val + collect(subs.val))
But this gives the following error:
Type mismatch: expected Float or Integer but was Collection (line
3, column 18 (offset: 73)) "RETURN max(n.val + collect(subs.val))"
What is the correct may to solve this sort of problem?
This should work for you:
MATCH p=(n {name:'main'})-[:r*0..]->(subs)
UNWIND NODES(p) AS node
RETURN MAX(node.val) AS result;
This query uses a variable-length pattern to follow the entire (optional) chain of r relationships. Your query did not. The NODES() function produces a collection of path nodes, and the UNWIND converts the collection to data rows. Aggregation functions like MAX() only aggregate across data rows.

Resources