Using APOC to match variable length of relationships in between

For example, if I want to match a pattern like this:
MATCH (:Person)-[:A]->(:Movie)-[:B*]->(:Movie)-[:C]->(:Person)
where the first and last relationship's length ([:A] and [:C]) is fixed (they are both with length 1), but the middle relationship's length ([:B]) is variable.


Then the following paths will both match this pattern:
(:Person)-[:A]->(:Movie)-[:B]->(:Movie)-[:C]->(:Person)
(:Person)-[:A]->(:Movie)-[:B]->()-[:B]->(:Movie)-[:C]->(:Person)


I'm wondering if it's possible to use only one APOC path expander procedure (say apoc.path.expandConfig) and use the sequence to achieve this? (It seems that APOC does not support variable length of relationships in between though)


I know that I could create a new relationship between these two (:Movie) nodes based on this post
but my goal is to return the whole path all at once, including the relationship and nodes between these two (:Movie) nodes.


Thank you so much for all your help!

You can use cypher to get all the nodes and relationships along the path for any length. Does this not meet your needs?

match p = (:Person)-[:A]->(:Movie)-[:B*]->(:Movie)-[:C]->(:Person)
return nodes(p) as nodes, relationships(p) as relationships

If you only want the Movie nodes and B relationships, you can use list filter as follows:

match p = (:Person)-[:A]->(:Movie)-[:B*]->(:Movie)-[:C]->(:Person)
return [i in nodes(p) where not i:Person] as nodes, [r in relationships(p) where r:B] as relationships

Thank you so much for your reply!
Yes, it is, but I want to compare the performance between Cypher and APOC (for research purposes).
That's why I'm asking the equivalent way of using APOC for this query.

Got it. Sounds interesting. I don’t have much experience with the APOC library. Hopefully those that do with pitch in.

Hi @echo_xiangchen

This is my data.

CREATE (s:Person {name:'Start'})-[:A]->(:Movie {name:'movie11'})-[:B]->(:Movie {name:'movie12'})-[:C]->(c:Person {name:'End'})
CREATE (s)-[:A]->(:Movie {name:'movie21'})-[:B]->(:Movie  {name:'movie22'})-[:B]->(:Movie {name:'movie23'})-[:C]->(c)

Now you can find both routes.
The problem is that A, B, and C are connected by or, so we can also search like A-B-A-B-C.

MATCH (a:Person {name: "Start"})
MATCH (c:Person {name: "End"})
CALL apoc.path.expandConfig(a, {
    relationshipFilter: "A>|B>|C>",
    minLevel: 1,
    maxLevel: 5,
    terminatorNodes: [c]
})
YIELD path
RETURN path, length(path) AS hops
ORDER BY hops;

Hi @koji , thank you for your reply!
Your query also works for the pattern:
(:Person)-[:C]->(:Movie)-[:B]->(:Movie)-[:A]->(c:Person)
But the pattern I want to query is that [:A] has to be the first relationship, and [:C] has to be the last relationship, with one or more [:B] in between.
I'm wondering if APOC can do this?

@echo_xiangchen

How about this query.
This is not a bad Cypher, but it is not a good Cypher either.
An "A" and a "C" must be added to the result to make it a complete path.

MATCH (:Person {name: "Start"})-[:A]->(b:Movie)
MATCH (:Person {name: "End"})<-[:C]-(d:Movie)
CALL apoc.path.expandConfig(b, {
    relationshipFilter: "B>",
    minLevel: 1,
    maxLevel: 5,
    terminatorNodes: [d]
})
YIELD path
RETURN path, length(path) AS hops
ORDER BY hops;

Hi @koji , thanks!
What I'm thinking in the beginning is that maybe I could use one single apoc.path.expandConfig procedure (without using any MATCH clauses)and manipulate the label and relationship filter to get the same result.
But I guess it might be impossible to do that. :face_exhaling:

Hi @echo_xiangchen

It seems impossible at current apoc.path.expandConfig.
I am looking forward to Neo4j 5.0 or/and next apoc.

Hi @koji
Thank you so much for your reply!
I'm also looking forward to their update in Neo4j 5.0.

Hi @echo_xiangchen !

Based on @koji 's data, you can try something like

MATCH (:Person)-[:A]->(s:Movie)
with collect(s) as start
MATCH (:Person)<-[:C]-(e:Movie)
with start, collect(e) as end
CALL apoc.path.expandConfig(start, {
    relationshipFilter: "B>",
    terminatorNodes: end
})
YIELD path
with path, nodes(path) as n
with head(n) as s, last(n) as e, path
MATCH p1 = (:Person)-[:A]->(s)
with apoc.path.combine(p1, path) as path, e
MATCH p2 = (e)-[:C]->(:Person)
with apoc.path.combine(path, p2) as path
RETURN path, length(path) AS hops
ORDER BY hops;

There's no 'simpler' way to do what you want unless you create your own traversal internally. This way relies on the possibility of holding all the movies with this characteristics on ram tho.

Lemme know if works on your data.

Bennu