Search code examples
neo4jcypherneo4j-apoc

apoc.path.expandConfig() extract (subject)-[object]->(predicate) triplet


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?


Solution

  • 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