Search code examples
neo4jcypher

Query for Collapsing Node in Path with Multiple Relationships


In my DB, my connections between item nodes are done through a three-way node-connection, like the one highlighted below. I chose it this way because I don't think there's a way to do a three-way arrow or relationship.

example graph

Anyways, I don't think this modeling is a problem in itself. But I would like to query this graph in a way that I ditch the CONNECTED_BY relationship and collapse the connection path (e.g. the higlighted node in the picture) down to a simple relationship. How do I do it with Cypher in Neo4j?

I know there's the apoc.refactor.collapseNode procedure, but, as far as I know, it actually mutates the database? Is there a way to use solely as a query? Not to mention that, in my case, I would have to omit the other relationships on the collapsed node somehow.

Here's an example of how to reproduce what I have:

// Users

// #1
CREATE (:User{name: "philippe_fanaro"});
CREATE (:User{name: "john_doe"});

// Items

// #1
MATCH  (u:User{name: "john_doe"})
CREATE (u)-[:CREATED]->(:Item{title: "Designing Data-Intensive Applications"});

// #2
MATCH  (u:User{name: "philippe_fanaro"})
CREATE (u)-[:CREATED]->(:Item{title: "System Design Interview – An insider's guide"});

// Connections

// #1
MATCH  (i1:Item), (i2:Item), (u:User{name: "philippe_fanaro"})
WHERE  i1.title CONTAINS "Designing"
  AND  i2.title CONTAINS "System"
CREATE (i1)- [:CONNECTION_ORIGIN]
           ->(c:Connection{title: "Relation"})
           - [:CONNECTION_DESTINATION]->(i2),
       (c)-[:CONNECTED_BY]->(u);

Solution

  • Not sure exactly what you want to query and what you want the result to be. But if you want to do something similar to apoc.refactor.collapseNode() but as a virtual path instead of modifying the graph then maybe something like this could work (this query is based on the nodes and relationships you created in your example above with John Doe and Philippe Fanaro and creates a path from the "Designing"-book to the "System"-book with a virtual relationship called ":CONNECTION")?

    MATCH (n1)-[:CONNECTION_ORIGIN]->(r)-[:CONNECTION_DESTINATION]->(n2)
    
    WITH n1, apoc.create.vRelationship(n1, "CONNECTION", {}, n2) AS rel
    
    RETURN apoc.path.create(n1, [rel])
    

    Or, while adding the connection properties into the collapsed relationship and also returning the users:

    MATCH (n1:Item)
           -[:CONNECTION_ORIGIN]
          ->(r:Connection)
           -[:CONNECTION_DESTINATION]
          ->(n2:Item),
          (r)-[:CONNECTED_BY]-(u:User)
    
    WITH u, 
         n1, 
         apoc.create.vRelationship(
           n1, 
           "CONNECTION", 
           apoc.map.merge(
             properties(r), 
             { connected_by: u.id }
           ), 
           n2
         ) AS rel
    
    RETURN u, apoc.path.create(n1, [rel])
    

    Which yields the following:

    results