Detour Step

The detour step is a powerful operation that allows you to execute a sub-traversal for each element currently in the main walker stream. The results yielded by each sub-traversal do not replace the original element in the main stream. This enables complex conditional logic and exploration without losing the main traversal context.

Detour step diagram showing input elements triggering sub-walkers, whose results form the output stream

In this diagram:

  • Input Elements: The main walker stream arrives at the detour step with elements A and X.
  • .detour(|sub| ...): For each input element, a sub-walker is initiated:
    • Sub-Walker (from A): Starts at A, executes the steps defined in the closure (e.g., edges().head()), and yields results B and C.
    • Sub-Walker (from X): Starts at X, executes the same steps, and yields result Y.
  • Output Elements (Results): The main walker stream continues, now containing the combined results from all sub-walkers (A, A, X). The original elements (A, X) have been replaced.

Syntax

walker.detour(|sub_walker_builder| {
    // Configure the sub-walker with additional steps
    sub_walker_builder
        .edges(...) // Example step
        .head()     // Example step
        // ... more steps ...
})

Parameters

  • detour_fn: A closure that receives a StartWalkerBuilder positioned at the current element from the main stream. This closure defines the steps for the sub-traversal.

Return Value

Returns a new walker where the elements are the combined results of all the sub-traversals executed within the detour.

Examples

Basic Detour

This example shows how to use a detour to find projects created by people who follow someone over 30:

    // Basic detour example: Find projects created by people who follow someone over 30
    println!("Projects created by people who follow someone over 30:");

    let projects = graph
        .walk()
        .vertices(Vertex::person()) // Start with all people
        .detour(|sub_walker| {
            // The detour checks if this person follows someone over 30
            sub_walker
                .edges(Edge::follows()) // Follow "follows" edges
                .head() // Move to the target person
                .filter_by_person(|person, _| person.age() > 30) // Check if they're over 30
                .take(1) // We only need one match to qualify
        })
        // Back to original person vertices that passed the detour check
        .edges(Edge::created()) // Find what they created
        .head() // Move to the created project
        .filter_project() // Keep only project vertices
        .collect::<Vec<_>>();

    for project in projects {
        println!("{:?}", project);
    }

Nested Detours

This example demonstrates using nested detours for more complex traversal logic:

    // Complex detour example: Find people who created projects that were liked by others
    println!("\nPeople who created projects that were liked by others:");

    let creators = graph
        .walk()
        .vertices(Vertex::person()) // Start with all people
        .push_default_context()
        .detour(|creator_walker| {
            // Detour to check if this person created something that was liked
            creator_walker
                .edges(Edge::created()) // Find what they created
                .head() // Move to the created project
                .detour(|project_walker| {
                    // Nested detour to check if the project was liked
                    project_walker
                        .edges(Edge::liked()) // Find "liked" edges pointing to this project
                        .tail() // Move to the person who liked it
                        .filter(|liker, ctx| {
                            // Check that the liker is different from the creator
                            *ctx.vertex_id() != liker.id()
                        })
                })
        })
        .collect::<Vec<_>>();

    for creator in creators {
        println!("{:?} created something that was liked", creator);
    }

When to use detour

Detour shines in several common graph traversal scenarios:

  • Conditional filtering based on connected elements:
    For example, "Find people who follow someone over 30" requires exploring connections before making filtering decisions

  • Looking ahead without changing position:
    When you need to check properties of elements further in the graph before deciding how to proceed

  • Complex multi-step conditions:
    When a condition requires checking multiple properties or relationships that would be awkward with chained filters

  • Collecting contextual information:
    When you need to gather information from related elements to inform decisions in the main traversal

  • Avoiding traversal state mutation:
    When you want to explore potential paths without permanently changing your traversal state

Implementation Notes

  • The detour closure receives a fresh walker starting at the current position
  • All walker operations inside the detour are executed in a sub-traversal context
  • Results from the detour replace the current elements in the traversal
  • The main traversal continues from where it left off, but with the filtered/transformed elements
  • For better performance, try to use indexed lookups within detours whenever possible
  • Detours can be nested for extremely complex traversal logic
  • Using detours improves code readability by logically grouping related traversal operations