Basic Operations

Overview

Once you've defined your graph model, you can perform basic operations like adding vertices and edges, querying the graph, and modifying elements. This guide covers the fundamental operations available in Graph API.

Creating a Graph

To start working with graphs, first create a new graph instance:

        // Import the standard model and create a new graph
        let mut graph = SimpleGraph::<Vertex, Edge>::new();

Adding Vertices

Once you have a graph instance, you can add vertices to it:

        // Add a person vertex
        let bryn = graph.add_vertex(Vertex::Person {
            name: "Bryn".to_string(),
            username: "bryn123".to_string(),
            biography: "Graph enthusiast".to_string(),
            age: 28,
        });

        // Add a project vertex
        let project = graph.add_vertex(Vertex::Project {
            name: "GraphAPI".to_string(),
        });

        // Add a comment vertex
        let comment = graph.add_vertex(Vertex::Comment {
            text: "Great project!".to_string(),
            date: "2023-05-15".to_string(),
        });

Adding Edges

Edges connect vertices in your graph:

        // Add simple edges
        graph.add_edge(bryn, project, Edge::Created);
        graph.add_edge(julia, bryn, Edge::Follows);

        // Add edges with properties
        graph.add_edge(
            bryn,
            project,
            Edge::Liked {
                timestamp: "2023-01-15".to_string(),
            },
        );

Querying Vertices

You can query vertices using the walker API. Here are some examples:

Full Scan

When you need to find all vertices in the graph:

        // Get all vertices in the graph
        let all_vertices = graph
            .walk()
            .vertices() // Start with all vertices
            .collect::<Vec<_>>();

        // Count vertices by type using type-safe filters
        let person_count = graph
            .walk()
            .vertices()
            .filter_person() // Use generated helper method
            .count();

        let project_count = graph
            .walk()
            .vertices()
            .filter_project() // Use generated helper method
            .count();

Label-Based Lookup

For more efficient queries, use label-based indexes:

        // Find all Person vertices using label index
        let all_people = graph
            .walk()
            .vertices()
            .filter_person() // Use generated helper for label filtering
            .collect::<Vec<_>>();

        // Find all Project vertices using label index
        let all_projects = graph
            .walk()
            .vertices()
            .filter_project() // Use generated helper for label filtering
            .collect::<Vec<_>>();

Property Filtering

Filter vertices based on their properties:

        // Find people over 30 years old
        let older_people = graph
            .walk()
            .vertices()
            .filter_by_person(|person, _| {
                // Type-safe access to Person properties
                person.age() > 30
            })
            .map(|v, _| {
                // Use type-safe projection and accessor methods
                let person = v.project::<Person<_>>().unwrap();
                format!("{} ({})", person.name(), person.age())
            })
            .collect::<Vec<_>>();

        // Find people with "programmer" in their biography
        let programmers = graph
            .walk()
            .vertices()
            .filter_by_person(|person, _| {
                // Type-safe access to Person properties
                person.biography().contains("programmer")
            })
            .map(|v, _| {
                // Use type-safe projection and accessor methods
                v.project::<Person<_>>().unwrap().name().to_string()
            })
            .collect::<Vec<_>>();

Working with Edges

The walker API also provides powerful tools for working with edges:

Finding Connected Edges

Get all edges connected to a vertex:

            // Get all edges connected to a specific vertex
            let connected_edges = graph
                .walk()
                .vertices_by_id(vec![person_id])
                .edges(EdgeSearch::scan()) // Get all connected edges
                .collect::<Vec<_>>();

Directional Edge Queries

Specify whether you want incoming or outgoing edges:

            // Get only outgoing edges
            let outgoing_edges = graph
                .walk()
                .vertices_by_id(vec![person_id])
                .edges(EdgeSearch::scan().outgoing()) // Only outgoing edges
                .collect::<Vec<_>>();

            // Get only incoming edges
            let incoming_edges = graph
                .walk()
                .vertices_by_id(vec![person_id])
                .edges(EdgeSearch::scan().incoming()) // Only incoming edges
                .collect::<Vec<_>>();

Label-Based Edge Filtering

Filter edges by their label:

        // Find all "Created" edges in the graph
        let creation_edges = graph
            .walk()
            .edges(EdgeSearch::scan())
            .filter_created() // Type-safe filter using generated helper
            .collect::<Vec<_>>();

        // Find all "Follows" edges in the graph
        let follow_edges = graph
            .walk()
            .edges(EdgeSearch::scan())
            .filter_follows() // Type-safe filter using generated helper
            .collect::<Vec<_>>();

        // Find all edges with timestamp properties
        let timestamped_edges = graph
            .walk()
            .edges(EdgeSearch::scan())
            .filter(|e, _| match e.weight() {
                Edge::Liked { timestamp } => true,
                Edge::Commented { timestamp } => true,
                _ => false,
            })
            .collect::<Vec<_>>();

Modifying the Graph

You can modify the graph during traversal using the mutation API:

Updating Vertex Properties

        // Update all Person vertices to increment their age
        let updated_count = graph
            .walk_mut()
            .vertices()
            .filter_person() // Type-safe filter using generated helper
            .mutate(|v| {
                // Type-safe mutation using projection
                if let Some(mut person) = v.project_mut::<Person<_>>() {
                    // Update the age property
                    let current_age = person.age();
                    person.set_age(current_age + 1);
                    true // Indicate that we modified the vertex
                } else {
                    false // No modification
                }
            });

        // Add a property to a specific person
        graph
            .walk_mut()
            .vertices()
            .filter_by_person(|p, _| p.username() == "bryn123")
            .mutate(|v| {
                // Type-safe mutation using projection
                if let Some(mut person) = v.project_mut::<Person<_>>() {
                    // Update the biography property
                    person.set_biography("Updated biography: Graph API expert");
                    true // Indicate that we modified the vertex
                } else {
                    false // No modification
                }
            });

Verifying Changes

After modification, you can verify the changes:

        // Check that Bryn's biography was updated
        let updated_bio = graph
            .walk()
            .vertices()
            .filter_by_person(|p, _| p.username() == "bryn123")
            .map(|v, _| {
                // Type-safe access using projection
                v.project::<Person<_>>().unwrap().biography().to_string()
            })
            .first();

        // Verify all people are now older
        let all_ages = graph
            .walk()
            .vertices()
            .filter_person() // Type-safe filter using generated helper
            .map(|v, _| {
                // Type-safe access using projection
                let person = v.project::<Person<_>>().unwrap();
                (person.name().to_string(), person.age())
            })
            .collect::<Vec<_>>();

Next Steps

Now that you understand the basic operations, you can:

  1. Learn about Advanced Traversals using the Walker API
  2. Explore Context and State in graph traversals
  3. See Examples of Walker Steps to build more complex queries