Cypher Statement.SyntaxError Error in looping with foreach and creating relationships - graph

I would like to link nodes pairwise with a common attributes. I use Cypher with neo4j 5.2.
I have the following statement:
MATCH (n:platformUser {phone : 123456})
WITH collect(n) as nodes
FOREACH (i in range(0, size(nodes)-1) |
CREATE (nodes[i])-[:test_relation]->(nodes[i+1]) )
Unfortunately it does not work. I have the following error message.
Invalid input '(': expected "allShortestPaths" or "shortestPath" (line 4, column 12
(offset: 116))
" CREATE (nodes[i])-[:test_relation]->(nodes[i+1]))"
^
I have try a couple things with the brackets or without .My guess is that is a small bug but I don't see what I am missing.
Does someone know what is wrong ? Thanks in advance

You can do UNWIND and it will do a cartesian product (combinations) in the collection nodes. On line#5, you need to ensure that n1 is not the same with n2 thus n1<n2
MATCH (n:platformUser {phone : 123456})
WITH collect(n) as nodes, size(collect(n)) as sz
UNWIND nodes[0..sz-1] as n1
UNWIND nodes[1..sz] as n2
WITH n1, n2 WHERE n1<n2
CREATE (n1)-[:test_relation]->(n2)
Sample result:

In case you want to use an APOC function, you can use below. The APOC function will give you a list of all two nodes combination with phone: 123456. Then for each item on the list (UNWIND) is a list of two nodes, n1 and n2. Lastly, create the relationship between n1 and n2.
MATCH (n:platformUser {phone : 123456})
WITH apoc.coll.combinations(collect(n), 2) as nodes
UNWIND nodes as node
WITH node[0] as n1, node[1] as n2
CREATE (n1)-[:test_relation]->(n2)

Related

Creating edges in neo4j based on query results

I'm modelling a search term transition graph in a e-commerce software as a graph of nodes (terms) and edges (transitions). If a user types e.g. iphone in the search bar and then refines the query to iphone 6s this will be modeled as two nodes and a edge between those nodes. The same transition of terms of the different users will result in several edges between the nodes.
I'd now like to create an edge with a cumulated weight of 4 to represent that 4 users did this specific transition. How can I combine the results of a count(*) query with a create query to produce an edge with a property weight = 4
My count(*) query is:
MATCH (n:Term)-[r]->(n1:Term)
RETURN type(r), count(*)
I'd expect the combined query to look like this, but this kind of sql like composition seems not to be possible in cypher:
MATCH (n:Term), (n1:Term)
WHERE (n)-[tr:TRANSITION]->(n1)
CREATE (n)-[actr:ACC_TRANSITION {count:
MATCH (n:Term)-[r]->(n1:Term) RETURN
count(*)}
]->(n1)
RETURN n, n1
A non generic query to produce the accumulated transition that works is:
MATCH (n:Term), (n1:Term)
WHERE n.term = 'iphone' AND n1.term ='iphone 6s'
CREATE (n)-[actr:ACC_TRANSITION {count: 4}]->(n1)
RETURN n, n1
Any other ideas on how to approach and model this problem?
Use WITH like this:
MATCH (n:Term)-[r]->(n1:Term)
WITH n as n, count(*) as rel_count, n1
CREATE (n)-[:ACC_TRANSITION {count:rel_count}]->(n1)
RETURN n, n1
If you match the nodes and relationship first and then use set, you will not produce duplicate nodes or relationships
Match (n:Term)-[r]->(n1.Term)
with n as nn,count(r) as rel_count,n1 as nn1
set r.ACC_TRANSITION=rel_count
return nn,nn1,r
The create function will create duplicates.

Recursive multiple relationships

I'm attempting to recursively perform alternate match statements with 2 specific relationships.
For example, Pets are owned by a Person. Pets LIKE other people (not owner) Those people have pets owned by them, who like other people etc.
match (n.Person {id.123})<-[r.OwnedBy]-(p.Pet) Return n, r, p
match (p.Pet {id.123})-[r.Likes]->(n.Person) Return p, r, n
Notice the directional relationships involved - #1 is backwards, #2 is forwards.
What I want to do is to, given a person(id),
1. Display pets [OwnedBy] this person(id)
2. Display people [Liked] by those pets
3. Display pets [OwnedBy] the people in 2.
etc. recursively
Independently, these Match statements work. together, they do not.
I tried adding the 2nd match statement, using different variables, then it will go down 2 levels and stop.
In the real data set, there are dozens of nodes and relationships. I'm trying to limit the display to a 'tree' view of only these 2 relationships/nodes.
Thanks!
How about this?
match (n:Person {id:123})<-[:OwnedBy]-(p:Pet)-[:Likes]->(n2:Person)<-[:OwnedBy]-(p2:Pet)
return n, collect(distinct p) as pets, collect(distinct n2) as peopleLiked, collect(distinct p2) as petsOfPeopleLiked
Though if you're only interested in the graph display, this should work:
match path = (n:Person {id:123})<-[:OwnedBy]-(p:Pet)-[:Likes]->(n2:Person)<-[:OwnedBy]-(p2:Pet)
return path, n, p, n2, p2
You can also utilize APOC Procedures. This can handle showing these paths, using only these two types of relationships:
match (n:Person {id:123})
call apoc.path.expandConfig(n, {relationshipFilter:'<OwnedBy|Likes>'}) yield path
return path

Cypher query to stop graph traversal when reaching a hub

I have a graph database that contains highly connected nodes (hubs). These nodes can have more than 40000 relationships.
When I want to traverse the graph starting from a node, I would like to stop traversal at these hubs not to retrieve too many nodes.
I think I should use aggregation function and conditional stop based on the count of relationship for each node, but I didn't manage to write the good cypher query.
I tried:
MATCH p=(n)-[r*..10]-(m)
WHERE n.name='MyNodeName' AND ALL (x IN nodes(p) WHERE count(x) < 10)
RETURN p;
and also:
MATCH (n)-[r*..10]-(m) WHERE n.name='MyNodeName' AND COUNT(r) < 10 RETURN p;
I think you can't stop the query at some node if you MATCH a path of length 10. You could count the number of relationships for all nodes in the path, but only after the path is matched.
You could solve this by adding an additional label to the hub nodes and filter that in your query:
MATCH (a:YourLabel)
OPTIONAL MATCH (a)-[r]-()
WITH a, count(r) as count_rels
CASE
WHEN count_rels > 20000
THEN SET a :Hub
END
Your query:
MATCH p=(n)-[r*..10]-(m)
WHERE n.name='MyNodeName' AND NONE (x IN nodes(p) WHERE x:Hub)
RETURN p
I used this approach in a similar case.
Since Neo4j 2.2 there is a cool trick to use the internal getDegree() function to determine if a node is a dense node.
You also forgot the label (and probably index) for n
For your case that would mean:
MATCH p=(n:Label)-[r*..10]-(m)
WHERE n.name='MyNodeName' AND size((m)--()) < 10
RETURN p;

Path properties in Cypher

I have a following graph in Neo4j
(id:5,t:e)<--(id:4,t:w)<--(id:0;t:s)-->(id:1,t:w)-->(id:2,t:b)-->(id:3,t:e)
now I search paths from nodes with t:s to nodes with t:e such that only white-listed nodes with t:w are in-between.
So ideally i need a query to return only (0)-->(4)-->(5) but not (0)-->(1)-->(2)-->(3).
EDIT: i have forgotten to mention that paths may have variable length: from 0 to potentially infinity. It means that I may have an arbitrary number of "t:w" nodes
Best regards
Working just with the information that you have provided above you could use
MATCH p=({t:'s'})-->({t:'w'})-->({t:'e'}) RETURN p
Of course if an s could link directly to an e you will need to use variable length relationships matches.
MATCH p=({t:'s'})-[*0..1]->({t:'w'})-[]->({t:'e'})
RETURN DISTINCT p
EDIT - Paths of any length
MATCH p=({t:'s'})-[*0..1]->({t:'w'})-[*]->({t:'e'})
RETURN DISTINCT p
To match a path of any length use the * operator in the relationship path match. It is usually best to put some bounds on that match, an example of which is the *0..1 (length 0 to 1). You can leave either end open *..6 (length 1 to 6) or *2.. (length 2 to whatever).
The problem with this is that now you cannot guarantee the node types in the intervening nodes (so t:"b" will be matched). To avoid that I think you'll have to filter.
MATCH p=({t:'s'})-[*]->({t:'e'})
WHERE ALL (node IN NODES(p)
WHERE node.t = 's' OR node.t = 'w' OR node.t = 'e' )
RETURN p
End Edit
You should introduce labels to your nodes and use relationship types for traversal though as that is where Neo/Cypher is going to be able to help you out. You should also make sure that if you are matching on properties that they are indexed correctly.

Neo4J - Extracting graph as a list based on relationship strength

I have a typical friend of friend graph database i.e. a social network database. The requirement is to extract all the nodes as a list in such a way that the least connected nodes appear together in the list and the most connected nodes are placed further apart in the list.
Basically its asking a graph to be represented as a list and I'm not sure if we can really do that. For e.g. if A is related to B with strength 10, B is related to C with strength 80, A to C is 20
then how to place this in a list ?
A, B, C - no because then A is distant from C relatively more than B which is not the case
A, C, B - yes because A and B are less related that A,C and C,B.
With 3 nodes its very simple but with lot of nodes - is it possible to put them in a list based on relationship strength ?
Ok, I think this is maybe what you want. An inverse of the shortestPath traversal with weights. If not, tell me how the output should be.
http://console.neo4j.org/r/n8npue
MATCH p=(n)-[*]-(m) // search all paths
WHERE n <> m
AND ALL (x IN nodes(p) WHERE length([x2 IN nodes(p) WHERE x2=x])=1) // this filters simple paths
RETURN [n IN nodes(p)| n.name] AS names, // get the names out
reduce(acc=0, r IN relationships(p)| acc + r.Strength) AS totalStrength // calculate total strength produced by following this path
ORDER BY length(p) DESC , totalStrength ASC // get the max length (hopefully a full traversal), and the minimum strength
LIMIT 1
This is not going to be efficient for a large graph, but I think it's definitely doable--probably needs using the traversal/graphalgo API shortest path functionality if you need speed on a large graph.

Resources