FOREACH with collection in cypher - foreach

I have a collection of rels,created using this
MATCH (u:user)-[i:INTEREST]->(t:term)
WITH COLLECT([i,t]) AS its
RETURN its
and it returns the array of rels and nodes correctly.
see also http://console.neo4j.org/r/cw7saq
Now I want to set the properties of the relationship, but don't see how I can access the rels in the array. Tried this,
MATCH (u:user)-[i:INTEREST]->(t:term)
WITH COLLECT([i,t]) AS its
FOREACH (it IN its |
SET it[0].testprop=89292" )
but it returns an error
Error: Invalid input '[': expected an identifier character, node labels, a property map, a relationship pattern, '(', '.' or '=' (line 4, column 16)
" SET it[0].testprop=89292" )"
anyone knows what is the right syntax to do this ?

Anyone encountering a subset error like mentioned by OP can resolve it with parentheses:
MATCH (u:user)-[i:INTEREST]->(t:term)
WITH COLLECT([i,t]) AS its
FOREACH (it IN its |
SET (it[0]).testprop=89292" )

There's no need to collect the term nodes as well. Just do it as follows:
MATCH path=(u:user)-[i:INTEREST]->(t:term)
FOREACH (n IN rels(path) | set n.testprop=89292)

Related

Creating new relationships from path (neo4j)

I try to build a new relationship from the allshortpath returned path.
$
MATCH (p1:Category {catName: "Main_topic_classifications"}),
(p2:Category {catName: "Monarchs_of_the_Bulgars"}),
path = allShortestPaths((p2)-[:SUBCAT_OF*]->(p1))
FOREACH (s IN rels(path) |
MERGE (startNode(s))-[:NEW_SUBCAT]->(ENDNODE(s)))
However, when I run this previous query I obtained this error:
Neo.ClientError.Statement.SyntaxError: Invalid input '(': expected an identifier character, whitespace, NodeLabel, a property map or ')' (line 5, column 24 (offset: 248))
" MERGE (:startNode(s))-[:NEW_REL]->(:ENDNODE(s)))"
^
The Cypher language does not allow a node pattern to contain a function that returns the node (even though that would be very convenient).
This query (which first creates the node variables s and e, so that they can be used in node patterns) should work for you:
MATCH
(p1:Category {catName: "Main_topic_classifications"}),
(p2:Category {catName: "Monarchs_of_the_Bulgars"}),
path = allShortestPaths((p2)-[:SUBCAT_OF*]->(p1))
UNWIND RELATIONSHIPS(path) AS rel
WITH STARTNODE(rel) AS s, ENDNODE(rel) AS e
MERGE (s)-[:NEW_SUBCAT]->(e)

NEO4J I try to do an foreach script with function but so fail

I need to change some value in Neo4J list
I use apoc.date.add to change timestamp of my date but in foreach it's failed
This is my code:
MATCH path=(t:Trip)
FOREACH (n IN rels(path) | set SET n.EndTime = apoc.date.format(apoc.date.add(apoc.date.parse(n.EndTime,'ms',"yyyy-MM-dd'T'HH:mm:ss'Z'"),"ms",86400000,"ms"),"ms","yyyy-MM-dd'T'HH:mm:ss'Z'")
But that does not work
can anyone help me
regards
I fixed some typo errors in your Cypher query. Try it:
MATCH path=(t:Trip)
FOREACH (n IN rels(path) |
SET n.EndTime = apoc.date.format(apoc.date.add(apoc.date.parse(n.EndTime,'ms',"yyyy-MM-dd'T'HH:mm:ss'Z'"),"ms",86400000,"ms"),"ms","yyyy-MM-dd'T'HH:mm:ss'Z'")
)
Removed the extra set keyword and added an ) in the end.

Remove duplicates from Node array properties

I have a property A on my nodes that holds an array of string values:
n.A=["ABC","XYZ","123","ABC"]
During merges I frequently will write code similar to n.A = n.A + "New Value". The problem I've been running into is that I end up with duplicate values in my arrays; not insurmountable but I'd like to avoid it.
How can I write a cypher query that will remove all duplicate values from the array A? Some duplicates have already been inserted at this point, and I'd like to clean them up.
When adding new values to existing arrays how can I make sure I only save a copy of the array with distinct values? (may end up being the exact same logic as used to solve my first question)
The query to add a non-duplicate value can be done efficiently (in this example, I assume that the id and newValue parameters are provided):
OPTIONAL MATCH (n {id: {id}})
WHERE NONE(x IN n.A WHERE x = {newValue})
SET n.A = n.A + {newValue};
This query does not create a temporary array, and will only alter the n.A array if it does not already contain the {newValue} string.
[EDITED]
If you want to (a) create the n node if it does not already exist, and (b) append {newValue} to n.A only if {newValue} is not already in n.A, this should work:
OPTIONAL MATCH (n { id: {id} })
FOREACH (x IN (
CASE WHEN n IS NULL THEN [1] ELSE [] END ) |
CREATE ({ id: {id}, A: [{newValue}]}))
WITH n, CASE WHEN EXISTS(n.A) THEN n.A ELSE [] END AS nA
WHERE NONE (x IN nA WHERE x = {newValue})
SET n.A = nA + {newValue};
If the OPTIONAL MATCH fails, then the FOREACH clause will create a new node node (with the {id} and an array containing {newValue}), and the following SET clause will do nothing because n would be NULL.
If the OPTIONAL MATCH succeeds, then the FOREACH clause will do nothing, and the following SET clause will append {newValue} to n.A iff that value does not already exist in n.A. If the SET should be performed, but the existing node did not already have the n.A property, then the query would concatenate an empty array to {newValue} (thus generating an array containing just {newValue}) and set that as the n.A value.
Combined some of the information on UNWIND with other troubleshooting and came up with the following Cypher query for removing duplicates from existing array properties.
match (n)
unwind n.system as x
with distinct x, n
with collect(x) as set, n
set n.system = set
Once you've cleaned up existing duplicates, you can use this when adding new values:
match (n)
set n.A = filter (x in n.A where x<>"newValue") + "newValue"

Cypher - Best way to create relationships between two arrays of nodes

I have two arrays of nodes
TYPE1 : [Node1, Node2, ...NodeN]
TYPE2 : [OtherNode1, OtherNode2....OtherNodeN]
I'm trying to connect each TYPE1 node to its corresponding TYPE2 node as follows.
(Node1) -[:RELATED_TO] -> (OtherNode1)
It's a simple one-to-one correspondence.
I used
MATCH (x:TYPE1),(y:TYPE2)
with x, y
with COLLECT(x) as n1, COLLECT(y) as n2
FOREACH(i in RANGE(0, 9) |
CREATE (n1[i])-[:RELATED_TO]->(n2[i])
)
which fails with
Error: Invalid input '[': expected an identifier character, node labels, a property map, ')' or a relationship pattern (line 4, column 21)
I have two questions.
What am I doing wrong in the query?
What is the best way to accomplish what I'm doing?
Many thanks!
Consider the following example data:
FOREACH (i IN range(1,10) | CREATE (:TYPE1), (:TYPE2))
Because you aren't interested in ordering your collections by any properties, you'll just be joining nodes in whatever order they are found by MATCH. The following query will do (what I think) you are trying to do, though it is inelegant:
MATCH (x:TYPE1), (y:TYPE2)
WITH COLLECT(DISTINCT x) AS n1, COLLECT(DISTINCT y) AS n2
WHERE LENGTH(n1) = LENGTH(n2)
FOREACH (i IN RANGE(0, LENGTH(n1) - 1) |
FOREACH (node IN [n1[i]] |
FOREACH (othernode IN [n2[i]] |
MERGE (node)-[:RELATED_TO]-(othernode)
)
)
)
Browser view post-query:

Neo4j Cypher Query Error while executing nested Foreach

I am trying to execute the following cypher query to create a relationship between all the nodes which are present in the collection nodelist.
START n=node(*)
MATCH(n)
WHERE has(n.Gender)
WITH n.Gender as Gender
WITH collect(n) as nodelist
FOREACH (i in RANGE(0,length(nodelist-2))|
FOREACH(si in [nodelist[i]|
FOREACH(si2 in[nodelist[i+1] |
CREATE UNIQUE (si-[:KNOWS]->si2))))
It gives me an error in the second FOREACH loop at nodelist[i] .
I tried putting nodelist(i) and it gives me an error saying
`-' expected but '|' found
Any idea where I am going wrong?
If you want to create a knows relation between all nodes in a collection:
MATCH(n)
WHERE has(n.Gender)
WITH collect(n) as nodelist
FOREACH (x IN nodelist |
FOREACH (y IN filter(z IN nodelist WHERE NOT (z=x)) |
MERGE (x)-[:knows]-(y)
)
)
Note the direction of your relations. Each node will be connected to each other node in nodelist once in a particular direction. If you want double relations i.e. a-[knows]->b as well as b-[:knows]->a you'll have to modify the query accordingly.
Your first FOREACH should be like below. Also I think line 4 is redundant in your query. In case you want to collect gender you can directly do collect(n.Gender) as Gender. Incase you do need to use that line you need to pass n in the first WITH too, otherwise it wont be reconginized at the second WITH clause. Also you dont need to start with START n=node(*)
Try below
MATCH(n)
WHERE has(n.Gender)
WITH collect(n) as nodelist,collect(n.Gender) as Gender
FOREACH (i in RANGE(0,length(nodelist)-2)|
FOREACH(si in [nodelist[i]]|
FOREACH(si2 in [nodelist[i+1]] |
CREATE UNIQUE (si)-[:KNOWS]->(si2) )))

Resources