All Shortest Paths in AQL

Find all paths of shortest length between a start and target vertex

General query idea

This type of query finds all paths of shortest length between two given documents (startVertex and targetVertex) in your graph.

Every returned path is a JSON object with two attributes:

  • An array containing the vertices on the path.
  • An array containing the edges on the path.

Example

A visual representation of the example graph:

Train Connection Map

Each ellipse stands for a train station with the name of the city written inside of it. They are the vertices of the graph. Arrows represent train connections between cities and are the edges of the graph.

Assuming that you want to go from Carlisle to London by train, the expected two shortest paths are:

  1. Carlisle – Birmingham – London
  2. Carlisle – York – London

Another path that connects Carlisle and London is Carlisle – Glasgow – Edinburgh – York – London, but it is has two more stops and is therefore not a path of the shortest length.

Syntax

The syntax for All Shortest Paths queries is similar to the one for Shortest Path and there are also two options to either use a named graph or a set of edge collections. It only emits a path variable however, whereas SHORTEST_PATH emits a vertex and an edge variable.

Working with named graphs

FOR path
  IN OUTBOUND|INBOUND|ANY ALL_SHORTEST_PATHS
  startVertex TO targetVertex
  GRAPH graphName
  • FOR: emits the variable path which contains one shortest path as an object, with the vertices and edges of the path.
  • IN OUTBOUND|INBOUND|ANY: defines in which direction edges are followed (outgoing, incoming, or both)
  • ALL_SHORTEST_PATHS: the keyword to compute All Shortest Paths
  • startVertex TO targetVertex (both string|object): the two vertices between which the paths will be computed. This can be specified in the form of a ID string or in the form of a document with the attribute _id. All other values result in a warning and an empty result. If one of the specified documents does not exist, the result is empty as well and there is no warning.
  • GRAPH graphName (string): the name identifying the named graph. Its vertex and edge collections will be looked up.

All Shortest Paths traversals do not support edge weights.

Working with collection sets

FOR path
  IN OUTBOUND|INBOUND|ANY ALL_SHORTEST_PATHS
  startVertex TO targetVertex
  edgeCollection1, ..., edgeCollectionN

Instead of GRAPH graphName you can specify a list of edge collections. The involved vertex collections are determined by the edges of the given edge collections.

Traversing in mixed directions

For All Shortest Paths with a list of edge collections, you can optionally specify the direction for some of the edge collections. Say, for example, you have three edge collections edges1, edges2 and edges3, where in edges2 the direction has no relevance, but in edges1 and edges3 the direction should be taken into account. In this case you can use OUTBOUND as a general search direction and ANY specifically for edges2 as follows:

FOR path IN OUTBOUND ALL_SHORTEST_PATHS
  startVertex TO targetVertex
  edges1, ANY edges2, edges3

All collections in the list that do not specify their own direction will use the direction defined after IN (here: OUTBOUND). This allows using a different direction for each collection in your path search.

Examples

Load an example graph to get a named graph that reflects some possible train connections in Europe and North America:

Train Connection Map

arangosh> var examples = require("@arangodb/graph-examples/example-graph.js");
arangosh> var graph = examples.loadGraph("kShortestPathsGraph");
arangosh> db.places.toArray();
arangosh> db.connections.toArray();
Show execution results
Hide execution results
[ 
  { 
    "_key" : "Inverness", 
    "_id" : "places/Inverness", 
    "_rev" : "_fyvMSI6---", 
    "label" : "Inverness" 
  }, 
  { 
    "_key" : "Aberdeen", 
    "_id" : "places/Aberdeen", 
    "_rev" : "_fyvMSJ----", 
    "label" : "Aberdeen" 
  }, 
  { 
    "_key" : "Leuchars", 
    "_id" : "places/Leuchars", 
    "_rev" : "_fyvMSJ---_", 
    "label" : "Leuchars" 
  }, 
  { 
    "_key" : "StAndrews", 
    "_id" : "places/StAndrews", 
    "_rev" : "_fyvMSJ---A", 
    "label" : "StAndrews" 
  }, 
  { 
    "_key" : "Edinburgh", 
    "_id" : "places/Edinburgh", 
    "_rev" : "_fyvMSJ---B", 
    "label" : "Edinburgh" 
  }, 
  { 
    "_key" : "Glasgow", 
    "_id" : "places/Glasgow", 
    "_rev" : "_fyvMSJ---C", 
    "label" : "Glasgow" 
  }, 
  { 
    "_key" : "York", 
    "_id" : "places/York", 
    "_rev" : "_fyvMSJC---", 
    "label" : "York" 
  }, 
  { 
    "_key" : "Carlisle", 
    "_id" : "places/Carlisle", 
    "_rev" : "_fyvMSJC--_", 
    "label" : "Carlisle" 
  }, 
  { 
    "_key" : "Birmingham", 
    "_id" : "places/Birmingham", 
    "_rev" : "_fyvMSJC--A", 
    "label" : "Birmingham" 
  }, 
  { 
    "_key" : "London", 
    "_id" : "places/London", 
    "_rev" : "_fyvMSJC--B", 
    "label" : "London" 
  }, 
  { 
    "_key" : "Brussels", 
    "_id" : "places/Brussels", 
    "_rev" : "_fyvMSJG---", 
    "label" : "Brussels" 
  }, 
  { 
    "_key" : "Cologne", 
    "_id" : "places/Cologne", 
    "_rev" : "_fyvMSJG--_", 
    "label" : "Cologne" 
  }, 
  { 
    "_key" : "Toronto", 
    "_id" : "places/Toronto", 
    "_rev" : "_fyvMSJG--A", 
    "label" : "Toronto" 
  }, 
  { 
    "_key" : "Winnipeg", 
    "_id" : "places/Winnipeg", 
    "_rev" : "_fyvMSJG--B", 
    "label" : "Winnipeg" 
  }, 
  { 
    "_key" : "Saskatoon", 
    "_id" : "places/Saskatoon", 
    "_rev" : "_fyvMSJK---", 
    "label" : "Saskatoon" 
  }, 
  { 
    "_key" : "Edmonton", 
    "_id" : "places/Edmonton", 
    "_rev" : "_fyvMSJK--_", 
    "label" : "Edmonton" 
  }, 
  { 
    "_key" : "Jasper", 
    "_id" : "places/Jasper", 
    "_rev" : "_fyvMSJK--A", 
    "label" : "Jasper" 
  }, 
  { 
    "_key" : "Vancouver", 
    "_id" : "places/Vancouver", 
    "_rev" : "_fyvMSJK--B", 
    "label" : "Vancouver" 
  } 
]
[ 
  { 
    "_key" : "62100", 
    "_id" : "connections/62100", 
    "_from" : "places/Inverness", 
    "_to" : "places/Aberdeen", 
    "_rev" : "_fyvMSJK--C", 
    "travelTime" : 3 
  }, 
  { 
    "_key" : "62102", 
    "_id" : "connections/62102", 
    "_from" : "places/Aberdeen", 
    "_to" : "places/Inverness", 
    "_rev" : "_fyvMSJO---", 
    "travelTime" : 2.5 
  }, 
  { 
    "_key" : "62104", 
    "_id" : "connections/62104", 
    "_from" : "places/Aberdeen", 
    "_to" : "places/Leuchars", 
    "_rev" : "_fyvMSJO--_", 
    "travelTime" : 1.5 
  }, 
  { 
    "_key" : "62106", 
    "_id" : "connections/62106", 
    "_from" : "places/Leuchars", 
    "_to" : "places/Aberdeen", 
    "_rev" : "_fyvMSJO--A", 
    "travelTime" : 1 
  }, 
  { 
    "_key" : "62108", 
    "_id" : "connections/62108", 
    "_from" : "places/Leuchars", 
    "_to" : "places/Edinburgh", 
    "_rev" : "_fyvMSJO--B", 
    "travelTime" : 1.5 
  }, 
  { 
    "_key" : "62110", 
    "_id" : "connections/62110", 
    "_from" : "places/Edinburgh", 
    "_to" : "places/Leuchars", 
    "_rev" : "_fyvMSJS---", 
    "travelTime" : 3 
  }, 
  { 
    "_key" : "62112", 
    "_id" : "connections/62112", 
    "_from" : "places/Edinburgh", 
    "_to" : "places/Glasgow", 
    "_rev" : "_fyvMSJS--_", 
    "travelTime" : 1 
  }, 
  { 
    "_key" : "62114", 
    "_id" : "connections/62114", 
    "_from" : "places/Glasgow", 
    "_to" : "places/Edinburgh", 
    "_rev" : "_fyvMSJS--A", 
    "travelTime" : 1 
  }, 
  { 
    "_key" : "62116", 
    "_id" : "connections/62116", 
    "_from" : "places/Edinburgh", 
    "_to" : "places/York", 
    "_rev" : "_fyvMSJW---", 
    "travelTime" : 3.5 
  }, 
  { 
    "_key" : "62118", 
    "_id" : "connections/62118", 
    "_from" : "places/York", 
    "_to" : "places/Edinburgh", 
    "_rev" : "_fyvMSJW--_", 
    "travelTime" : 4 
  }, 
  { 
    "_key" : "62120", 
    "_id" : "connections/62120", 
    "_from" : "places/Glasgow", 
    "_to" : "places/Carlisle", 
    "_rev" : "_fyvMSJW--A", 
    "travelTime" : 1 
  }, 
  { 
    "_key" : "62122", 
    "_id" : "connections/62122", 
    "_from" : "places/Carlisle", 
    "_to" : "places/Glasgow", 
    "_rev" : "_fyvMSJW--B", 
    "travelTime" : 1 
  }, 
  { 
    "_key" : "62124", 
    "_id" : "connections/62124", 
    "_from" : "places/Carlisle", 
    "_to" : "places/York", 
    "_rev" : "_fyvMSJa---", 
    "travelTime" : 2.5 
  }, 
  { 
    "_key" : "62126", 
    "_id" : "connections/62126", 
    "_from" : "places/York", 
    "_to" : "places/Carlisle", 
    "_rev" : "_fyvMSJa--_", 
    "travelTime" : 3.5 
  }, 
  { 
    "_key" : "62128", 
    "_id" : "connections/62128", 
    "_from" : "places/Carlisle", 
    "_to" : "places/Birmingham", 
    "_rev" : "_fyvMSJa--A", 
    "travelTime" : 2 
  }, 
  { 
    "_key" : "62130", 
    "_id" : "connections/62130", 
    "_from" : "places/Birmingham", 
    "_to" : "places/Carlisle", 
    "_rev" : "_fyvMSJa--B", 
    "travelTime" : 1 
  }, 
  { 
    "_key" : "62132", 
    "_id" : "connections/62132", 
    "_from" : "places/Birmingham", 
    "_to" : "places/London", 
    "_rev" : "_fyvMSJe---", 
    "travelTime" : 1.5 
  }, 
  { 
    "_key" : "62134", 
    "_id" : "connections/62134", 
    "_from" : "places/London", 
    "_to" : "places/Birmingham", 
    "_rev" : "_fyvMSJe--_", 
    "travelTime" : 2.5 
  }, 
  { 
    "_key" : "62136", 
    "_id" : "connections/62136", 
    "_from" : "places/Leuchars", 
    "_to" : "places/StAndrews", 
    "_rev" : "_fyvMSJe--A", 
    "travelTime" : 0.2 
  }, 
  { 
    "_key" : "62138", 
    "_id" : "connections/62138", 
    "_from" : "places/StAndrews", 
    "_to" : "places/Leuchars", 
    "_rev" : "_fyvMSJe--B", 
    "travelTime" : 0.2 
  }, 
  { 
    "_key" : "62140", 
    "_id" : "connections/62140", 
    "_from" : "places/York", 
    "_to" : "places/London", 
    "_rev" : "_fyvMSJi---", 
    "travelTime" : 1.8 
  }, 
  { 
    "_key" : "62142", 
    "_id" : "connections/62142", 
    "_from" : "places/London", 
    "_to" : "places/York", 
    "_rev" : "_fyvMSJi--_", 
    "travelTime" : 2 
  }, 
  { 
    "_key" : "62144", 
    "_id" : "connections/62144", 
    "_from" : "places/London", 
    "_to" : "places/Brussels", 
    "_rev" : "_fyvMSJi--A", 
    "travelTime" : 2.5 
  }, 
  { 
    "_key" : "62146", 
    "_id" : "connections/62146", 
    "_from" : "places/Brussels", 
    "_to" : "places/London", 
    "_rev" : "_fyvMSJi--B", 
    "travelTime" : 3.5 
  }, 
  { 
    "_key" : "62148", 
    "_id" : "connections/62148", 
    "_from" : "places/Brussels", 
    "_to" : "places/Cologne", 
    "_rev" : "_fyvMSJm---", 
    "travelTime" : 2 
  }, 
  { 
    "_key" : "62150", 
    "_id" : "connections/62150", 
    "_from" : "places/Cologne", 
    "_to" : "places/Brussels", 
    "_rev" : "_fyvMSJm--_", 
    "travelTime" : 1.5 
  }, 
  { 
    "_key" : "62152", 
    "_id" : "connections/62152", 
    "_from" : "places/Toronto", 
    "_to" : "places/Winnipeg", 
    "_rev" : "_fyvMSJm--A", 
    "travelTime" : 36 
  }, 
  { 
    "_key" : "62154", 
    "_id" : "connections/62154", 
    "_from" : "places/Winnipeg", 
    "_to" : "places/Toronto", 
    "_rev" : "_fyvMSJm--B", 
    "travelTime" : 35 
  }, 
  { 
    "_key" : "62156", 
    "_id" : "connections/62156", 
    "_from" : "places/Winnipeg", 
    "_to" : "places/Saskatoon", 
    "_rev" : "_fyvMSJq---", 
    "travelTime" : 12 
  }, 
  { 
    "_key" : "62158", 
    "_id" : "connections/62158", 
    "_from" : "places/Saskatoon", 
    "_to" : "places/Winnipeg", 
    "_rev" : "_fyvMSJq--_", 
    "travelTime" : 5 
  }, 
  { 
    "_key" : "62160", 
    "_id" : "connections/62160", 
    "_from" : "places/Saskatoon", 
    "_to" : "places/Edmonton", 
    "_rev" : "_fyvMSJq--A", 
    "travelTime" : 12 
  }, 
  { 
    "_key" : "62162", 
    "_id" : "connections/62162", 
    "_from" : "places/Edmonton", 
    "_to" : "places/Saskatoon", 
    "_rev" : "_fyvMSJq--B", 
    "travelTime" : 17 
  }, 
  { 
    "_key" : "62164", 
    "_id" : "connections/62164", 
    "_from" : "places/Edmonton", 
    "_to" : "places/Jasper", 
    "_rev" : "_fyvMSJu---", 
    "travelTime" : 6 
  }, 
  { 
    "_key" : "62166", 
    "_id" : "connections/62166", 
    "_from" : "places/Jasper", 
    "_to" : "places/Edmonton", 
    "_rev" : "_fyvMSJu--_", 
    "travelTime" : 5 
  }, 
  { 
    "_key" : "62168", 
    "_id" : "connections/62168", 
    "_from" : "places/Jasper", 
    "_to" : "places/Vancouver", 
    "_rev" : "_fyvMSJu--A", 
    "travelTime" : 12 
  }, 
  { 
    "_key" : "62170", 
    "_id" : "connections/62170", 
    "_from" : "places/Vancouver", 
    "_to" : "places/Jasper", 
    "_rev" : "_fyvMSJu--B", 
    "travelTime" : 13 
  } 
]

Suppose you want to query a route from Carlisle to London, and compare the outputs of SHORTEST_PATH, K_SHORTEST_PATHS and ALL_SHORTEST_PATHS. Note that SHORTEST_PATH returns any of the shortest paths, whereas ALL_SHORTEST_PATHS returns all of them. K_SHORTEST_PATHS returns the shortest paths first but continues with longer paths, until it found all routes or reaches the defined limit (the number of paths).

Using SHORTEST_PATH to get one shortest path:

FOR v, e IN OUTBOUND SHORTEST_PATH 'places/Carlisle' TO 'places/London'
  GRAPH 'kShortestPathsGraph'
    RETURN { place: v.label }
Show query results
Hide query results
[
  {
    "place": "Carlisle"
  },
  {
    "place": "York"
  },
  {
    "place": "London"
  }
]

Using ALL_SHORTEST_PATHS to get both shortest paths:

FOR p IN OUTBOUND ALL_SHORTEST_PATHS 'places/Carlisle' TO 'places/London'
  GRAPH 'kShortestPathsGraph'
    RETURN { places: p.vertices[*].label }
Show query results
Hide query results
[
  {
    "places": [
      "Carlisle",
      "York",
      "London"
    ]
  },
  {
    "places": [
      "Carlisle",
      "Birmingham",
      "London"
    ]
  }
]

Using K_SHORTEST_PATHS without a limit to get all paths in order of increasing length:

FOR p IN OUTBOUND K_SHORTEST_PATHS 'places/Carlisle' TO 'places/London'
  GRAPH 'kShortestPathsGraph'
    RETURN { places: p.vertices[*].label }
Show query results
Hide query results
[
  {
    "places": [
      "Carlisle",
      "Birmingham",
      "London"
    ]
  },
  {
    "places": [
      "Carlisle",
      "York",
      "London"
    ]
  },
  {
    "places": [
      "Carlisle",
      "Glasgow",
      "Edinburgh",
      "York",
      "London"
    ]
  }
]

If you ask for routes that don’t exist, you get an empty result (from Carlisle to Toronto):

FOR p IN OUTBOUND ALL_SHORTEST_PATHS 'places/Carlisle' TO 'places/Toronto'
  GRAPH 'kShortestPathsGraph'
    RETURN {
      places: p.vertices[*].label
    }
Show query results
Hide query results
[]

And finally clean up by removing the named graph:

arangosh> var examples = require("@arangodb/graph-examples/example-graph.js");
arangosh> examples.dropGraph("kShortestPathsGraph");
Show execution results
Hide execution results