I have a couple of nodes and I need to add a incremental value in all nodes of a particular label.
match wd= (w:MYNODE)
forEach(n IN nodes(wd)|
set n.incrementalId=??
);
currently I have tried for each to traverse on each node but unable to get index of the each loop
ps: I have also tried with but unable to increment ie ++ the current value.
This is a more succinct version of #InverseFalcon's first query (and resembles the form of the query in your question):
MATCH (w:MYNODE)
WITH COLLECT(w) as ws
FOREACH(i IN RANGE(0, SIZE(ws)-1) | SET (ws[i]).incrementalId = i);
If the nodes already exist, then you'll have to use a more awkward approach using collections and indexes:
MATCH (w:MYNODE)
WITH collect(w) as myNodes
UNWIND range(0, size(myNodes)-1) as index
WITH index, myNodes[index] as node
SET node.incrementalId = index
If the nodes don't already exist, and you want to create some number of them with incremental ids, then it's an easier task using the range() function to produce a list of indexes:
UNWIND range(1, 100) as id
CREATE (:MYNODE {id:id})
Related
I'm trying to read in a row of data (not from csv but passed as parameters) by unwinding and then merging. It seems that the match part of the query under the unwind is taking a really long time (whereas a simple create is more or less instant). I'm a bit confused because the the match should be fairly quick to run since it can index on the label first.
Here's a minimal version of my query (way more data will be input than just an id in real life):
WITH *, timestamp() as time0
WITH * UNWIND range(0, 9000) as unwound_data0
MERGE (node0:Node {id: unwound_data0}) ON CREATE SET node0.dbcreated = time0
WITH time0, collect(node0) as nodes0
RETURN time0
If I simplify it to
UNWIND range(0, 9000) as unwound_data0
MATCH (node0: Node)
RETURN node0
It takes just as long. But if I change match to create, then its very fast.
Any ideas on how to speed this up?
Thought I would provide a slightly more detailed answer for anyone landing here. To speed this up you may need to create an index for the id property you are attempting to match on.
You can create an index in Cypher with the below command:
CREATE INDEX index_name
FOR (a:LabelName)
ON (a.propertyName)
Set index_name, LabelName and propertyName to the values relevant to you.
If the property you are merging/matching on is unique then you can instead create a constraint in cypher with the below command:
CREATE CONSTRAINT constraint_name
ON (a:LabelName)
ASSERT a.id_property IS UNIQUE
Creating a constraint on the property of interest creates an index that will be used during lookup operations. It also ensures that a property is unique, throwing an error if you try to create a node with the same property value.
I've got a list of id's and a list of values. I want to catch each node with the id and set a property by the value.
With just one Node that is super basic:
MATCH (n) WHERE n.id='node1' SET n.name='value1'
But i have a list of id's ['node1', 'node2', 'node3'] and same amount of values ['value1', 'value2', 'value3'] (For simplicity i used a pattern but values and id's vary a lot). My first approach was to use the query above and just call the database each time. But nowadays this isn't appropriate since i got thousand of id's which would result in thousand of requests.
I came up with this approach that I iterate over each entry in both lists and set the values. The first node from the node list has to get the first value from the value list and so on.
MATCH (n) WHERE n.id IN["node1", "node2"]
WITH n, COLLECT(n) as nodeList, COLLECT(["value1","value2"]) as valueList
UNWIND nodeList as nodes
UNWIND valueList as values
FOREACH (index IN RANGE(0, size(nodeList)) | SET nodes.name=values[index])
RETURN nodes, values
The problem with this query is that every node gets the same value (the last of the value list). The reason is in the last part SET nodes.name=values[index] I can't use the index on the left side nodes[index].name - doesn't work and the database throws error if i would do so. I tried to do it with the nodeList, node and n. Nothing worked out well. I'm not sure if this is the right way to achieve the goal maybe there is a more elegant way.
Create pairs from the ids and values first, then use UNWIND and simple MATCH .. SET query:
// THe first line will likely come from parameters instead
WITH ['node1', 'node2', 'node3'] AS ids,['value1', 'value2', 'value3'] AS values
WITH [i in range(0, size(ids)) | {id:ids[i], value:values[i]}] as pairs
UNWIND pairs AS pair
MATCH (n:Node) WHERE n.id = pair.id
SET n.value = pair.value
The line
WITH [i in range(0, size(ids)) | {id:ids[i], value:values[i]}] as pairs
combines two concepts - list comprehensions and maps. Using the list comprehension (with omitted WHERE clause) it converts list of indexes into a list of maps with id,value keys.
I run a Cypher query and update labels of the nodes matching a certain criteria. I also want to update nodes that do not pass that criteria in the same query, before I update the matched ones. Is there a construct in Cypher that can help me achieve this?
Here is a concrete formulation. I have a pool of labels from which I choose and assign to nodes. When I run a certain query, I assign one of those labels, l, to the nodes returned under the conditions specified by WHERE clause in the query. However, l could have been assigned to other nodes previously, and I want to rid all those nodes of l which are not the result of this query.
The conditions in WHERE clause could be arbitrary; hence simple negation would probably not work. An example code is as follows:
MATCH (v)
WHERE <some set of conditions>
// here I want to remove 'l' from the nodes
// not satisfied by the above condition
SET v:l
I have solved this problem by using a temporary label through this process:
Assign x to v.
Remove l from all nodes.
Assign l to all nodes containing x.
Removing x from all nodes.
Is there a better way to achieve this in Cypher?
This seems like one reasonable solution:
MATCH (v)
WITH REDUCE(s = {a:[], d:[]}, x IN COLLECT(v) |
CASE
WHEN <some set of conditions> AND NOT('l' IN LABELS(x)) THEN {a: s.a+x, d: s.d}
WHEN 'l' IN LABELS(x) THEN {a: s.a, d: s.d+x}
END) AS actions
FOREACH (a IN actions.a | SET a:l)
FOREACH (d IN actions.d | REMOVE d:l)
The above query tests every node, and remembers in the actions.a list the nodes that need the l label but do not yet have it, and in the actions.d list the nodes that have the label but should not. Then it performs the appropriate action for each list, without updating any nodes that are already OK.
I have the following cql (which does not work):
MATCH p = (c2:Config)<-[:OVERRIDES*]-(c1:Config)
WHERE c1['properties.name'] = 'NodeA'
AND NOT (c2)-[:OVERRIDES]->()
UNWIND NODES(p) AS props
RETURN props.`properties.name` as name,
PROPERTIES(props) AS properties,
SIZE(nodes(p)) AS `index`
What I'm trying to do is generate an index value that either accumulates or decrements with each row. I thought maybe the number of nodes in NODES(p) would go up/down as the graph was processed, but it stays constant. Is there anyway to do what I want??
You've unwound the nodes in the path, but want to get the index of each of these nodes in the path?
You'll need to use a different approach here, as you don't get index info when you UNWIND the nodes directly. You'll need to start with the index first, and then get each node in the path via the index in the list:
...
UNWIND range(0,size(nodes(p))-1) as index
WITH p, nodes(p)[index] as props
...
Initial setup of the sample database is provided link to console
There are various cases and within each case, there are performers(with properties id and name). This is the continuation of problems defined problem statement and solution to unique node creation
The solution in the second link is (credits to Christophe Willemsen
)
MATCH (n:Performer)
WITH collect(DISTINCT (n.name)) AS names
UNWIND names as name
MERGE (nn:NewUniqueNode {name:name})
WITH names
MATCH (c:Case)
MATCH (p1)-[r:RELATES_TO]->(p2)<-[:RELATES]-(c)-[:RELATES]->(p1)
WITH r
ORDER BY r.length
MATCH (nn1:NewUniqueNode {name:startNode(r).name})
MATCH (nn2:NewUniqueNode {name:endNode(r).name})
MERGE (nn1)-[rf:FINAL_RESULT]->(nn2)
SET rf.strength = CASE WHEN rf.strength IS NULL THEN r.value ELSE rf.strength + r.value END
This solution achieved what was asked for.
But I need to achieve something like this.
foreach (Case.id in the database)
{
foreach(distinct value of r.Length)
{
//update value property of node normal
normal.value=normal.value+0.5^(r.Length-2)
//create new nodes and add the result as their relationship or merge it to existing one
MATCH (nn1:NewUniqueNode {name:startNode(r).name})
MATCH (nn2:NewUniqueNode {name:endNode(r).name})
MERGE (nn1)-[rf:FINAL_RESULT]->(nn2)
//
rf.strength=rf.strength + r.value*0.5^(r.Length-2);
}
}
The problem is to track the change in the case and then the r.Length property. How can it be achieved in Cypher?
I will not redo the last part, where setting strengths.
One thing though, in your console link, there is only one Normal node, so why do you need to iterate over each case, you can just match distinct relationships lengths.
By the way for the first part :
MATCH (n:Case)
MATCH (n)-[:RELATES]->()-[r:RELATES_TO]->()<-[:RELATES]-(n)
WITH collect(DISTINCT (r.length)) AS lengths
MATCH (normal:Normal)
UNWIND lengths AS l
SET normal.value = normal.value +(0.5^l)
RETURN normal.value
Explanations :
Match the cases
Foreach case, match the RELATES_TO relationships of Performers for that Case
collect distinct lengths
Match the Normal node, iterate the the distinct lengths collection and setting the proper value on the normal node