Context
The push_context
step allows you to carry information along a graph traversal, making it possible to access data from
previous steps while working with the current element. Context creates a typed value that travels with the traversal
without changing its position.
In this diagram:
- An Input Stream contains elements A and B.
- The
.push_context(|v| v.id())
step is applied. The context function (e.g.,|v| v.id()
) is evaluated once when the step is encountered (conceptually, when element A is processed). - The resulting context value (e.g.,
"A_ID"
) is then attached to all subsequent elements (A and B) in the Output Stream. - This demonstrates that the context value is determined when the
push_context
step is applied and remains fixed for the rest of that traversal segment.
Methods for Adding Context
// Adding context to a traversal
walker.push_context(|element, current_context| {
// Create and return new context value
})
// Adding current vertex/edge as context
walker.push_default_context()
Parameters
context_fn
: A function that takes:- A reference to the current element (vertex or edge)
- The current context (if any)
- Returns a new context value to be carried along the traversal
Return Value
Returns a new walker with the context added, preserving the current traversal position.
Accessing Context
Any step that accepts a function with context (like map
, filter
, etc.) will receive:
- The current element as the first parameter
- The context as the second parameter
// Using context in a map step
walker
.push_context(|v, _| v.id()) // Store vertex ID
.map(|current, context| {
// Use the stored vertex ID from context
format!("Current: {}, Source: {}", current.id(), context)
})
Examples
Vertex Context
Store information about the source vertex during a traversal:
// Use push_default_context to make source vertex information available during traversal
let knows: Vec<_> = graph
.walk()
.vertices_by_id(vec![bryn_id, julia_id])
.push_default_context()
.edges(EdgeSearch::scan().outgoing())
.filter_follows()
.head()
.map(|target, ctx| {
if let Vertex::Person { name, .. } = ctx.vertex() {
format!(
"{} follows {}",
name,
target.project::<Person<_>>().unwrap().name()
)
} else {
"Not a person".to_string()
}
})
.collect::<Vec<_>>();
// Check the results - should have 2 person descriptions
assert_eq!(knows.len(), 2);
println!("Vertex Context Example - Relationships found:");
for relationship in &knows {
println!("- {}", relationship);
}
Edge Context
Track edge types during traversal:
// Walk the graph starting from the person vertex
let edge_types = graph
.walk()
.vertices_by_id(vec![person_id])
.edges(EdgeSearch::scan().outgoing())
.push_context(|edge, _ctx| {
// Determine edge type based on the edge type
let edge_type = match edge.weight() {
Edge::Created => "Created",
Edge::Follows => "Follows",
Edge::Liked { .. } => "Liked",
Edge::Commented { .. } => "Commented",
};
// Return the edge type as context
edge_type
})
.map(|_v, c| *c)
.collect::<Vec<_>>();
println!("{:?}", edge_types);
Path Tracking
Build a representation of the path taken during traversal:
// Track the path while traversing
let paths = graph
.walk()
.vertices_by_id(vec![start_id])
// Start with an empty path containing just the start vertex name
.push_context(|v, _| {
vec![match v.weight() {
Vertex::Person { name, .. } => name.clone(),
_ => "Unknown".to_string(),
}]
})
// Follow outgoing follows edges
.edges(EdgeSearch::scan().outgoing())
.filter_follows()
.tail()
// Add each person to the path
.push_context(|v, ctx| {
let mut new_path = (**ctx).clone();
if let Vertex::Person { name, .. } = v.weight() {
new_path.push(name.clone());
}
new_path
})
// Collect all paths
.map(|_, ctx| ctx.join(" -> "))
.collect::<Vec<_>>();
// Print all paths
println!("All paths from start:");
for path in paths {
println!("- {}", path);
}
Type Safety
The context system is fully type-safe:
- Each context value has a concrete type
- Context transformation functions must return the correct type
- Closures that receive context are provided with the correctly typed context
Common Use Cases
- Path tracking: Store the sequence of vertices or edges traversed
- Metadata collection: Gather information from different parts of the graph
- Aggregation: Build up composite results during traversal
- Decision making: Use information from earlier steps to influence later decisions
Best Practices
- Keep contexts immutable - create new contexts rather than modifying existing ones
- Use
push_default_context()
when you simply need to track the current vertex/edge - Chain multiple context operations to build complex data structures
- Consider type-safety when designing context pipelines