Skip to main content

LpgStore

Struct LpgStore 

Source
pub struct LpgStore { /* private fields */ }
Expand description

The core in-memory graph storage.

Everything lives here: nodes, edges, properties, adjacency indexes, and version chains for MVCC. Concurrent reads never block each other.

Most users should go through GrafeoDB (from the grafeo_engine crate) which adds transaction management and query execution. Use LpgStore directly when you need raw performance for algorithm implementations.

§Example

use grafeo_core::graph::lpg::LpgStore;
use grafeo_core::graph::Direction;

let store = LpgStore::new();

// Create a small social network
let alice = store.create_node(&["Person"]);
let bob = store.create_node(&["Person"]);
store.create_edge(alice, bob, "KNOWS");

// Traverse outgoing edges
for neighbor in store.neighbors(alice, Direction::Outgoing) {
    println!("Alice knows node {:?}", neighbor);
}

§Lock Ordering

LpgStore contains multiple RwLock fields that must be acquired in a consistent order to prevent deadlocks. Always acquire locks in this order:

§Level 1 - Entity Storage (mutually exclusive via feature flag)

  1. nodes / node_versions
  2. edges / edge_versions

§Level 2 - Catalogs (acquire as pairs when writing)

  1. label_to_id + id_to_label
  2. edge_type_to_id + id_to_edge_type

§Level 3 - Indexes

  1. label_index
  2. node_labels
  3. property_indexes

§Level 4 - Statistics

  1. statistics

§Level 5 - Nested Locks (internal to other structs)

  1. PropertyStorage::columns (via node_properties/edge_properties)
  2. ChunkedAdjacency::lists (via forward_adj/backward_adj)

§Rules

  • Catalog pairs must be acquired together when writing.
  • Never hold entity locks while acquiring catalog locks in a different scope.
  • Statistics lock is always last.
  • Read locks are generally safe, but avoid read-to-write upgrades.

Implementations§

Source§

impl LpgStore

Source

pub fn create_edge(&self, src: NodeId, dst: NodeId, edge_type: &str) -> EdgeId

Creates a new edge.

Source

pub fn create_edge_with_props( &self, src: NodeId, dst: NodeId, edge_type: &str, properties: impl IntoIterator<Item = (impl Into<PropertyKey>, impl Into<Value>)>, ) -> EdgeId

Creates a new edge with properties.

Source

pub fn get_edge(&self, id: EdgeId) -> Option<Edge>

Gets an edge by ID (latest visible version).

Source

pub fn delete_edge(&self, id: EdgeId) -> bool

Deletes an edge (using latest epoch).

Source

pub fn edge_count(&self) -> usize

Returns the number of edges (non-deleted at current epoch).

Source

pub fn batch_create_edges( &self, edges: &[(NodeId, NodeId, &str)], ) -> Vec<EdgeId>

Creates multiple edges in batch, significantly faster than calling create_edge() in a loop.

Each tuple is (src, dst, edge_type). Returns the assigned EdgeIds in the same order. Acquires the adjacency write lock once for all edges, rather than once per edge.

Source

pub fn edge_type(&self, id: EdgeId) -> Option<ArcStr>

Gets the type of an edge by ID.

Source§

impl LpgStore

Source

pub fn create_property_index(&self, property: &str)

Creates an index on a node property for O(1) lookups by value.

After creating an index, calls to Self::find_nodes_by_property will be O(1) instead of O(n) for this property. The index is automatically maintained when properties are set or removed.

§Example
use grafeo_core::graph::lpg::LpgStore;
use grafeo_common::types::Value;

let store = LpgStore::new();

// Create nodes with an 'id' property
let alice = store.create_node(&["Person"]);
store.set_node_property(alice, "id", Value::from("alice_123"));

// Create an index on the 'id' property
store.create_property_index("id");

// Now lookups by 'id' are O(1)
let found = store.find_nodes_by_property("id", &Value::from("alice_123"));
assert!(found.contains(&alice));
Source

pub fn drop_property_index(&self, property: &str) -> bool

Drops an index on a node property.

Returns true if the index existed and was removed.

Source

pub fn has_property_index(&self, property: &str) -> bool

Returns true if the property has an index.

Source§

impl LpgStore

Source

pub fn create_node(&self, labels: &[&str]) -> NodeId

Creates a new node with the given labels.

Uses the system transaction for non-transactional operations.

Source

pub fn create_node_with_props( &self, labels: &[&str], properties: impl IntoIterator<Item = (impl Into<PropertyKey>, impl Into<Value>)>, ) -> NodeId

Creates a new node with labels and properties.

Source

pub fn create_node_with_props_versioned( &self, labels: &[&str], properties: impl IntoIterator<Item = (impl Into<PropertyKey>, impl Into<Value>)>, epoch: EpochId, tx_id: TxId, ) -> NodeId

Creates a new node with labels and properties within a transaction context.

Source

pub fn get_node(&self, id: NodeId) -> Option<Node>

Gets a node by ID (latest visible version).

Source

pub fn delete_node(&self, id: NodeId) -> bool

Deletes a node and all its edges (using latest epoch).

Source

pub fn delete_node_edges(&self, node_id: NodeId)

Deletes all edges connected to a node (implements DETACH DELETE).

Call this before delete_node() if you want to remove a node that has edges. Grafeo doesn’t auto-delete edges - you have to be explicit.

Source

pub fn node_count(&self) -> usize

Returns the number of nodes (non-deleted at current epoch).

Source

pub fn node_ids(&self) -> Vec<NodeId>

Returns all node IDs in the store.

This returns a snapshot of current node IDs. The returned vector excludes deleted nodes. Results are sorted by NodeId for deterministic iteration order.

Source§

impl LpgStore

Source

pub fn set_node_property(&self, id: NodeId, key: &str, value: Value)

Sets a property on a node.

Source

pub fn set_edge_property(&self, id: EdgeId, key: &str, value: Value)

Sets a property on an edge.

Source

pub fn remove_node_property(&self, id: NodeId, key: &str) -> Option<Value>

Removes a property from a node.

Returns the previous value if it existed, or None if the property didn’t exist.

Source

pub fn remove_edge_property(&self, id: EdgeId, key: &str) -> Option<Value>

Removes a property from an edge.

Returns the previous value if it existed, or None if the property didn’t exist.

Source

pub fn get_node_property(&self, id: NodeId, key: &PropertyKey) -> Option<Value>

Gets a single property from a node without loading all properties.

This is O(1) vs O(properties) for get_node().get_property(). Use this for filter predicates where you only need one property value.

§Example
let store = LpgStore::new();
let node_id = store.create_node(&["Person"]);
store.set_node_property(node_id, "age", Value::from(30i64));

// Fast: Direct single-property lookup
let age = store.get_node_property(node_id, &PropertyKey::new("age"));

// Slow: Loads all properties, then extracts one
let age = store.get_node(node_id).and_then(|n| n.get_property("age").cloned());
Source

pub fn get_edge_property(&self, id: EdgeId, key: &PropertyKey) -> Option<Value>

Gets a single property from an edge without loading all properties.

This is O(1) vs O(properties) for get_edge().get_property().

Source

pub fn get_node_property_batch( &self, ids: &[NodeId], key: &PropertyKey, ) -> Vec<Option<Value>>

Gets a property for multiple nodes in a single batch operation.

More efficient than calling Self::get_node_property in a loop because it reduces lock overhead and enables better cache utilization.

§Example
use grafeo_core::graph::lpg::LpgStore;
use grafeo_common::types::{NodeId, PropertyKey, Value};

let store = LpgStore::new();
let n1 = store.create_node(&["Person"]);
let n2 = store.create_node(&["Person"]);
store.set_node_property(n1, "age", Value::from(25i64));
store.set_node_property(n2, "age", Value::from(30i64));

let ages = store.get_node_property_batch(&[n1, n2], &PropertyKey::new("age"));
assert_eq!(ages, vec![Some(Value::from(25i64)), Some(Value::from(30i64))]);
Source

pub fn get_nodes_properties_batch( &self, ids: &[NodeId], ) -> Vec<FxHashMap<PropertyKey, Value>>

Gets all properties for multiple nodes in a single batch operation.

Returns a vector of property maps, one per node ID (empty map if no properties). More efficient than calling Self::get_node in a loop.

Source

pub fn get_nodes_properties_selective_batch( &self, ids: &[NodeId], keys: &[PropertyKey], ) -> Vec<FxHashMap<PropertyKey, Value>>

Gets selected properties for multiple nodes (projection pushdown).

This is more efficient than Self::get_nodes_properties_batch when you only need a subset of properties. It only iterates the requested columns instead of all columns.

Use this for: Queries with explicit projections like RETURN n.name, n.age instead of RETURN n (which requires all properties).

§Example
use grafeo_core::graph::lpg::LpgStore;
use grafeo_common::types::{PropertyKey, Value};

let store = LpgStore::new();
let n1 = store.create_node(&["Person"]);
store.set_node_property(n1, "name", Value::from("Alice"));
store.set_node_property(n1, "age", Value::from(30i64));
store.set_node_property(n1, "email", Value::from("alice@example.com"));

// Only fetch name and age (faster than get_nodes_properties_batch)
let keys = vec![PropertyKey::new("name"), PropertyKey::new("age")];
let props = store.get_nodes_properties_selective_batch(&[n1], &keys);

assert_eq!(props[0].len(), 2); // Only name and age, not email
Source

pub fn get_edges_properties_selective_batch( &self, ids: &[EdgeId], keys: &[PropertyKey], ) -> Vec<FxHashMap<PropertyKey, Value>>

Gets selected properties for multiple edges (projection pushdown).

Edge-property version of Self::get_nodes_properties_selective_batch.

Source§

impl LpgStore

Source

pub fn add_label(&self, node_id: NodeId, label: &str) -> bool

Adds a label to a node.

Returns true if the label was added, false if the node doesn’t exist or already has the label.

Source

pub fn remove_label(&self, node_id: NodeId, label: &str) -> bool

Removes a label from a node.

Returns true if the label was removed, false if the node doesn’t exist or doesn’t have the label.

Source

pub fn nodes_by_label(&self, label: &str) -> Vec<NodeId>

Returns all nodes with a specific label.

Uses the label index for O(1) lookup per label. Returns a snapshot - concurrent modifications won’t affect the returned vector. Results are sorted by NodeId for deterministic iteration order.

Source

pub fn label_count(&self) -> usize

Returns the number of distinct labels in the store.

Source

pub fn property_key_count(&self) -> usize

Returns the number of distinct property keys in the store.

This counts unique property keys across both nodes and edges.

Source

pub fn edge_type_count(&self) -> usize

Returns the number of distinct edge types in the store.

Source

pub fn all_labels(&self) -> Vec<String>

Returns all label names in the database.

Source

pub fn all_edge_types(&self) -> Vec<String>

Returns all edge type names in the database.

Source

pub fn all_property_keys(&self) -> Vec<String>

Returns all property keys used in the database.

Source§

impl LpgStore

Source

pub fn find_nodes_in_range( &self, property: &str, min: Option<&Value>, max: Option<&Value>, min_inclusive: bool, max_inclusive: bool, ) -> Vec<NodeId>

Finds all nodes whose property value falls within a range.

Uses zone maps to skip the scan entirely when no values could possibly match. This is the primary building block for range predicates in query execution.

§Arguments
  • property - The property key to check
  • min - Optional lower bound
  • max - Optional upper bound
  • min_inclusive - Whether the lower bound is inclusive
  • max_inclusive - Whether the upper bound is inclusive
§Example
use grafeo_core::graph::lpg::LpgStore;
use grafeo_common::types::Value;

let store = LpgStore::new();
let n1 = store.create_node(&["Person"]);
let n2 = store.create_node(&["Person"]);
store.set_node_property(n1, "age", Value::from(25i64));
store.set_node_property(n2, "age", Value::from(35i64));

// Find nodes where age > 30
let result = store.find_nodes_in_range(
    "age",
    Some(&Value::from(30i64)),
    None,
    false, // exclusive lower bound
    true,  // inclusive upper bound (doesn't matter since None)
);
assert_eq!(result.len(), 1); // Only n2 matches
Source

pub fn find_nodes_by_properties( &self, conditions: &[(&str, Value)], ) -> Vec<NodeId>

Finds nodes matching multiple property equality conditions.

This is more efficient than intersecting multiple single-property lookups because it can use indexes when available and short-circuits on the first miss.

§Example
use grafeo_core::graph::lpg::LpgStore;
use grafeo_common::types::Value;

let store = LpgStore::new();
let alice = store.create_node(&["Person"]);
store.set_node_property(alice, "name", Value::from("Alice"));
store.set_node_property(alice, "city", Value::from("NYC"));

// Find nodes where name = "Alice" AND city = "NYC"
let matches = store.find_nodes_by_properties(&[
    ("name", Value::from("Alice")),
    ("city", Value::from("NYC")),
]);
assert!(matches.contains(&alice));
Source

pub fn find_nodes_by_property( &self, property: &str, value: &Value, ) -> Vec<NodeId>

Finds all nodes that have a specific property value.

If the property is indexed, this is O(1). Otherwise, it scans all nodes which is O(n). Use Self::create_property_index for frequently queried properties.

§Example
use grafeo_core::graph::lpg::LpgStore;
use grafeo_common::types::Value;

let store = LpgStore::new();
store.create_property_index("city"); // Optional but makes lookups fast

let alice = store.create_node(&["Person"]);
let bob = store.create_node(&["Person"]);
store.set_node_property(alice, "city", Value::from("NYC"));
store.set_node_property(bob, "city", Value::from("NYC"));

let nyc_people = store.find_nodes_by_property("city", &Value::from("NYC"));
assert_eq!(nyc_people.len(), 2);
Source

pub fn find_nodes_matching_filter( &self, property: &str, filter_value: &Value, ) -> Vec<NodeId>

Finds nodes whose property matches an operator filter.

The filter_value is either a scalar (equality) or a Value::Map with $-prefixed operator keys like $gt, $lt, $gte, $lte, $in, $nin, $ne, $contains.

Source

pub fn node_property_might_match( &self, property: &PropertyKey, op: CompareOp, value: &Value, ) -> bool

Checks if a node property predicate might match any nodes.

Uses zone maps for early filtering. Returns true if there might be matching nodes, false if there definitely aren’t.

Source

pub fn edge_property_might_match( &self, property: &PropertyKey, op: CompareOp, value: &Value, ) -> bool

Checks if an edge property predicate might match any edges.

Source

pub fn node_property_zone_map( &self, property: &PropertyKey, ) -> Option<ZoneMapEntry>

Gets the zone map for a node property.

Source

pub fn edge_property_zone_map( &self, property: &PropertyKey, ) -> Option<ZoneMapEntry>

Gets the zone map for an edge property.

Source§

impl LpgStore

Source

pub fn statistics(&self) -> Arc<Statistics>

Returns the current statistics (cheap Arc clone, no deep copy).

Source

pub fn estimate_label_cardinality(&self, label: &str) -> f64

Estimates cardinality for a label scan.

Source

pub fn estimate_avg_degree(&self, edge_type: &str, outgoing: bool) -> f64

Estimates average degree for an edge type.

Source§

impl LpgStore

Source

pub fn neighbors( &self, node: NodeId, direction: Direction, ) -> impl Iterator<Item = NodeId> + '_

Iterates over neighbors of a node in the specified direction.

This is the fast path for graph traversal - goes straight to the adjacency index without loading full node data.

Source

pub fn edges_from( &self, node: NodeId, direction: Direction, ) -> impl Iterator<Item = (NodeId, EdgeId)> + '_

Returns edges from a node with their targets.

Returns an iterator of (target_node, edge_id) pairs.

Source

pub fn edges_to(&self, node: NodeId) -> Vec<(NodeId, EdgeId)>

Returns edges to a node (where the node is the destination).

Returns (source_node, edge_id) pairs for all edges pointing TO this node. Uses the backward adjacency index for O(degree) lookup.

§Example
let store = LpgStore::new();
let a = store.create_node(&["Node"]);
let b = store.create_node(&["Node"]);
let c = store.create_node(&["Node"]);
let _e1 = store.create_edge(a, b, "LINKS");
let _e2 = store.create_edge(c, b, "LINKS");

// For edges: A->B, C->B
let incoming = store.edges_to(b);
assert_eq!(incoming.len(), 2);
Source

pub fn out_degree(&self, node: NodeId) -> usize

Returns the out-degree of a node (number of outgoing edges).

Uses the forward adjacency index for O(1) lookup.

Source

pub fn in_degree(&self, node: NodeId) -> usize

Returns the in-degree of a node (number of incoming edges).

Uses the backward adjacency index for O(1) lookup if available, otherwise falls back to scanning edges.

Source

pub fn all_nodes(&self) -> impl Iterator<Item = Node> + '_

Returns an iterator over all nodes in the database.

This creates a snapshot of all visible nodes at the current epoch. Useful for dump/export operations.

Source

pub fn all_edges(&self) -> impl Iterator<Item = Edge> + '_

Returns an iterator over all edges in the database.

This creates a snapshot of all visible edges at the current epoch. Useful for dump/export operations.

Source

pub fn nodes_with_label<'a>( &'a self, label: &str, ) -> impl Iterator<Item = Node> + 'a

Returns an iterator over nodes with a specific label.

Source

pub fn edges_with_type<'a>( &'a self, edge_type: &str, ) -> impl Iterator<Item = Edge> + 'a

Returns an iterator over edges with a specific type.

Source§

impl LpgStore

Source

pub fn new() -> Self

Creates a new LPG store with default configuration.

Source

pub fn with_config(config: LpgStoreConfig) -> Self

Creates a new LPG store with custom configuration.

Source

pub fn current_epoch(&self) -> EpochId

Returns the current epoch.

Trait Implementations§

Source§

impl Default for LpgStore

Source§

fn default() -> Self

Returns the “default value” for a type. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> if into_left is true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> if into_left(&self) returns true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

impl<T> Pointable for T

Source§

const ALIGN: usize

The alignment of pointer.
Source§

type Init = T

The type for initializers.
Source§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
Source§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
Source§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
Source§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.