Get nodes from LENGTH(r) - neo4j

This code
MATCH (n { name: 'Create node' })<-[r*]-(s { name: ';' })
WITH n,s, LENGTH(r) AS depth
RETURN n,s, depth
will return the number of relationships between first and last nodes. Is it possible to get the nodes that are in between those relationships?
Bonus question: is it possible to get them in order?
http://console.neo4j.org/r/z1iafh
(this code does not work in console, only on localhost. query to create nodes
create
(_0 {name:"CREATE"}),
(_1 {name:"("}),
(_2 {name:"node_name"}),
(_3 {name:")"}),
(_4 {name:";"}),
_1-[:CREATE_NODE_COMMAND]->_0,
_2-[:CREATE_NODE_COMMAND]->_1,
_3-[:CREATE_NODE_COMMAND]->_2,
_4-[:CREATE_NODE_COMMAND]->_3
)

You could augment your match statement to match the entire path from n to s and then you could use the nodes function on the path to return the collection of nodes in order (from n to s). If you want just the nodes between the start and end nodes you could return the collection form the second to the second last only.
MATCH p=(n { name: 'Create node' })<-[r*]-(s { name: ';' })
WITH n,s, size(r) as depth, length(p) as depth2, nodes(p) as nodes
RETURN n,s, depth, depth2, nodes[1..length(nodes)-1]
size() can be used to return the number of elements in a collection whereas length() should only be used to return the length of a path or a string. Its use on other objects (collections and patterns) may be deprecated in future neo4j versions; currently supported for backwards compatibility.

Related

Neo4j Matching Variable number of relationships from previous query

The code on the Neo4j docs shows the following for matching against a variable number of relationships.
MATCH (charlie { name: 'Charlie Sheen' })-[:ACTED_IN*1..3]-(movie:Movie)
RETURN movie.title
I am trying to do this based on a previous query using a WITH statement but cannot find the right syntax. Everything I try yields an error. I am aiming for something of the form:
MATCH p = ...
...
WITH length(p) AS len_p
MATCH (charlie { name: 'Charlie Sheen' })-[:ACTED_IN*len_p..len_p]-(movie:Movie)
RETURN movie.title
However, this syntax yields Neo.ClientError.Statement.SyntaxError.
What is the recommended way to do this?
[EDITED]
You can use the apoc.path.expandConfig procedure to perform variable-length relationship queries with dynamic bounds.
For example (to get paths of length len_p consisting of ACTED_IN relationships ending in a Movie node):
...
WITH length(p) AS len_p
MATCH (charlie:Person {name: 'Charlie Sheen'})
CALL apoc.path.expandConfig(charlie, {
relationshipFilter: 'ACTED_IN',
labelFilter: '>Movie',
minLevel: len_p,
maxLevel: len_p
}) YIELD path
RETURN LAST(NODES(path)).title AS title

cypher to combine nodes and relationships into a single column

So as a complication to this question, I basically want to do
MATCH (n:TEST) OPTIONAL MATCH (n)-[r]->() RETURN DISTINCT n, r
And I want to return n and r as one column with no repeat values. However, running
MATCH (n:TEST) OPTIONAL MATCH (n)-[r]->() UNWIND n+r AS x RETURN DISTINCT x
gives a "Type mismatch: expected List but was Relationship (line 1, column 47)" error. And this query
MATCH (n:TEST) RETURN DISTINCT n UNION MATCH ()-[n]->() RETURN DISTINCT n
Puts nodes and relationships in the same column, but the context from the first match is lost in the second half.
So how can I return all matched nodes and relationships as one minimal list?
UPDATE:
This is the final modified version of the answer query I am using
MATCH (n:TEST)
OPTIONAL MATCH (n)-[r]->()
RETURN n {.*, rels:collect(r {properties:properties(r), id:id(r), type:type(r), startNode:id(startNode(r)), endNode:id(endNode(r))})} as n
There are a couple ways to handle this, depending on if you want to hold these within lists, or within maps, or if you want a map projection of a node to include its relationships.
If you're using Neo4j 3.1 or newer, then map projection is probably the easiest approach. Using this, we can output the properties of a node and include its relationships as a collected property:
MATCH (n:TEST)
OPTIONAL MATCH (n)-[r]->()
RETURN n {.*, rels:collect(r)} as n
Here's what you might do if you wanted each row to be its own pairing of a node and a single one of its relationships as a list:
...
RETURN [n, r] as pair
And as a map:
...
RETURN {node:n, rel:r} as pair
EDIT
As far as returning more data from each relationship, if you check the Code results tab, you'll see that the id, relationship type, and start and end node ids are included, and accessible from your back-end code.
However, if you want to explicitly return this data, then we just need to include it in the query, using another map projection for each relationship:
MATCH (n:TEST)
OPTIONAL MATCH (n)-[r]->()
RETURN n {.*, rels:collect(r {.*, id:id(r), type:type(r), startNode:startNode(r), endNode:endNode(r)})} as n

Getting number of unique paths traversed while traversing using Neo4j traversal API

I have created following basic graph:
CREATE (:NodeType1 {prop1:'value1'})-[:RelType1 {prop2:'value2'}]->(:NodeType2 {prop3:'value3'})-[:RelType2 {prop4:'value4'}]->(:NodeType3 {prop5:'value5'})-[:RelType3 {prop6:'value6'}]->(:NodeType4 {prop7:'value7'})
CREATE (:NodeType1 {prop1:'value8'})-[:RelType1 {prop2:'value9'}]->(:NodeType2 {prop3:'value10'})-[:RelType2 {prop4:'value11'}]->(:NodeType3 {prop5:'value12'})-[:RelType3 {prop6:'value13'}]->(:NodeType4 {prop7:'value14'})
MATCH path=(n:NodeType1 {prop1:'value1'})-[*]->(m:NodeType4 {prop7:'value7'})
CREATE (n)-[:RelType1 {prop2:'value15'}]->(:NodeType2 {prop3:'value16'})-[:RelType2 {prop4:'value17'}]->(:NodeType3 {prop5:'value18'})-[:RelType3 {prop6:'value19'}]->(m)
The graph looks like this:
When I run following cypher:
MATCH path=(a:NodeType1 {prop1:"value1"})-[:RelType1]->(b)-[:RelType2]->(c)-[:RelType3]->(d)
RETURN count(nodes(path))
I get 2 as the output. It seems that nodes() doesnt actually return the number nodes in the path but simply the number of rows in the returned result, since if I return the path:
MATCH path=(a:NodeType1 {prop1:"value1"})-[:RelType1]->(b)-[:RelType2]->(c)-[:RelType3]->(d)
RETURN path
I get two rows in the returned result:
Now I am guessing how can I get the same output when doing traversal using Neo4J traversal API. I get number of unique nodes in the cypher as follows:
MATCH path=(a:NodeType1 {prop1:"value1"})-[:RelType1]->(b)-[:RelType2]->(c)-[:RelType3]->(d)
RETURN size(collect(distinct a))+size(collect(distinct b))+size(collect(distinct c))+size(collect(distinct d))
Above query correctly returns 6.
Same can be done in the traversal API by having static counter inside path expander which is incremented each time expand() is called. (Is there any better approach for this?)
public class MyPathExpander implements PathExpander{
static int nodeCount = 0;
#Override
public Iterable expand(Path path, BranchState state) {
Node lastNode = path.endNode();
nodeCount++; //**increment the count of nodes visited
if(lastNode.hasLabel(MyLabels.NodeType1))
return lastNode.getRelationships(MyRelations.RelType1, Direction.OUTGOING);
else if (lastNode.hasRelationship(MyRelations.RelType1, Direction.INCOMING))
return lastNode.getRelationships(MyRelations.RelType2, Direction.OUTGOING);
else if (lastNode.hasRelationship(MyRelations.RelType2, Direction.INCOMING))
return lastNode.getRelationships(MyRelations.RelType3, Direction.OUTGOING);
else if (lastNode.hasRelationship(MyRelations.RelType3, Direction.INCOMING))
return null;
return null;
}
}
However I am not able to think of way which will tell me number of unique paths followed during traversal while using Traversal API (equivalent to above RETURN count(nodes(path))). How can I do this? Is it not possible with traversal API?
PS: By unique path I mean unique permutations of order of nodes visited while traversing. For example, all a-b-c, a-c-b and a-b-c-d are unique.
This query not return count of unique path, but count of all paths return by query:
MATCH path=(a:NodeType1 {prop1:"value1"})-[:RelType1]->(b)
-[:RelType2]->(c)-[:RelType3]->(d)
RETURN count(nodes(path))
If you want to count the number of unique nodes in the query, you can do so:
MATCH path=(a:NodeType1 {prop1:"value1"})-[:RelType1]->(b)
-[:RelType2]->(c)-[:RelType3]->(d)
UNWIND nodes(path) AS N
RETURN count(distinct N)
One way to count the number of unique paths will be (for each path calculates its unique fingerprint, it will not be difficult to repeat by traversal API):
MATCH path=(a:NodeType1 {prop1:"value1"})-[:RelType1]->(b)
-[:RelType2]->(c)-[:RelType3]->(d)
WITH path,
REDUCE(acc="", x in nodes(path) | acc + id(x)) +
REDUCE(acc="", x in rels(path) | acc + id(x)) as uniID
RETURN count(distinct uniID)

How to match nodes with exactly matching properties in Cypher?

In Cypher is there a way to match nodes exactly, i.e. match where the existing node contains only the properties queried for?
For example if we have the following nodes:
CREATE (n {field1: 'value1'})
CREATE (n {field1: 'value1', field2: 'value2'})
And match:
MATCH (n {field1: 'value1'}) RETURN (n)
This will return the both nodes. However, I want the second node to be excluded because it also contains field2 which is not in the set of queried properties
One way to do this is to use the properties function, i.e.
MATCH (n) WHERE properties(n) = {field1: 'value1'} RETURN (n)
But would be good to know whether there are other/better ways

Neo4J: find a sub-graph of arbitrary depth with nodes connected by a given set of relations?

How to build a Neo4J query that:
1) Will return all nodes in a sub-graph of arbitrary depth with nodes connected by a given set of relations?
For example in Cypher-like syntax:
MATCH (*)-[r1:FRIEND_OF AND r2:COLLEAGUE_WITH]->(*) RETURN *
This query will return just the nodes, as you stated in your question:
MATCH (n)-[:FRIEND_OF|COLLEAGUE_WITH*]->(m)
RETURN n, m;
If you also want the relationships:
MATCH (n)-[r:FRIEND_OF|COLLEAGUE_WITH*]->(m)
RETURN n, r, m;

Resources