0

I am trying to write a cypher query which when given a particular start and a fixed terminating node will go ahead and expand the path and extract me the nodes and the relationships in the following format.

(node1)-[relation]->[node2] i.e (subject)-[object]->(predicate) triplet

This is the cypher query which I am trying:

MATCH (e1: Location { name: 'Pune' }), (e2: Location { name: 'Bangalore' })
CALL apoc.path.expandConfig(e1, { terminatorNodes: [e2], limit: 2  }) YIELD path
WITH [relation in relationships(path) | type(relation)] as rel, nodes(path) as nodes
RETURN { Relations: rel, Nodes: nodes } as results

I have tried using List comprehensions for grouping them into the format, but am unable to do this, you can point out any obvious mistakes I'm making.


Update:

I need to multi hop paths with relation between them, is there any chance of using nodes list?

Kunal Mukherjee
  • 5,775
  • 3
  • 25
  • 53

1 Answers1

2

The limit: 2 here means you will get 2 result paths, not that the length of the path is 2. You can use minLevel and maxLevel config properties to constrain how many expansions to perform. For a single expansion (start node, 1 relationship, end node) you can set both of these to 1.

As for formatting this output, the easiest way is to install APOC Procedures and use the apoc.text.format() function (this works as the sprintf() java method).

For example:

MATCH (e1: Location { name: 'Pune' }), (e2: Location { name: 'Bangalore' })
CALL apoc.path.expandConfig(e1, { terminatorNodes: [e2], minLevel: 1, maxLevel:1  }) YIELD path
WITH e1, e2, [relation in relationships(path) | type(relation)][0] as rel
RETURN apoc.text.format('(%s)-[%s]->(%s)',[e1.name, rel, e2.name])

That said, I'm not sure the path expander is needed here. Cypher should be adequate unless there are special circumstances in play:

MATCH (e1: Location { name: 'Pune' }), (e2: Location { name: 'Bangalore' })
MATCH (e1)-[rel]->(e)
where e = e2 // to force a 2-node index lookup and hash join
WITH e1, e2, type(rel) as rel
RETURN apoc.text.format('(%s)-[%s]->(%s)',[e1.name, rel, e2.name])

EDIT

Okay, so you seem to need this to find paths of multiple hops, some limit on the paths, and to see a representation of the paths as sets of triples.

Unfortunately while paths display in this kind of triple format (showing all properties for each element), paths are not lists and we cannot manipulate them as lists.

There is an APOC function (apoc.path.elements()) that will provide a list form of a path with alternating node-relationship-node-relationship elements, but you want to work with triples, so we need to do some manipulation of this list and selecting sublists via indexes to get a list of all triplets in the path. We can then extract the properties we want for the triplets, then apply the string formatting.

This assumes that we are only expanding outgoing relationships (otherwise we'd need to put in some more effort to get the correct direction represented properly across all triplets).

MATCH (e1: Location { name: 'Pune' }), (e2: Location { name: 'Bangalore' })
CALL apoc.path.expandConfig(e1, { terminatorNodes: [e2], relationshipFilter:'>', limit:2}) YIELD path
WITH apoc.path.elements(path) as pathElements
WITH [idx in range(0, size(pathElements) - 1) | CASE WHEN idx % 2 = 0 THEN pathElements[idx].name ELSE type(pathElements[idx]) END] as pathElements
WITH [idx in range(0, size(pathElements) - 2, 2) | pathElements[idx..idx+3]] as triplets
WITH [triplet in triplets | apoc.text.format('(%s)-[%s]->(%s)', triplet)] as tripletsText
RETURN tripletsText
InverseFalcon
  • 29,576
  • 4
  • 38
  • 51
  • Also I want to project the result into the format `(node1)-[relation]->(node2)`, how to do it? – Kunal Mukherjee Jan 25 '19 at 12:22
  • `[relation in relationships(path) | type(relation)][0]` this is being hardcoded to print the first index of the relations list everytime? – Kunal Mukherjee Jan 25 '19 at 12:39
  • Yes. You said you wanted triplets only, so that's only a single hop, so each path will be of length 1 with a single relationship. Or were you looking for a variable-length path and to show all triplets in the path like this? – InverseFalcon Jan 25 '19 at 12:41
  • So without using e1 and e2, using the `nodes` list is it not possible to achieve this? – Kunal Mukherjee Jan 25 '19 at 12:43
  • It's achievable, you would use `nodes(path)[0].name as e1, nodes(path)[1].name as e2`, just seems simpler to reuse your variables since you already know the start and end nodes (assuming still that you are only looking for 1-hop paths) – InverseFalcon Jan 25 '19 at 12:45
  • for multi-hop path, we would need a loop to do achieve it? – Kunal Mukherjee Jan 25 '19 at 12:58
  • So you do need multiple hops. Your wording seemed to suggest you were only looking for triplets, I had thought this meant only a single hop (triplet) per path. – InverseFalcon Jan 25 '19 at 13:04
  • There may be multiple paths to that terminator nodes that is unknown, for every path I want to get the triplet – Kunal Mukherjee Jan 25 '19 at 13:06
  • 1
    I think you need to be very clear here: are you expecting paths consisting only of a single hop (triplet), and you don't know how many of these exist, or are you instead looking for paths of some unknown length, and you want to get the representation of each path broken down into multiple triplets? – InverseFalcon Jan 25 '19 at 13:14
  • while this solves the problem now, but its kinda tricky maintaining it in the long run – Kunal Mukherjee Jan 25 '19 at 14:14
  • 1
    That's fair. This more complex case could probably be encoded into a separate function in APOC (something like `apoc.path.asCypherTriples(path)`, but it would require a way to specify what properties to extract from the nodes in question. – InverseFalcon Jan 25 '19 at 14:17