Neo4j: get all relations between queried nodes - neo4j

I want to make a cypher query that do below tasks:
there is a given start node, and I want to get all related nodes in 2 hops
sort queried nodes by hops asc, and limit it with given number
and get all relations between result of 1.
I tried tons of queries, and I made below query for step 1, 2
MATCH path=((start {eid:12018})-[r:REAL_CALL*1..2]-(end))
WITH start, end, path
ORDER BY length(path) ASC
RETURN start, collect(distinct end)[..10]
But when I try to get relationships in queried path with below query, it returns all relationships in the path :
MATCH path=((start {eid:12018})-[r:REAL_CALL*1..2]-(end))
WITH start, end, path
ORDER BY length(path) ASC
RETURN start, collect(distinct end)[..10], relationships(path)
I think I have to match again with result of first match instead of get relationships from path directly, but all of my attempts have failed.
How can I get all relationships between queried nodes?
Any helps appreciate, thanks a lot.

[EDITED]
Something like this may work for you:
MATCH (start {eid:12018})-[rels:REAL_CALL*..2]-(end)
RETURN start, end, COLLECT(rels) AS rels_collection
ORDER BY
REDUCE(s = 2, rs in rels_collection | CASE WHEN SIZE(rs) < s THEN SIZE(rs) ELSE s END)
LIMIT 10;
The COLLECT aggregation function will generate a collection (of relationship collections) for each distinct start/end pair. The LIMIT clause limits the returned results to the first 10 start/end pairs, based on the ORDER BY clause. The ORDER BY clause uses REDCUE to calculate the minimum size of each path to a given end node.

Related

Find closest node that matches criteria but cannot have intermediary nodes that have certain properties

I have the following criteria:
From a starting node with an internal ID of X, I want to grab the closest WorkOrderNode that has an action_code of "INS".
However, between those two nodes, there can be an arbitrary number of nodes, and I need to make sure these intermediary nodes are not WorkOrderNodes that have action_codes other than "MV" or "SPT".
Here is my latest attempt:
MATCH p=(a)<-[*]-(b:WorkOrderNode {action_code: 'INS'})
WHERE ID(a)=105
RETURN b, size(relationships(p)) as distance
ORDER BY distance
LIMIT 1
This fulfills the 1st criteria but I'm having problems implementing the 2nd criteria. I tried using AND NOT EXISTS((b)-[*]->(c:WorkOrderNode) WHERE c.action_code NOT IN ['MV', 'SPT'] in the parent WHERE clause but neo4j throws an error because I can't have a WHERE clause in an EXISTS clause.
Might not be the most optimized, but I would try the following:
MATCH p=(a)<-[*]-(b:WorkOrderNode {action_code: 'INS'})
WHERE ID(a)=105 AND
NONE(node IN nodes(p) WHERE node:WorkOrderNode AND node.action_code IN ['MV', 'SPT'])
RETURN b, length(p) as distance
ORDER BY distance
LIMIT 1

neo4j indegree outdegree union

I want to compute Indegree and Outdegree and return a graph that has a connection between top 5 Indegree nodes and top 5 Outdegree nodes. I have written a code as
match (a:Port1)<-[r]-()
return a.id as NodeIn, count(r) as Indegree
order by Indegree DESC LIMIT 5
union
match (n:Port1)-[r]->()
return n.id as NodeOut, count(r) as Outdegree
order by Outdegree DESC LIMIT 5
union
match p=(u:Port1)-[:LinkTo*1..]->(t:Port1)
where u.id in NodeIn and t.id in NodeOut
return p
I get an error as
All sub queries in an UNION must have the same column names (line 4, column 1 (offset: 99)) "union"
What are the changes that I need to do to the code?
There's a few things we can improve.
The matches you're doing isn't the most efficient way to get incoming and outgoing degrees for relationships.
Also, UNION can only be used to combine query results with identical columns. In this case, we won't even need UNION, we can use WITH to pipe results from one part of a query to another, and COLLECT() the nodes you need in between.
Try this query:
match (a:Port1)
with a, size((a)<--()) as Indegree
order by Indegree DESC LIMIT 5
with collect(a) as NodesIn
match (a:Port1)
with NodesIn, a, size((a)-->()) as Outdegree
order by Outdegree DESC LIMIT 5
with NodesIn, collect(a) as NodesOut
unwind NodesIn as NodeIn
unwind NodesOut as NodeOut
// we now have a cartesian product between both lists
match p=(NodeIn)-[:LinkTo*1..]->(NodeOut)
return p
Be aware that this performs two NodeLabelScans of :Port1 nodes, and does a cross product of the top 5 of each, so there are 25 variable length path matches, which can be expenses, as this generates all possible paths from each NodeIn to each NodeOut.
If you only one the shortest connection between each, then you might try replacing your variable length match with a shortestPath() call, which only returns the shortest path found between each two nodes:
...
match p = shortestPath((NodeIn)-[:LinkTo*1..]->(NodeOut))
return p
Also, make sure your desired direction is correct, as you're matching nodes with the highest in degree and getting an outgoing path to nodes with the highest out degree, that seems like it might be backwards to me, but you know your requirements best.

neo4j how to use count(distinct()) over the nodes of path

I search the longest path of my graph and I want to count the number of distinct nodes of this longest path.
I want to use count(distinct())
I tried two queries.
First is
match p=(primero)-[:ResponseTo*]-(segundo)
with max(length(p)) as lengthPath
match p1=(primero)-[:ResponseTo*]-(segundo)
where length(p1) = lengthPath
return nodes(p1)
The query result is a graph with the path nodes.
But if I tried the query
match p=(primero)-[:ResponseTo*]-(segundo)
with max(length(p)) as lengthPath
match p1=(primero)-[:ResponseTo*]-(segundo)
where length(p1) = lengthPath
return count(distinct(primero))
The result is
count(distinct(primero))
2
How can I use count(distinct()) over the node primero.
Node Primero has a field called id.
You should bind at least one of those nodes, add a direction and also consider a path-limit otherwise this is an extremely expensive query.
match p=(primero)-[:ResponseTo*..30]-(segundo)
with p order by length(p) desc limit 1
unwind nodes(p) as n
return distinct n;

Including vars in Neo4j WITH statement changes query output

I'm trying to find the number of nodes of a certain kind in my database that are connected to more than one other node of another kind. In my case, it's place nodes connected to several name nodes. I have a query that works:
MATCH rels=(p:Place)-[c:Called]->(n:Name)
WITH p,count(n) as counts
WHERE counts > 1
RETURN p;`
However, that only returns the place nodes, and ideally I'd like it to return all the nodes and edges involved. I've found a question on returning variables from before the WITH, but if I include any of the other variables I've defined, the query returns no responses, i.e. this query returns nothing:
MATCH rels=(p:Place)-[c:Called]->(n:Name)
WITH p, count(n) as counts, rels
WHERE counts > 1
RETURN p;
I don't know how to return the information that I want without changing the results of the query. Any help would be much appreciated
The reason your second query returns nothing is because its WITH clause specifies as aggregation "grouping keys" both p and rels. Since each rels path has only a single n value, counts would always be 1.
Something like this might work for you:
MATCH path=(p:Place)-[:Called]->(:Name)
WITH p, COLLECT(path) as paths
WHERE SIZE(paths) > 1
RETURN p, paths;
This returns each matching Place node and all its paths.
Try this:
MATCH (p:Place)-[c:Called]->(n:Name)
WHERE size((p)-[:Called]->(:Name)) > 1
WITH p,count(n) as counts, collect(n) AS names, collect(c) AS calls
RETURN p, names, calls, counts ORDER BY counts DESC;
This query makes use of Cypher's collect() function to create lists of the names and called relationships for each place that has more than Called relationship with a Name node.

With cypher, how can i get the less Transfer Path in my DB?

i created a Bus Route database with neo4j, you can download it here https://www.dropbox.com/s/zamkyh2aaw3voe6/data.rar?dl=0
i want to get the less Transfer Path, and i am doing like :
MATCH path=allShortestPaths((start:潍坊_STATION {name:'寒亭一村'})-[rels*..50]->(end:潍坊_STATION {name:'火车站'}))
RETURN NODES(path) AS stations,relationships(path) AS path,
length(FILTER(index IN RANGE(1, length(rels)-1) WHERE (rels[index]).bus <> (rels[index - 1]).bus)) AS transfer_count
ORDER BY transfer_count
LIMIT 10
But the result is not correct , who can help me ?
Does this give you what you want:
MATCH path=allShortestPaths((start:潍坊_STATION {name:'寒亭一村'})-[rels*..50]->(end:潍坊_STATION {name:'火车站'}))
RETURN NODES(path) AS stations,relationships(path) AS route,
length(path) AS transfer_count
ORDER BY transfer_count ASC
LIMIT 10
This should return 10 paths connecting the two stations ordered by the length of the path. Although, the ordering here isn't needed. Since we are using the allShortestPaths function, all paths found will be the same length (in the case of the data you shared, 22).
You mention transfer time in your question, but I don't see the time as a property in your data. If you have the time stored you could use the reduce function to sum the travel time and order by that.
Edit
Use the extract function to collect the bus names from the relationships in the path:
MATCH
path=allShortestPaths((start:潍坊_STATION {name:'寒亭一村'})-[rels*..50]->(end:潍坊_STATION {name:'火车站'}))
RETURN
NODES(path) AS stations, relationships(path) AS route,
length(path) AS transfer_count, extract(x in rels | x.bus) AS buses
ORDER BY transfer_count ASC
LIMIT 10

Resources