Skip to content

AllPaths

relationalai.semantics.std.path

Selects every matching path. Built by PathPattern.all.

Methods

.paths()

AllPaths.paths() -> DerivedColumn

Return one PathTraversal per matching path.

Enumeration uses walk semantics: nodes and edges may repeat, so cycles in the graph can result in arbitrarily many paths.

Returns:

Examples:

p = m.path(Person.follows.repeat(1, 3)).where(Person.name == "Alice").all().paths()
m.where(p).select(
p, p.length, p.nodes["index"], Person(p.nodes).name.alias("node")
).inspect()
# p length index node
# 0 6w6baylbPzQBFBq7X+77Ug 1 0 Alice
# 1 6w6baylbPzQBFBq7X+77Ug 1 1 Bob
# 2 IBUHvCZTYTKnBRjIey8suw 2 0 Alice
# 3 IBUHvCZTYTKnBRjIey8suw 2 1 Bob
# 4 IBUHvCZTYTKnBRjIey8suw 2 2 David

Each distinct p value identifies one path; rows sharing a p describe the nodes visited along it.

Notes:

Only the path’s endpoint identifiers unify with the enclosing scope. Filters on interior nodes must be attached with PathPattern.where on the pattern; a filter referencing interior nodes placed in an enclosing Model.where cannot be routed into the path and raises.

Performance: Enumerating all paths is worst-case exponential in the size of the graph and thus may be EXTREMELY EXPENSIVE! Try to make your path pattern as selective as possible, and run on smaller test data when building your model and query for path count first. Confirm that your graph is acyclic via Graph.is_acyclic. Unless you need variable repeats or enumeration of nodes and edges along the path, consider using regular PyRel to express your query instead of .all().paths(). If you only need to know whether or which endpoints are connected — not the paths themselves — use PathPattern.exists, which runs in polynomial time.

.count()

AllPaths.count() -> Aggregate

Return the number of matching paths.

The returned object acts like any other PyRel sum or count aggregate. When used bare, it has an implied empty .per(), so returns the single global count of matching paths, exactly like a bare count(x) aggregates globally. Group the count by applying .per(...) to the result with the pattern’s endpoint identifiers: .per(initial_node, final_node) counts per connected pair, .per(final_node) counts per final node, and so on.

Returns:

  • Aggregate - The Integer count, usable inside Model.where / Model.select, grouped by an optional .per(...) at the use site.

Raises:

  • relationalai.util.error.RAIException - PathNonDeterminismError if the pattern is ambiguous. Counting requires the pattern to be deterministic, i.e., there is only one way to match each path. An ambiguous pattern like path(x.follows.repeat(0, 1), y.follows.repeat(0, 1), z), where x and y are Refs of the same Concept, raises when used with this method.

Examples:

Define the count of paths from each criminal to each suspect. Suppose the below pattern path(Criminal.works_with.repeat(1, 5), suspect) matches:

  • 3 paths of length 1 from Alice to Bob
  • 2 paths of length 2 from Alice to Bob
  • 1 path of length 3 from Alice to Charlie Unlike AllShortestPaths.count, every path is counted, not only the shortest ones.
suspect = Person.ref()
path_counts = m.path(Criminal.works_with.repeat(1, 5), suspect).all().count()

Select the number of paths, grouped by both path endpoints, assuming that Alice is the only Criminal in the model:

m.select(Criminal.name, suspect.name, path_counts.per(Criminal, suspect)).inspect()
# name name_2 sum
# 0 Alice Alice <NA>
# 1 Alice Bob 5
# 2 Alice Charlie 1

Select the total number of paths:

m.select(path_counts).inspect()
# sum
# 0 6

Select the number of paths, grouped by the final node:

m.select(suspect.name, path_counts.per(suspect) | 0).inspect()
# name v0
# 0 Alice 0
# 1 Bob 5
# 2 Charlie 1

Select the suspects with 2 or more paths from a Criminal:

m.where(path_counts.per(suspect) >= 2).select(suspect.name).inspect()
# name
# 0 Bob

Unlike .all_shortest().per().count(), the pattern need not name its endpoints:

m.select(m.path(Criminal.works_with.repeat(1, 3)).all().count())

Notes:

Only the path’s endpoint identifiers unify with the enclosing scope. Filters on interior nodes must be attached with PathPattern.where on the pattern; a filter referencing interior nodes placed in an enclosing Model.where cannot be routed into the path and raises.

Counting uses the same walk semantics as AllPaths.paths: nodes and edges may repeat, so on a cyclic graph every walk within the repeat bounds is counted.

A group with no matching paths gets no count. If you select the group’s keys together with the count, its count shows as empty, not 0. Use | 0 to get a 0 instead.

Performance: The paths are counted without being enumerated: computing the counts is polynomial in the size of the graph, even when the number of paths is exponential. Use this to check the size of a result before calling AllPaths.paths. The count itself can exceed the 128-bit integer range (2^127 - 1), which raises a numeric overflow error rather than returning a wrong count.

Inheritance Hierarchy

AllPathsPathSelector