You can use APOC path expander procs for this. By default it does a bfs expansion, and if you provide a limit
and supply the labels of nodes you want whitelisted in the labelFilter
you can get what you want:
MATCH (start:label1 {id:1234}), (end:label2 {id:5678})
CALL apoc.path.spanningTree(start, {endNodes:[end], limit:1, labelFilter:'label1|label2'}) YIELD path
RETURN path
Note that the labels in the labelFilter that you're whitelisting must include a label on your end node (it will not apply the filter to the start node by default, so the start node's label doesn't need to be in there.
That said, if the end node has a label that you don't want whitelisted for traversal, you can use an end label filter in the labelFilter
, which is the label prefixed with >
.
So if both the start and end node were :Node labeled, but you only wanted the middle nodes in the traversal to be :label1 or :label2 nodes, then you could use this:
MATCH (start:Node {id:1234}), (end:Node {id:5678})
CALL apoc.path.spanningTree(start, {endNodes:[end], limit:1, labelFilter:'label1|label2|>Node'}) YIELD path
RETURN path
As far as the "why spanningTree()?", it's a bit oddly named, but it uses NODE_GLOBAL
uniqueness during traversal, meaning a node will only ever be visited once, so there will ever only be a single path to each node (no cycles), so the collection of returned paths (if you don't have an end node, or any restriction on end nodes) takes the form of a spanning tree. The point is that with bfs expansion and NODE_GLOBAL uniqueness and the label filter (and supplying your end node) you get a shortest path using only the labels you want during the traversal.