0

I started to learn Neo4j a few days ago.

I'm using it to find best path and make some analyzes.

The logic is a Person (id, name) can go to a Restaurant (id, name) via some Street (id, name). The connection between them have a cost. PS: All streets have a connection between them. For example:

(Person {id: 1})-[CONNECTION {cost:10}]->(Street {id: 1})
(Person {id: 1})-[CONNECTION {cost:11}]->(Street {id: 2})
(Street {id: 1})-[CONNECTION {cost:4}]->(Street {id: 2})
(Street {id: 2})-[CONNECTION {cost:11}]->(Restaurant {id: 1})
(Street {id: 2})-[CONNECTION {cost:7}]->(Restaurant {id: 2})

...

I am using Dijkstra to find all best path to all Restaurant for a specific Person. But the problem is that I can't set the maximum depth, and I would like to limit a maximum of 3 streets. How could I do that?

CALL gds.graph.project(
  'Person-Street-Restaurant',    
  ['Person', 'Street' 'Restaurant'],   
  'CONNECTION',
  {
    relationshipProperties: 'cost'
  }
)

MATCH (source:Person{id:1})
CALL gds.allShortestPaths.dijkstra.stream('Person-Street-Restaurant', {
    sourceNode: source,
    relationshipWeightProperty: 'cost'
})
YIELD sourceNode, targetNode, totalCost, nodeIds
WHERE 'Restaurant' IN LABELS(gds.util.asNode(targetNode))
RETURN
    gds.util.asNode(sourceNode).name AS sourceNodeName,
    gds.util.asNode(targetNode).name AS targetNodeName,
    totalCost,
    [nodeId IN nodeIds | gds.util.asNode(nodeId).name] AS nodeNames,
    SIZE(nodeIds) AS hops
ORDER BY totalCost

Image example

The best path is: P->S#3->S#4->S#5->R, total cost 7. But if I limit to 2 streets, it should be P->S#1->S#2->R, total cost 10. PS: In this example is easy because we have fewer connections, but in real case we have a lot and all streets have connection between them.

Goge Pow
  • 11
  • 3

2 Answers2

0

Simple:

If your graph library has a visitor

  • visitor checks for distance from source and stops searching if too far.
ravenspoint
  • 19,093
  • 6
  • 57
  • 103
  • 1
    I am using Graph Data Science library, from Neo4j as well. I tried to find all paths from source to destination as you mentioned, but it's taking too long when max nodes is 3 or more. – Goge Pow Jun 18 '22 at 19:03
  • For just three nodes the result should be reached instantaneously! There must be something wrong with your code. Suggest: ask a new question about this, including posting your code. and specifying what exactly you mean by "too long". To improve your chances of getting a good answer, read https://stackoverflow.com/help/how-to-ask – ravenspoint Jun 20 '22 at 15:25
0

If you are open to using an alternative to Neo4J, InfiniteGraph has a much better way of performing weighted graph queries. InfiniteGraph provides a weight calculator operator that you define and then use in your query. You can constrain your query based on normal node and edge predicates and you can set the maximum length of the path.

Given your graph, I would define the schema as follows:

UPDATE SCHEMA {
    CREATE CLASS Person {
        name : String,
        id   : Integer,
        location: REFERENCE { REFERENCED Intersection }
                  
    },
    CREATE CLASS Intersection {
        streets : List { REFERENCE { REFERENCED Street }}
    },
    CREATE CLASS Street {
        cost :  Integer,
        intersections: List { REFERENCE { REFERENCED Intersection }},
        business: List { REFERENCE { REFERENCED Business }}
    },
    CREATE CLASS Business {
        name :  String
    }
}

You can define the WEIGHT CALCULATOR as follows:

CREATE WEIGHT CALCULATOR routeCost {
    minimum: 0,
    default: 0,
    edges: {
        ()-[s:Street]->()   : s.cost
    }
};

Next you can write your query to find the lowest cost route between two nodes:

MATCH m = LIGHTEST routeCost (
            (:Person {id == 1})-[1..20]->(:Restaurant {name == "H"})
          )
          RETURN m;

If you want to find only paths that have a total weight less than some value, you can add the MAX WEIGHT clause:

MATCH m = LIGHTEST routeCost (
            MAX WEIGHT 13.0 routeCost (
              (:Person {id == 1})-[1..20]->(:Restaurant {name == "H"})
            )
          )
          RETURN m;

In your case, you would simply modify the [1..20] to be [1..3] to limit the number streets that you want to allow.

Also, since you have encoded the cost on the edge, your edge weight function in the weight calculator would simple return s.cost with no multiplier.

InfiniteGraph supports very deep path queries out 20, 30, or more degrees and it is very fast.

djhallx
  • 690
  • 6
  • 17