How can I dynamically modify a returned path?

28 Views Asked by At

I am using FalkorDb 4.0.5 which is a fork of the now discontinued Redis Graph.

My simplified test case:

GRAPH.QUERY g 

// Create 4 nodes:
"CREATE (w:Work{name:'Work'}), (a:City{name:'A'}), (b:City{name:'B'}), (c:City{name:'C'}),

// Create "Road" edges, each with an `x` property: 
(w)-[r0:Road{x:0}]->(a), (a)-[r1:Road{x:1}]->(b), (b)-[r2:Road{x:2}]->(c), 

// Add "Accom" edges, also with an `x` property:
(a)-[a1:Accom{x:10}]->(h1:Hotel{name:'Hilton'}), 
(a)-[a2:Accom{x:11}]->(h2:Hotel{name:'Marriot'}), 
(c)-[a3:Accom{x:12}]->(h3:Hotel{name:'Marriot'}) 

RETURN w,a,b,c,r0,r1,r2,a1,h1,a2,h2,a3,h3"

This results in a graph that looks like this with the x values on each edge shown: enter image description here

So, the x in the path looks like:

(Work)-[x:0]-(A)-[x:1]-(B)-[x:2]-(C)

I am trying to work out how to write a query so I get 2 paths (because City A has 2 Accom edges: Hilton and Marriot) that looks like the following schematic.

for Hilton:  (Work)-[x:10]-(A)-[x:1]-(B)-[x:2 ]-(C)
for Marriot: (Work)-[x:11]-(A)-[x:1]-(B)-[x:12]-(C)

{repeat for other Hotels found attached to A ...}

i.e: Make a path from Work to C for each Hotel found attached to A (Hilton and Marriot in this case, but could be many others). Each path needs to be updated to reflect the x found on the Accom edge where that same Hotel is attached to the Town.

This is the query for the path for just the Road edges:

GRAPH.QUERY g "MATCH path=(p)-[r:Road*]->(c) RETURN path"

which yields the (Work)-[x:0]-(A)-[x:1]-(B)-[x:2]-(C).

But what query to get the two paths listed above?

Edit: Maybe I just need to do the following to get all the data then work it out in my application (Java / Quarkus)

GRAPH.QUERY g "MATCH path=(p)-[r]->(c) RETURN path"

Yields:
1) 1) "[(0), [0], (1)]"
2) 1) "[(1), [1], (2)]"
3) 1) "[(1), [3], (4)]"
4) 1) "[(1), [4], (5)]"
5) 1) "[(2), [2], (3)]"
6) 1) "[(3), [5], (6)]"

Thanks!

2

There are 2 best solutions below

0
SWilly22 On

What about:

MATCH p = (w:Work)-[:Road]->(A:City)-[:Accom]->(h:Hotel)<-[:Accom]-(A)-[:Road*]->(c:City{name:'C'}) RETURN p

Although it might be a bit too specific, as it requires the Hotel node to be directly connected to the first City reachable from Work.

0
Murrah On

@SWilly22: Thanks for your suggestion and thanks for your work on FalkorDB. :-) I learned a few things from your suggestion.

As I was working through this I realised that the outcome I was trying to achieve was not what I needed in my app. So, the original question about dynamically creating the paths as described in the question has been replaced by a different desired outcome.

What I needed was a record for each Hotel attached to City A returning the x properties from the Accom relationships where a City had the Hotel in question, with the list of cities in the same order as in the graph.

My desired output now looks like:

  1) 1) "Marriot"
     2) "[{city: A, x: 11}, {city: C, x: 12}]"
  2) 1) "Hilton"
     2) "[{city: A, x: 10}]"

The query follows. There is probably a better way to do this but it works! And yes, in the final version I will use parameter substitution for the start and end Cities :-)

GRAPH.QUERY g "
MATCH (a:City{name:'A'})-[:Accom]->(h:Hotel) WITH h.name AS hotelName 
MATCH path=(w:Work)-[:Road*]->(c:City{name:'C'}) 
UNWIND relationships(path) as uwpath 
UNWIND startNode(uwpath) as n 
CALL { WITH n, hotelName 
   MATCH (c:City)-[r:Accom]->(h:Hotel{name:hotelName}) RETURN c.name as city, r.x as x 
} 
WITH hotelName, collect( DISTINCT {city: city, x: x}) as records 
RETURN hotelName, records"

Here is the same query with comments:

GRAPH.QUERY g "

// Get the Hotel names attached to City A
MATCH (a:City{name:'A'})-[:Accom]->(h:Hotel) WITH h.name AS hotelName
 
// Get the path from Work to City C using the Road relationship
MATCH path=(w:Work)-[:Road*]->(c:City{name:'C'}) 

// Unwind the path into path records
UNWIND relationships(path) as uwpath 

// Unwind the path record to get the startNode as n
UNWIND startNode(uwpath) as n 

// Make a sub query call passing in the node and the hotelName
CALL { WITH n, hotelName 

  // See if this City has that Hotel and return the City name and the Accom.x property
  MATCH (c:City)-[r:Accom]->(h:Hotel{name:hotelName}) RETURN c.name as city, r.x as x 
} 

// Collect the result of the sub query into records of City and x
// Use DISTINCT to avoid duplicates as a result of the iteration of the CALL
WITH hotelName, collect( DISTINCT {city: city, x: x}) as records 

// Return the hotel name and the Cities and x having that Hotel
RETURN hotelName, records"

I am not sure if there is a better way to avoid needing DISTINCT, or even if there is a better way to achieve this outcome, but for now this works.

Thanks, Murray