Cannot access the property value after creation Neo4j - neo4j

My problem is, after I insert several nodes with uri properties of the type:
"http://www.w3.org/TR/2003/PR-owl-guide-20031209/wine#Red"
Later, I want to extract the part after hashtag (which is "Red" for this case), so I use the split function and create property name with value tail(records):
MATCH (n) WITH split (n.uri, '#') AS records, n
WHERE head(records) = 'http://www.w3.org/TR/2003/PR-owl-guide-20031209/wine'
SET n.name = tail(records)
after successfully creating the property name with appropriate name for a set of nodes, I check (everything perfect for now):
Match (n) Return keys(n)
I create a label (concept) for all the nodes:
MATCH (n) SET n :concept RETURN n
Later trying to access the value of property "name":
Match (n{name: 'Red'}) RETURN n
or
Match (n:concept{name: 'Red'}) RETURN n
I am getting the empty response (obviously it is not connected to label created, as even before I couldn't access it). I would appreciate your help. Thank you!

The TAIL(x) function returns a list (of all values in x after the first), not a scalar value. Therefore, the name in your example would have the value ["Red"], not "Red".
Instead of:
SET n.name = tail(records)
your Cypher code should have used the following (assuming your uri values always embed a "#"):
SET n.name = records[1]

Related

Update nodes by a list of ids and values in one cypher query

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.

Collection in WITH clause gets expanded to one element per row

I want to create a map projection with node properties and some additional information.
Also I want to collect some ids in a collection and use this later in the query to filter out nodes (where ID(n) in ids...).
The map projection is created in an apoc call which includes several union matches.
call apoc.cypher.run('MATCH (n)-[:IS_A]->({name: "User"}) MATCH (add)-[:IS_A]->({name: "AdditionalInformationForUser"}) RETURN n{.*, info: collect(add.name), id: ID(n)} as nodeWithInfo UNION MATCH (n)-[:IS_A]->({Department}) MATCH (add)-[:IS_A]->({"AdditionalInformationForDepartment"}) RETURN n{.*, info: collect(add.name), id: ID(n)} as nodeWithInfo', NULL) YIELD value
WITH (value.nodeWithInfo) AS nodeWithInfo
WITH collect(nodeWithInfo.id) as nodesWithAdditionalInfosIds, nodeWithInfo
MATCH (n)-[:has]->({"Vacation"})
MATCH (u)-[:is]->({"Out of Order"})
WHERE ID(n) in nodesWithAdditionalInfosIds and ID(u) in nodesWithAdditionalInfosIds
return n, u, nodeWithInfo
This does not return anything because, when the where part is evaluated it doesn´t check "nodesWithAdditionalInfosIds" as a flat list but instead only gets one id per row.
The problem only exists because I am passing the ids (nodesWithAdditionalInfosIds) AND the nodeProjection (nodeWithInfo) on in the WITH clause.
If I instead only use the id collection and don´t use the nodeWithInfo projection the following adjustement works and returns my only the nodes which ids are in the id collection:
...
WITH collect(nodeWithInfo.id) as nodesWithAdditionalInfosIds
MATCH (n)-[:has]->({"Urlaub"})
MATCH (u)-[:is]->({"Out of Order"})
WHERE ID(n) in nodesWithAdditionalInfosIds and ID(u) in nodesWithAdditionalInfosIds
return n, u
If I just return the collection "nodesWithAdditionalInfosIds" directly after the WITH clause in both examples this gets obvious. Since the first one generates a flat list in one result row and the second one gives me one id per row.
I have the feeling that I am missing a crucial knowledge about neo4js With clause.
Is there a way I can pass on my listOfIds and use it as a flat list without the need to have an exclusive WITH clause for the collection?
edit:
Right now I am using the following workaround:
After I do the check on the ID of "n" and "u" I don´t return but instead keep the filtered "n" and "u" nodes and start a second apoc call that returns "nodeWithInfo" like before.
WITH n, u
call apoc.cypher.run('MATCH (n)-[:IS_A]->({name: "User"}) MATCH (add)-[:IS_A]->({name: "AdditionalInformationForUser"}) RETURN n{.*, info: collect(add.name), id: ID(n)} as nodeWithInfo UNION MATCH (n)-[:IS_A]->({Department}) MATCH (add)-[:IS_A]->({"AdditionalInformationForDepartment"}) RETURN n{.*, info: collect(add.name), id: ID(n)} as nodeWithInfo', NULL) YIELD value
WITH (value.nodeWithInfo) AS nodeWithInfo, n, u
WHERE nodeWithInfo.id = ID(n) OR nodeWithInfo.id = ID(u)
RETURN nodeWithInfo, n, u
This way I can return the nodes n, u and the additional information (to one of the nodes) per row. But I am sure there must be a better way.
I know ids in neo4j have to be used with care, if at all. In this case I only need them to be valid inside this query, so it doesn´t matter if the next time the same node has another id.
The problem is stripped down to the core problem (in my opinion), the original query is a little bigger with several UNION MATCH inside apoc and the actual match on nodes which ids are contained in my collection is checking for some more restrictions instead of asking for any node.
Aggregating functions, like COLLECT(), aggregate over a set of "grouping keys".
In the following clause:
WITH collect(nodeWithInfo.id) as nodesWithAdditionalInfosIds, nodeWithInfo
the grouping key is nodeWithInfo. Therefore, each nodesWithAdditionalInfosIds would always be a list containing one value.
And in the following clause:
WITH collect(nodeWithInfo.id) as nodesWithAdditionalInfosIds
there is no grouping key. Therefore, in this situation, nodesWithAdditionalInfosIds will contain all the nodeWithInfo.id values.

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.

Keep track of Changes in Neo4j - Achieve functionality like a "flag" variable in standard programming

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

Neo4j - query issue for same label and one property the same

I have a problem in querying for two nodes with one similar property, one different property and having the same label. For one property, the property is the same between both nodes, i.e. both have a property called "Name" and both have the same value ("Data Storage"). For the other property though called "Note", they have different values. Both share the same label called "Issue". When I use the query below, I get both nodes.
match (n:Issue) where n.name="Data Storage" return n;
However, when I query with the following query...
match (n:Issue) where n.name="Data Storage" and n.note="xxxx" return n;
...it only works for one of the nodes and not for the other. I've tried creating the node which doesn't query and it seems to work fine. But I also did it with a different label. Is this some bug around not being able to query a node having the same label and sharing at least one common property?
match (n:Issue) where n.name="Data Storage" and n.note="xxxx" return n;
will match all nodes with label Issue, the value of the "name" property = "Data Storage" AND the value of the "note" property = "xxxx".
As you've described the 2 nodes, the value of the note property is different on each. The one matching xxxx is the only one that can be returned.
What is the goal of this query?
If this was your real question.
Query a node having the same label and sharing at least one common property?
you can try this for each property and do a union over all
MATCH (n:Issue)
WITH n.name, collect(n) as nodes, count(*) as cnt
WHERE cnt > 1
RETURN nodes
or this will be less performant
MATCH (n:Issue)
WITH n
MATCH (m:Issue)
WHERE m<>n AND (n.name = m.name OR n.note = m.note)
RETURN n,m

Resources