Zach
Zach

Reputation: 113

Cypher Query - Collect two separate types of nodes connected to a third type of node

First, here is what I desire and I then will explain what I have tried.

In this simplified example I have a graph with nodes A, B, C, and D. I have relationships like A->B, B->C and B->D only. I would like to aggregate each of the connections to a node A into a list and subsequently each of the connections to node B into two separate lists: one for B->C connections and one for B->D connections. Something like the below.

{
 'A': 'A',
 'Bs': [{
   'B': 'B',
   'Cs': [
     ...
   ]
   'Ds': [
     ...
   ]
 }, ...]
}

Now for my specific use case. I have trip nodes that are connected to waypoints. Waypoints are connected to airports and what I call a POI. I would like to return a trip with a list of its waypoints where each waypoint has two lists: a list of airports and a list of POI. I have not been able to accomplish this successfully and have not found any examples with this exact use case. In addition to the query below, I have tried many other variations with foreach loops and other constructs. I think this is the closest I have come. In the below query, B->C would equate to the HAS_AIRPORTS relationship and B->D to the HAS_POI relationship. I think my problem comes down to there being multiple aggregations that need to be made on the same level. I am getting some strange output where things seem to be repeating themselves because of the 2 aggregations. Sorry if this is not clear, I am new to the terminology of the cypher query language. Thank you, I am appreciative of any help.

            MATCH (t:Trip {uid: $tripId})
            WITH t
            OPTIONAL MATCH (t)-[:HAS_WAYPOINTS]-(w {active: true})
            WITH t, w
            OPTIONAL MATCH (w)-[wa:HAS_AIRPORT]-(a:NeoAirport)
            WITH t, w, collect(a{.*, active:wa.active}) as airports
            WITH t, w {.*, airports:airports } as waypoints
            OPTIONAL MATCH (w)-[wp:HAS_POI {active: true}]-(p:PointOfInterest)
            WITH t, waypoints, w, collect({source:wp.source, uid:p.uid, placeId:p.placeId}) as pois
            WITH t, apoc.map.setKey( waypoints, 'pointsOfInterest', pois )
            WITH t, t {.*, waypoints: collect(waypoints) } as trips
            OPTIONAL MATCH (t)-[tsa:HAS_START_AIRPORTS]-(sa)
            WITH t, collect(sa) as sa, trips
            WITH t, apoc.map.setKey( trips, 'startAirports', sa ) as trips
            OPTIONAL MATCH (t)-[tea:HAS_START_AIRPORTS]-(ea)
            WITH t, collect(ea) as ea, trips
            WITH t, apoc.map.setKey( trips, 'endAirports', ea ) as trips
            RETURN trips

Upvotes: 0

Views: 949

Answers (1)

InverseFalcon
InverseFalcon

Reputation: 30397

One thing that will help is that aggregations are not the only way to get the result from matched patterns into a list. You can use pattern comprehensions instead, which may simplify your query.

Also I'm going to assume that there is a :HAS_END_AIRPORTS relationship to end airports of a trip, and that we should use that to get end airports.

MATCH (t:Trip {uid: $tripId})
OPTIONAL MATCH (t)-[:HAS_WAYPOINTS]-(w {active: true})
WITH t, w, 
 [(w)-[wa:HAS_AIRPORT]-(a:NeoAirport) | a {.*, active:wa.active}] as airports, 
 [(w)-[wp:HAS_POI {active: true}]-(p:PointOfInterest) | p {source:wp.source, .uid, .placeId}] as pointsOfInterest
WITH t, collect(w {.*, airports, pointsOfInterest}) as waypoints
WITH t, waypoints, 
 [(t)-[:HAS_START_AIRPORTS]-(sa) | sa] as startAirports,
 [(t)-[:HAS_END_AIRPORTS]-(ea) | ea] as endAirports
RETURN t {.*, waypoints, startAirports, endAirports} as trips

Upvotes: 1

Related Questions