Bound AtlasMart multi-hop fraud queries by relationship type, depth, cycle policy, and selectivity so path exploration stays explainable and operationally safe.

Path, Neighborhood, Reachability, Pattern Matching, and Variable-Length Traversal

Use neighborhoods, reachability, bounded paths, and shortest-path-like reasoning without turning variable-length traversal into an unbounded combinatorial search.

Intermediate100–125 minutesBounded path + BFS labPython 3.13+ · standard libraryGQL + Neo4j Cypher optional referencesLast reviewed: August 2026

Learning outcomes

Use neighborhoods, reachability, bounded paths, and shortest-path-like reasoning without turning variable-length traversal into an unbounded combinatorial search.

01

Distinguish neighborhood, reachability, path, pattern match, and shortest-path-like questions.

02

Use explicit minimum/maximum path lengths and relationship predicates.

03

Explain cycles, uniqueness/visited policies, branching factor, and path explosion.

04

Interpret traversal evidence without assuming all variable-length queries are safe.

Implementation snapshot

Mandatory work uses Python 3.13+ standard library only on a single local process. No graph database, Docker image, cloud account, paid feature, network manipulation, or destructive failure injection is required. Neo4j 2026.07.1 is an optional current implementation reference; Community Edition is GPLv3. Product-specific clustering, sharding, security, and enterprise features are not assumed by the lab.

1. Different graph questions return different objects

A neighborhood asks which nodes/relationships are near an anchor within some hop limit. Reachability asks whether a target can be reached under permitted edge semantics. A path is a sequence of nodes and relationships connecting endpoints. Pattern matching constrains labels, relationship types/directions, properties, and sometimes path length. A shortest path minimizes an explicit cost such as hop count or weighted distance. These are not interchangeable. AtlasMart fraud screening may need “customers within four verified identity-sharing hops,” while dependency analysis may need “all services downstream of database X,” and recommendations may need scored candidate paths rather than every possible path.

2. Variable length must be bounded by semantics

Modern property-graph languages can express repeated path patterns. Neo4j's current Cypher documentation, for example, supports quantified path patterns such as a relationship repeated between lower and upper bounds and explicitly warns that broad variable-length patterns can match very large numbers of paths. The safe design question is not merely syntax. Choose a maximum depth justified by the domain, constrain edge types and tenant labels, prune on properties, and decide whether repeated nodes/edges are legal. A fraud rule that loses meaning beyond four hops should encode that business boundary instead of asking the database for arbitrary-length connectivity.

3. AtlasMart lab: make the mechanism observable

Save the following as lesson3_paths.py and run it with python lesson3_paths.py. The program has no dependencies and mutates no external state.

python · AtlasMart deterministic simulation
from collections import defaultdict, deque

adj = defaultdict(list)
edges = [
    ("cust:c1", "USES", "dev:d1"),
    ("dev:d1", "SEEN_WITH", "cust:c2"),
    ("cust:c2", "USES", "card:p9"),
    ("card:p9", "SEEN_WITH", "cust:c3"),
    ("cust:c3", "USES", "dev:d2"),
    ("dev:d2", "SEEN_WITH", "cust:c1"),  # cycle
    ("cust:c2", "USES", "dev:d3"),
    ("dev:d3", "SEEN_WITH", "cust:c4"),
]
for a,t,b in edges:
    adj[a].append((t,b))
    adj[b].append((t+"_REV",a))

def bounded_neighborhood(start, max_depth):
    q = deque([(start,0,[start])])
    best = {start:0}
    reached = []
    expansions = 0
    while q:
        node, depth, path = q.popleft()
        if depth == max_depth: continue
        expansions += 1
        for typ,nxt in adj[node]:
            nd = depth + 1
            if nd < best.get(nxt, 10**9):
                best[nxt] = nd
                q.append((nxt,nd,path+[nxt]))
                reached.append((nxt,nd,path+[nxt]))
    return reached, expansions

reached, expansions = bounded_neighborhood("cust:c1", 4)
print("bounded reached:", [(n,d) for n,d,p in reached])
print("expansions:", expansions)
path_to_c3 = next(p for n,d,p in reached if n == "cust:c3")
print("shortest discovered path to c3:", path_to_c3)

# Deliberately wrong: enumerate walks with no visited-state pruning.
def count_walks(start, depth):
    frontier = [start]
    total = 0
    for _ in range(depth):
        nxt = []
        for node in frontier:
            for _,neighbor in adj[node]:
                total += 1
                nxt.append(neighbor)
        frontier = nxt
    return total, len(frontier)

for depth in (2,4,6,8):
    examined, frontier = count_walks("cust:c1", depth)
    print(f"unpruned depth={depth}: edges_examined={examined}, frontier={frontier}")
Expected evidence

Expected evidence: bounded breadth-first search reaches the connected fraud neighborhood while remembering the shallowest depth and handling the cycle; the first discovered path to c3 is a shortest path in the unweighted model. The unpruned walk enumeration grows rapidly as depth increases because it revisits cycles and branches. This demonstrates path-explosion mechanics, not a product benchmark.

4. Branching factor creates a multiplicative search space

If each expanded node has roughly b useful outgoing relationships, an unconstrained tree-like traversal can expose on the order of b^d candidate walks at depth d. Real graphs have cycles and shared nodes, so the exact number varies, but the operational lesson remains: broad predicates and high-degree nodes make variable paths expensive. “Graph query” is not synonymous with cheap query. Measure rows/paths produced per expansion, maximum frontier size, memory, network hops, and tail latency.

5. Cycles require an explicit path semantics

AtlasMart's identity graph naturally contains cycles: customer→device→customer→card→customer and perhaps back to an earlier device. A traversal engine must know whether a node or relationship may repeat within a path. A reachability search often uses a visited set because revisiting the same state adds no value. A path enumeration query may intentionally permit different routes to the same node. Those choices change both correctness and cost; hiding them behind an unlimited *-style pattern is an operational mistake.

6. Security and tenancy belong inside traversal rules

A path query can leak information even when individual anchor lookups are authorized. Suppose tenant t1 can see a shared IP node but must not learn that tenant t2 also touched it. If traversal privileges or application filters do not preserve the tenant boundary at every hop, the graph becomes a lateral information channel. Tests therefore need negative paths: start with a permitted tenant anchor, cross a shared infrastructure node, and verify that forbidden tenant nodes/edge properties remain invisible.

7. Production judgment

Bound variable-length queries, verify query plans, cap result cardinality, and distinguish “shortest one path” from “enumerate all paths.” Use precomputation when the same expensive reachability answer is requested repeatedly and its freshness contract is clear. In distributed graphs, each frontier expansion can multiply network fan-out; in dense graphs, memory and scheduler pressure may dominate. Benchmark with realistic degree distributions and cold/warm cache states rather than a toy chain. The next lesson focuses on the supernodes and edge cuts that make those costs extreme.

Wrong approach: ask for “all paths of any length” and hope the graph is small

Failure injection / diagnosis

An investigator writes an unrestricted variable-length traversal with no maximum depth, no edge-type filter, and no cycle policy. On a cyclic fraud graph the query repeatedly revisits the same structures and can enumerate huge numbers of walks. Repair it by defining the business hop limit, permitted relationship types, uniqueness/visited semantics, tenant constraints, and a result budget; then measure frontier/path cardinality before rolling it into an online request path.

Verification, cleanup, and production checklist

Verification is the program output plus the conceptual checks below. Cleanup is simply deleting the local lesson3_paths.py file; the simulation creates no sockets, services, databases, containers, credentials, or persistent data. In production, additionally verify tenant authorization on traversals, identity/constraint health, degree and path-cardinality distributions, p95/p99 latency, cache and remote-hop behavior where applicable, backup/restore or projection rebuild, software/security advisories, and edition/license constraints before adopting product-specific features.

Check your understanding

  1. What is the difference between reachability and path enumeration?
  2. Why is a maximum traversal depth valuable?
  3. What causes path explosion?
  4. Why can a visited set be correct for one query and wrong for another?
  5. What security test is graph-specific?
Review the answers

1. Reachability can stop once existence is known; path enumeration materializes one or more concrete routes and can be much more expensive.

2. It encodes domain meaning and caps one dimension of the search space.

3. Branching, broad predicates, high-degree nodes, repeated states/cycles, and large depth combine multiplicatively.

4. Reachability often needs only the first state visit, while queries seeking distinct paths may intentionally revisit a node through different routes.

5. Verify that traversal through shared nodes cannot reveal unauthorized tenant nodes or relationship properties.

References

Foundational statements use the published property-graph standard where appropriate; implementation-sensitive examples use current official documentation and are labeled as examples rather than universal graph guarantees.

Keep knowledge open

Help the academy stay free and grow.

If these tutorials save you time, a small donation supports new lessons, technical review, diagrams, examples, and long-term maintenance.

ETHEthereum / ERC-20 only
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0

Send only Ethereum or ERC-20 compatible assets to this address.