k Paths in AQL

General query idea

This type of query finds all paths between two given documents, startVertex and targetVertex in your graph. The paths are restricted by minimum and maximum length of the paths.

Every such path will be returned as a JSON object with two components:

  • an array containing the vertices on the path
  • an array containing the edges on the path

Example

Let us take a look at a simple example to explain how it works. This is the graph that we are going to find some paths on:

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. The numbers near the arrows describe how long it takes to get from one station to another. They are used as edge weights.

Let us assume that we want to go from Aberdeen to London by train.

Here we have a couple of alternatives:

a) Straight way

  1. Aberdeen
  2. Leuchars
  3. Edinburgh
  4. York
  5. London

b) Detour at York

  1. Aberdeen
  2. Leuchars
  3. Edinburgh
  4. York
  5. Carlisle
  6. Birmingham
  7. London

c) Detour at Edinburgh

  1. Aberdeen
  2. Leuchars
  3. Edinburgh
  4. Glasgow
  5. Carlisle
  6. Birmingham
  7. London

d) Detour at Edinburgh to York

  1. Aberdeen
  2. Leuchars
  3. Edinburgh
  4. Glasgow
  5. Carlisle
  6. York
  7. London

Note that we only consider paths as valid that do not contain the same vertex twice. The following alternative would visit Aberdeen twice and will not be returned by k Paths:

  1. Aberdeen
  2. Inverness
  3. Aberdeen
  4. Leuchars
  5. Edinburgh
  6. York
  7. London

Example Use Cases

The use-cases for k Paths are about the same as for unweighted k Shortest Paths. The main difference is that k Shortest Paths will enumerate all paths with increasing length. It will stop as soon as a given limit is reached. k Paths will instead only enumerate all paths within a given range of path length, and are thereby upper-bounded.

The k Paths traversal can be used as foundation for several other algorithms:

  • Transportation of any kind (e.g. road traffic, network package routing)
  • Flow problems: We need to transfer items from A to B, which alternatives do we have? What is their capacity?

Syntax

The syntax for k Paths queries is similar to the one for K Shortest Path with the addition to define the minimum and maximum length of the path.

It is highly recommended that you use a reasonable maximum path length or a LIMIT statement, as k Paths is a potentially expensive operation. On large connected graphs it can return a large number of paths.

Working with named graphs

FOR path
  IN MIN..MAX OUTBOUND|INBOUND|ANY K_PATHS
  startVertex TO targetVertex
  GRAPH graphName
  [OPTIONS options]
  • FOR: emits the variable path which contains one path as an object containing vertices and edges of the path.
  • IN MIN..MAX: the minimal and maximal depth for the traversal:
    • min (number, optional): paths returned by this query will have at least a length of min many edges. If not specified, it defaults to 1. The minimal possible value is 0.
    • max (number, optional): paths returned by this query will have at most a length of max many edges. If omitted, max defaults to min. Thus only the vertices and edges in the range of min are returned. max can not be specified without min.
  • OUTBOUND|INBOUND|ANY: defines in which direction edges are followed (outgoing, incoming, or both)
  • K_PATHS: the keyword to compute all 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 document identifier string or in the form of an object with the attribute _id. All other values will lead to a warning and an empty result. This is also the case if one of the specified documents does not exist.
  • GRAPH graphName (string): the name identifying the named graph. Its vertex and edge collections will be looked up.
  • OPTIONS options (object, optional): used to modify the execution of the search. Right now there are no options that trigger an effect. However, this may change in the future.

Working with collection sets

FOR path
  IN MIN..MAX OUTBOUND|INBOUND|ANY K_PATHS
  startVertex TO targetVertex
  edgeCollection1, ..., edgeCollectionN
  [OPTIONS options]

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 k 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 general search direction and ANY specifically for edges2 as follows:

FOR vertex IN OUTBOUND K_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 to use a different direction for each collection in your path search.

Examples

We 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" : "_eLYV6WC---", 
    "label" : "Inverness" 
  }, 
  { 
    "_key" : "Aberdeen", 
    "_id" : "places/Aberdeen", 
    "_rev" : "_eLYV6WC--_", 
    "label" : "Aberdeen" 
  }, 
  { 
    "_key" : "Leuchars", 
    "_id" : "places/Leuchars", 
    "_rev" : "_eLYV6WC--A", 
    "label" : "Leuchars" 
  }, 
  { 
    "_key" : "StAndrews", 
    "_id" : "places/StAndrews", 
    "_rev" : "_eLYV6WC--B", 
    "label" : "StAndrews" 
  }, 
  { 
    "_key" : "Edinburgh", 
    "_id" : "places/Edinburgh", 
    "_rev" : "_eLYV6WC--C", 
    "label" : "Edinburgh" 
  }, 
  { 
    "_key" : "Glasgow", 
    "_id" : "places/Glasgow", 
    "_rev" : "_eLYV6WC--D", 
    "label" : "Glasgow" 
  }, 
  { 
    "_key" : "York", 
    "_id" : "places/York", 
    "_rev" : "_eLYV6WC--E", 
    "label" : "York" 
  }, 
  { 
    "_key" : "Carlisle", 
    "_id" : "places/Carlisle", 
    "_rev" : "_eLYV6WG---", 
    "label" : "Carlisle" 
  }, 
  { 
    "_key" : "Birmingham", 
    "_id" : "places/Birmingham", 
    "_rev" : "_eLYV6WG--_", 
    "label" : "Birmingham" 
  }, 
  { 
    "_key" : "London", 
    "_id" : "places/London", 
    "_rev" : "_eLYV6WG--A", 
    "label" : "London" 
  }, 
  { 
    "_key" : "Brussels", 
    "_id" : "places/Brussels", 
    "_rev" : "_eLYV6WG--B", 
    "label" : "Brussels" 
  }, 
  { 
    "_key" : "Cologne", 
    "_id" : "places/Cologne", 
    "_rev" : "_eLYV6WG--C", 
    "label" : "Cologne" 
  }, 
  { 
    "_key" : "Toronto", 
    "_id" : "places/Toronto", 
    "_rev" : "_eLYV6WG--D", 
    "label" : "Toronto" 
  }, 
  { 
    "_key" : "Winnipeg", 
    "_id" : "places/Winnipeg", 
    "_rev" : "_eLYV6WG--E", 
    "label" : "Winnipeg" 
  }, 
  { 
    "_key" : "Saskatoon", 
    "_id" : "places/Saskatoon", 
    "_rev" : "_eLYV6WG--F", 
    "label" : "Saskatoon" 
  }, 
  { 
    "_key" : "Edmonton", 
    "_id" : "places/Edmonton", 
    "_rev" : "_eLYV6WG--G", 
    "label" : "Edmonton" 
  }, 
  { 
    "_key" : "Jasper", 
    "_id" : "places/Jasper", 
    "_rev" : "_eLYV6WG--H", 
    "label" : "Jasper" 
  }, 
  { 
    "_key" : "Vancouver", 
    "_id" : "places/Vancouver", 
    "_rev" : "_eLYV6WG--I", 
    "label" : "Vancouver" 
  } 
]
[ 
  { 
    "_key" : "62090", 
    "_id" : "connections/62090", 
    "_from" : "places/Inverness", 
    "_to" : "places/Aberdeen", 
    "_rev" : "_eLYV6WG--J", 
    "travelTime" : 3 
  }, 
  { 
    "_key" : "62092", 
    "_id" : "connections/62092", 
    "_from" : "places/Aberdeen", 
    "_to" : "places/Inverness", 
    "_rev" : "_eLYV6WK---", 
    "travelTime" : 2.5 
  }, 
  { 
    "_key" : "62094", 
    "_id" : "connections/62094", 
    "_from" : "places/Aberdeen", 
    "_to" : "places/Leuchars", 
    "_rev" : "_eLYV6WK--_", 
    "travelTime" : 1.5 
  }, 
  { 
    "_key" : "62096", 
    "_id" : "connections/62096", 
    "_from" : "places/Leuchars", 
    "_to" : "places/Aberdeen", 
    "_rev" : "_eLYV6WK--A", 
    "travelTime" : 1 
  }, 
  { 
    "_key" : "62098", 
    "_id" : "connections/62098", 
    "_from" : "places/Leuchars", 
    "_to" : "places/Edinburgh", 
    "_rev" : "_eLYV6WK--B", 
    "travelTime" : 1.5 
  }, 
  { 
    "_key" : "62100", 
    "_id" : "connections/62100", 
    "_from" : "places/Edinburgh", 
    "_to" : "places/Leuchars", 
    "_rev" : "_eLYV6WK--C", 
    "travelTime" : 3 
  }, 
  { 
    "_key" : "62102", 
    "_id" : "connections/62102", 
    "_from" : "places/Edinburgh", 
    "_to" : "places/Glasgow", 
    "_rev" : "_eLYV6WK--D", 
    "travelTime" : 1 
  }, 
  { 
    "_key" : "62104", 
    "_id" : "connections/62104", 
    "_from" : "places/Glasgow", 
    "_to" : "places/Edinburgh", 
    "_rev" : "_eLYV6WK--E", 
    "travelTime" : 1 
  }, 
  { 
    "_key" : "62106", 
    "_id" : "connections/62106", 
    "_from" : "places/Edinburgh", 
    "_to" : "places/York", 
    "_rev" : "_eLYV6WK--F", 
    "travelTime" : 3.5 
  }, 
  { 
    "_key" : "62108", 
    "_id" : "connections/62108", 
    "_from" : "places/York", 
    "_to" : "places/Edinburgh", 
    "_rev" : "_eLYV6WK--G", 
    "travelTime" : 4 
  }, 
  { 
    "_key" : "62110", 
    "_id" : "connections/62110", 
    "_from" : "places/Glasgow", 
    "_to" : "places/Carlisle", 
    "_rev" : "_eLYV6WK--H", 
    "travelTime" : 1 
  }, 
  { 
    "_key" : "62112", 
    "_id" : "connections/62112", 
    "_from" : "places/Carlisle", 
    "_to" : "places/Glasgow", 
    "_rev" : "_eLYV6WO---", 
    "travelTime" : 1 
  }, 
  { 
    "_key" : "62114", 
    "_id" : "connections/62114", 
    "_from" : "places/Carlisle", 
    "_to" : "places/York", 
    "_rev" : "_eLYV6WO--_", 
    "travelTime" : 2.5 
  }, 
  { 
    "_key" : "62116", 
    "_id" : "connections/62116", 
    "_from" : "places/York", 
    "_to" : "places/Carlisle", 
    "_rev" : "_eLYV6WO--A", 
    "travelTime" : 3.5 
  }, 
  { 
    "_key" : "62118", 
    "_id" : "connections/62118", 
    "_from" : "places/Carlisle", 
    "_to" : "places/Birmingham", 
    "_rev" : "_eLYV6WO--B", 
    "travelTime" : 2 
  }, 
  { 
    "_key" : "62120", 
    "_id" : "connections/62120", 
    "_from" : "places/Birmingham", 
    "_to" : "places/Carlisle", 
    "_rev" : "_eLYV6WO--C", 
    "travelTime" : 1 
  }, 
  { 
    "_key" : "62122", 
    "_id" : "connections/62122", 
    "_from" : "places/Birmingham", 
    "_to" : "places/London", 
    "_rev" : "_eLYV6WO--D", 
    "travelTime" : 1.5 
  }, 
  { 
    "_key" : "62124", 
    "_id" : "connections/62124", 
    "_from" : "places/London", 
    "_to" : "places/Birmingham", 
    "_rev" : "_eLYV6WO--E", 
    "travelTime" : 2.5 
  }, 
  { 
    "_key" : "62126", 
    "_id" : "connections/62126", 
    "_from" : "places/Leuchars", 
    "_to" : "places/StAndrews", 
    "_rev" : "_eLYV6WO--F", 
    "travelTime" : 0.2 
  }, 
  { 
    "_key" : "62128", 
    "_id" : "connections/62128", 
    "_from" : "places/StAndrews", 
    "_to" : "places/Leuchars", 
    "_rev" : "_eLYV6WO--G", 
    "travelTime" : 0.2 
  }, 
  { 
    "_key" : "62130", 
    "_id" : "connections/62130", 
    "_from" : "places/York", 
    "_to" : "places/London", 
    "_rev" : "_eLYV6WO--H", 
    "travelTime" : 1.8 
  }, 
  { 
    "_key" : "62132", 
    "_id" : "connections/62132", 
    "_from" : "places/London", 
    "_to" : "places/York", 
    "_rev" : "_eLYV6WO--I", 
    "travelTime" : 2 
  }, 
  { 
    "_key" : "62134", 
    "_id" : "connections/62134", 
    "_from" : "places/London", 
    "_to" : "places/Brussels", 
    "_rev" : "_eLYV6WS---", 
    "travelTime" : 2.5 
  }, 
  { 
    "_key" : "62136", 
    "_id" : "connections/62136", 
    "_from" : "places/Brussels", 
    "_to" : "places/London", 
    "_rev" : "_eLYV6WS--_", 
    "travelTime" : 3.5 
  }, 
  { 
    "_key" : "62138", 
    "_id" : "connections/62138", 
    "_from" : "places/Brussels", 
    "_to" : "places/Cologne", 
    "_rev" : "_eLYV6WS--A", 
    "travelTime" : 2 
  }, 
  { 
    "_key" : "62140", 
    "_id" : "connections/62140", 
    "_from" : "places/Cologne", 
    "_to" : "places/Brussels", 
    "_rev" : "_eLYV6WS--B", 
    "travelTime" : 1.5 
  }, 
  { 
    "_key" : "62142", 
    "_id" : "connections/62142", 
    "_from" : "places/Toronto", 
    "_to" : "places/Winnipeg", 
    "_rev" : "_eLYV6WS--C", 
    "travelTime" : 36 
  }, 
  { 
    "_key" : "62144", 
    "_id" : "connections/62144", 
    "_from" : "places/Winnipeg", 
    "_to" : "places/Toronto", 
    "_rev" : "_eLYV6WS--D", 
    "travelTime" : 35 
  }, 
  { 
    "_key" : "62146", 
    "_id" : "connections/62146", 
    "_from" : "places/Winnipeg", 
    "_to" : "places/Saskatoon", 
    "_rev" : "_eLYV6WS--E", 
    "travelTime" : 12 
  }, 
  { 
    "_key" : "62148", 
    "_id" : "connections/62148", 
    "_from" : "places/Saskatoon", 
    "_to" : "places/Winnipeg", 
    "_rev" : "_eLYV6WS--F", 
    "travelTime" : 5 
  }, 
  { 
    "_key" : "62150", 
    "_id" : "connections/62150", 
    "_from" : "places/Saskatoon", 
    "_to" : "places/Edmonton", 
    "_rev" : "_eLYV6WS--G", 
    "travelTime" : 12 
  }, 
  { 
    "_key" : "62152", 
    "_id" : "connections/62152", 
    "_from" : "places/Edmonton", 
    "_to" : "places/Saskatoon", 
    "_rev" : "_eLYV6WS--H", 
    "travelTime" : 17 
  }, 
  { 
    "_key" : "62154", 
    "_id" : "connections/62154", 
    "_from" : "places/Edmonton", 
    "_to" : "places/Jasper", 
    "_rev" : "_eLYV6WW---", 
    "travelTime" : 6 
  }, 
  { 
    "_key" : "62156", 
    "_id" : "connections/62156", 
    "_from" : "places/Jasper", 
    "_to" : "places/Edmonton", 
    "_rev" : "_eLYV6WW--_", 
    "travelTime" : 5 
  }, 
  { 
    "_key" : "62158", 
    "_id" : "connections/62158", 
    "_from" : "places/Jasper", 
    "_to" : "places/Vancouver", 
    "_rev" : "_eLYV6WW--A", 
    "travelTime" : 12 
  }, 
  { 
    "_key" : "62160", 
    "_id" : "connections/62160", 
    "_from" : "places/Vancouver", 
    "_to" : "places/Jasper", 
    "_rev" : "_eLYV6WW--B", 
    "travelTime" : 13 
  } 
]

Suppose we want to query all routes from Aberdeen to London.

FOR p IN 1..10 OUTBOUND K_PATHS 'places/Aberdeen' TO 'places/London'
  GRAPH 'kShortestPathsGraph'
      RETURN { places: p.vertices[*].label, travelTimes: p.edges[*].travelTime }
Show query results
Hide query results
[
  {
    "places": [
      "Aberdeen",
      "Leuchars",
      "Edinburgh",
      "York",
      "London"
    ],
    "travelTimes": [
      1.5,
      1.5,
      3.5,
      1.8
    ]
  },
  {
    "places": [
      "Aberdeen",
      "Leuchars",
      "Edinburgh",
      "Glasgow",
      "Carlisle",
      "Birmingham",
      "London"
    ],
    "travelTimes": [
      1.5,
      1.5,
      1,
      1,
      2,
      1.5
    ]
  },
  {
    "places": [
      "Aberdeen",
      "Leuchars",
      "Edinburgh",
      "Glasgow",
      "Carlisle",
      "York",
      "London"
    ],
    "travelTimes": [
      1.5,
      1.5,
      1,
      1,
      2.5,
      1.8
    ]
  },
  {
    "places": [
      "Aberdeen",
      "Leuchars",
      "Edinburgh",
      "York",
      "Carlisle",
      "Birmingham",
      "London"
    ],
    "travelTimes": [
      1.5,
      1.5,
      3.5,
      3.5,
      2,
      1.5
    ]
  }
]

If we ask for routes that don’t exist we get an empty result (from Aberdeen to Toronto):

FOR p IN 1..10 OUTBOUND K_PATHS 'places/Aberdeen' TO 'places/Toronto'
  GRAPH 'kShortestPathsGraph'
      RETURN { places: p.vertices[*].label, travelTimes: p.edges[*].travelTime }
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