Filter Step

The filter step narrows a traversal by keeping only vertices or edges that match a specified predicate.

Filter step diagram showing elements being kept or discarded based on a predicate

In this diagram:

  • An Input Stream contains elements A (age=35), B (age=25), and C (age=40).
  • The .filter(|v| v.age > 30) step processes each element, applying the predicate.
  • Elements A and C satisfy the predicate (age > 30) and pass through to the Output Stream.
  • Element B does not satisfy the predicate and is Discarded, marked with an 'X'.

Syntax

walker.filter(|element, context| /* predicate logic */)

Parameters

  • predicate: A function that takes a reference to a graph element and optional context, and returns a boolean.
    • Returns true to keep the element in the traversal
    • Returns false to remove it from the traversal

Return Value

Returns a new walker containing only the elements that match the predicate.

Examples

Basic Filter

Filter vertices based on a simple condition:

    // Basic filter using a closure
    let adult_people = graph
        .walk()
        .vertices(Vertex::person())
        .filter_by_person(|person, _| person.age() >= 18)
        .collect::<Vec<_>>();

    println!("Found {} adults", adult_people.len());

Type-Specific Filtering

Use the type-specific filter methods generated by derive macros:

    // Use the type-specific filter methods generated by the VertexExt derive macro
    let people_named_b = graph
        .walk()
        .vertices(Vertex::person())
        .filter_by_person(|person, _| {
            // This closure gets a strongly-typed view of the Person data
            person.name().starts_with('B')
        })
        .collect::<Vec<_>>();

    println!(
        "Found {} people whose names start with B",
        people_named_b.len()
    );

Chained Filters

Combine multiple filters for complex queries:

    // Chain multiple filters for complex conditions
    let specific_people = graph
        .walk()
        .vertices(Vertex::person())
        // First filter: age range
        .filter_by_person(|person, _| person.age() > 25 && person.age() < 40)
        // Second filter: name contains 'y'
        .filter_by_person(|person, _| person.name().contains('y'))
        .collect::<Vec<_>>();

    println!(
        "Found {} people aged 26-39 with 'y' in their name",
        specific_people.len()
    );

Filter with Context

Filter based on context information:

    // Use filter with context
    let result = graph
        .walk()
        .vertices(Vertex::person())
        .push_context(|v, _| {
            // Store original vertex in context
            if let Some(person) = v.project::<Person<_>>() {
                person.name().to_string()
            } else {
                String::new()
            }
        })
        .filter(|_, ctx| {
            // Filter based on context
            ctx.len() > 3
        })
        .collect::<Vec<_>>();

    println!(
        "Found {} people with names longer than 3 characters",
        result.len()
    );

Best Practices

  • Prefer indexed searches over filter steps when querying by property values
  • Break complex filtering logic into multiple chained filters for readability
  • Use pattern matching to handle different vertex or edge types correctly
  • Leverage generated filter methods from derive macros for stronger type safety

Common Use Cases

  • Post-retrieval refinement: Filtering elements after initial selection when indexes don't fully cover criteria
  • Dynamic filtering: Applying runtime conditions that can't be encoded in initial searches
  • Complex conditions: Implementing filtering logic that combines multiple properties or calculations
  • Context-aware filtering: Using information from previous traversal steps to inform filtering decisions