pub struct PgGraphMemory<N, E> { /* private fields */ }Expand description
Postgres-backed GraphMemory<N, E>.
Cheap to clone — internal state is an Arc<PgPool> plus two
owned table-name strings.
Implementations§
Source§impl<N, E> PgGraphMemory<N, E>
impl<N, E> PgGraphMemory<N, E>
Sourcepub fn builder() -> PgGraphMemoryBuilder<N, E>
pub fn builder() -> PgGraphMemoryBuilder<N, E>
Start a fluent builder. connection_string is the only
required field; everything else has a sensible default.
Sourcepub async fn list_nodes(
&self,
ns: &Namespace,
limit: usize,
offset: usize,
) -> Result<Vec<NodeId>>
pub async fn list_nodes( &self, ns: &Namespace, limit: usize, offset: usize, ) -> Result<Vec<NodeId>>
Paginated node-id enumeration, ascending by id (UUID v7 mint-time order). Operator-side admin / migration path.
Sourcepub async fn list_node_records(
&self,
ns: &Namespace,
limit: usize,
offset: usize,
) -> Result<Vec<(NodeId, N)>>where
N: DeserializeOwned,
pub async fn list_node_records(
&self,
ns: &Namespace,
limit: usize,
offset: usize,
) -> Result<Vec<(NodeId, N)>>where
N: DeserializeOwned,
Paginated (NodeId, N) enumeration — single round-trip
versus list_nodes + per-id node(). Operator-side
bulk-export path.
Sourcepub async fn list_edges(
&self,
ns: &Namespace,
limit: usize,
offset: usize,
) -> Result<Vec<EdgeId>>
pub async fn list_edges( &self, ns: &Namespace, limit: usize, offset: usize, ) -> Result<Vec<EdgeId>>
Paginated edge-id enumeration. Operator-side migration path.
Sourcepub async fn list_edge_records(
&self,
ns: &Namespace,
limit: usize,
offset: usize,
) -> Result<Vec<GraphHop<E>>>where
E: DeserializeOwned,
pub async fn list_edge_records(
&self,
ns: &Namespace,
limit: usize,
offset: usize,
) -> Result<Vec<GraphHop<E>>>where
E: DeserializeOwned,
Paginated GraphHop<E> enumeration — full structural body
in one round-trip. Operator-side bulk-export path.
Sourcepub async fn prune_orphan_nodes(&self, ns: &Namespace) -> Result<usize>
pub async fn prune_orphan_nodes(&self, ns: &Namespace) -> Result<usize>
Drop every node with no incident edge — single SQL anti-join
against the edges table. Two-phase prune companion to
prune_older_than: the edge sweep leaves orphans that this
call cleans up. Operator-side admin path.
Trait Implementations§
Source§impl<N, E> Clone for PgGraphMemory<N, E>
impl<N, E> Clone for PgGraphMemory<N, E>
Source§impl<N, E> Debug for PgGraphMemory<N, E>
impl<N, E> Debug for PgGraphMemory<N, E>
Source§impl<N, E> GraphMemory<N, E> for PgGraphMemory<N, E>where
N: Clone + Send + Sync + Serialize + DeserializeOwned + 'static,
E: Clone + Send + Sync + Serialize + DeserializeOwned + 'static,
impl<N, E> GraphMemory<N, E> for PgGraphMemory<N, E>where
N: Clone + Send + Sync + Serialize + DeserializeOwned + 'static,
E: Clone + Send + Sync + Serialize + DeserializeOwned + 'static,
Source§fn add_node<'life0, 'life1, 'life2, 'async_trait>(
&'life0 self,
_ctx: &'life1 ExecutionContext,
ns: &'life2 Namespace,
node: N,
) -> Pin<Box<dyn Future<Output = Result<NodeId>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
fn add_node<'life0, 'life1, 'life2, 'async_trait>(
&'life0 self,
_ctx: &'life1 ExecutionContext,
ns: &'life2 Namespace,
node: N,
) -> Pin<Box<dyn Future<Output = Result<NodeId>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
node and return its assigned id.Source§fn add_edge<'life0, 'life1, 'life2, 'life3, 'life4, 'async_trait>(
&'life0 self,
_ctx: &'life1 ExecutionContext,
ns: &'life2 Namespace,
from: &'life3 NodeId,
to: &'life4 NodeId,
edge: E,
timestamp: DateTime<Utc>,
) -> Pin<Box<dyn Future<Output = Result<EdgeId>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
'life3: 'async_trait,
'life4: 'async_trait,
fn add_edge<'life0, 'life1, 'life2, 'life3, 'life4, 'async_trait>(
&'life0 self,
_ctx: &'life1 ExecutionContext,
ns: &'life2 Namespace,
from: &'life3 NodeId,
to: &'life4 NodeId,
edge: E,
timestamp: DateTime<Utc>,
) -> Pin<Box<dyn Future<Output = Result<EdgeId>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
'life3: 'async_trait,
'life4: 'async_trait,
from to to carrying edge.
timestamp is supplied by the caller so re-inserting after
a replay produces deterministic edges.Source§fn add_edges_batch<'life0, 'life1, 'life2, 'async_trait>(
&'life0 self,
_ctx: &'life1 ExecutionContext,
ns: &'life2 Namespace,
edges: Vec<(NodeId, NodeId, E, DateTime<Utc>)>,
) -> Pin<Box<dyn Future<Output = Result<Vec<EdgeId>>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
fn add_edges_batch<'life0, 'life1, 'life2, 'async_trait>(
&'life0 self,
_ctx: &'life1 ExecutionContext,
ns: &'life2 Namespace,
edges: Vec<(NodeId, NodeId, E, DateTime<Utc>)>,
) -> Pin<Box<dyn Future<Output = Result<Vec<EdgeId>>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
(from, to, edge, timestamp); endpoints must already exist
(same contract as Self::add_edge). Returns the assigned
EdgeIds in input order. Read moreSource§fn get_node<'life0, 'life1, 'life2, 'life3, 'async_trait>(
&'life0 self,
_ctx: &'life1 ExecutionContext,
ns: &'life2 Namespace,
id: &'life3 NodeId,
) -> Pin<Box<dyn Future<Output = Result<Option<N>>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
'life3: 'async_trait,
fn get_node<'life0, 'life1, 'life2, 'life3, 'async_trait>(
&'life0 self,
_ctx: &'life1 ExecutionContext,
ns: &'life2 Namespace,
id: &'life3 NodeId,
) -> Pin<Box<dyn Future<Output = Result<Option<N>>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
'life3: 'async_trait,
get per
.claude/rules/naming.md — single-item primary-key
lookup).Source§fn get_edge<'life0, 'life1, 'life2, 'life3, 'async_trait>(
&'life0 self,
_ctx: &'life1 ExecutionContext,
ns: &'life2 Namespace,
edge_id: &'life3 EdgeId,
) -> Pin<Box<dyn Future<Output = Result<Option<GraphHop<E>>>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
'life3: 'async_trait,
fn get_edge<'life0, 'life1, 'life2, 'life3, 'async_trait>(
&'life0 self,
_ctx: &'life1 ExecutionContext,
ns: &'life2 Namespace,
edge_id: &'life3 EdgeId,
) -> Pin<Box<dyn Future<Output = Result<Option<GraphHop<E>>>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
'life3: 'async_trait,
GraphHop<E> — from, to, edge, timestamp).
Operators rarely want the payload alone for edges; the
endpoints and timestamp are usually load-bearing for any
follow-up decision (audit context, freshness check,
neighbour navigation). Returning the full hop saves a
second lookup. Read moreSource§fn neighbors<'life0, 'life1, 'life2, 'life3, 'async_trait>(
&'life0 self,
_ctx: &'life1 ExecutionContext,
ns: &'life2 Namespace,
node: &'life3 NodeId,
direction: Direction,
) -> Pin<Box<dyn Future<Output = Result<Vec<(EdgeId, NodeId, E)>>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
'life3: 'async_trait,
fn neighbors<'life0, 'life1, 'life2, 'life3, 'async_trait>(
&'life0 self,
_ctx: &'life1 ExecutionContext,
ns: &'life2 Namespace,
node: &'life3 NodeId,
direction: Direction,
) -> Pin<Box<dyn Future<Output = Result<Vec<(EdgeId, NodeId, E)>>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
'life3: 'async_trait,
node in the requested direction. Each
triple is (EdgeId, neighbour NodeId, edge payload).Source§fn traverse<'life0, 'life1, 'life2, 'life3, 'async_trait>(
&'life0 self,
_ctx: &'life1 ExecutionContext,
ns: &'life2 Namespace,
start: &'life3 NodeId,
direction: Direction,
max_depth: usize,
) -> Pin<Box<dyn Future<Output = Result<Vec<GraphHop<E>>>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
'life3: 'async_trait,
fn traverse<'life0, 'life1, 'life2, 'life3, 'async_trait>(
&'life0 self,
_ctx: &'life1 ExecutionContext,
ns: &'life2 Namespace,
start: &'life3 NodeId,
direction: Direction,
max_depth: usize,
) -> Pin<Box<dyn Future<Output = Result<Vec<GraphHop<E>>>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
'life3: 'async_trait,
start, expanding up to
max_depth hops along edges in the requested direction.
Returns the visited hops in BFS order (excluding the seed
node, which has no inbound edge in this traversal). Use
Direction::Both for relationship-graph queries that
don’t care about edge polarity (knowledge graphs typically
want this).Source§fn find_path<'life0, 'life1, 'life2, 'life3, 'life4, 'async_trait>(
&'life0 self,
_ctx: &'life1 ExecutionContext,
ns: &'life2 Namespace,
from: &'life3 NodeId,
to: &'life4 NodeId,
direction: Direction,
max_depth: usize,
) -> Pin<Box<dyn Future<Output = Result<Option<Vec<GraphHop<E>>>>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
'life3: 'async_trait,
'life4: 'async_trait,
fn find_path<'life0, 'life1, 'life2, 'life3, 'life4, 'async_trait>(
&'life0 self,
_ctx: &'life1 ExecutionContext,
ns: &'life2 Namespace,
from: &'life3 NodeId,
to: &'life4 NodeId,
direction: Direction,
max_depth: usize,
) -> Pin<Box<dyn Future<Output = Result<Option<Vec<GraphHop<E>>>>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
'life3: 'async_trait,
'life4: 'async_trait,
from to to (BFS) along
edges in the requested direction. Returns the sequence of
hops; Some(vec![]) means from == to (already at
destination — no edges traversed); None means no path
exists within max_depth hops.Source§fn temporal_filter<'life0, 'life1, 'life2, 'async_trait>(
&'life0 self,
_ctx: &'life1 ExecutionContext,
ns: &'life2 Namespace,
from: DateTime<Utc>,
to: DateTime<Utc>,
) -> Pin<Box<dyn Future<Output = Result<Vec<GraphHop<E>>>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
fn temporal_filter<'life0, 'life1, 'life2, 'async_trait>(
&'life0 self,
_ctx: &'life1 ExecutionContext,
ns: &'life2 Namespace,
from: DateTime<Utc>,
to: DateTime<Utc>,
) -> Pin<Box<dyn Future<Output = Result<Vec<GraphHop<E>>>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
[from, to). Useful for
audit-log style queries (“what relationships did the agent
learn last week”).Source§fn node_count<'life0, 'life1, 'life2, 'async_trait>(
&'life0 self,
_ctx: &'life1 ExecutionContext,
ns: &'life2 Namespace,
) -> Pin<Box<dyn Future<Output = Result<usize>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
fn node_count<'life0, 'life1, 'life2, 'async_trait>(
&'life0 self,
_ctx: &'life1 ExecutionContext,
ns: &'life2 Namespace,
) -> Pin<Box<dyn Future<Output = Result<usize>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
ns. Cheap operator metric for
size-based decisions (paginate vs stream, fast-fail
empty-namespace check, audit / dashboard surface).
Default impl returns 0.Source§fn edge_count<'life0, 'life1, 'life2, 'async_trait>(
&'life0 self,
_ctx: &'life1 ExecutionContext,
ns: &'life2 Namespace,
) -> Pin<Box<dyn Future<Output = Result<usize>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
fn edge_count<'life0, 'life1, 'life2, 'async_trait>(
&'life0 self,
_ctx: &'life1 ExecutionContext,
ns: &'life2 Namespace,
) -> Pin<Box<dyn Future<Output = Result<usize>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
ns. Cheap operator metric — same
rationale as Self::node_count. Default impl returns
0.Source§fn delete_edge<'life0, 'life1, 'life2, 'life3, 'async_trait>(
&'life0 self,
_ctx: &'life1 ExecutionContext,
ns: &'life2 Namespace,
edge_id: &'life3 EdgeId,
) -> Pin<Box<dyn Future<Output = Result<()>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
'life3: 'async_trait,
fn delete_edge<'life0, 'life1, 'life2, 'life3, 'async_trait>(
&'life0 self,
_ctx: &'life1 ExecutionContext,
ns: &'life2 Namespace,
edge_id: &'life3 EdgeId,
) -> Pin<Box<dyn Future<Output = Result<()>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
'life3: 'async_trait,
Source§fn delete_node<'life0, 'life1, 'life2, 'life3, 'async_trait>(
&'life0 self,
_ctx: &'life1 ExecutionContext,
ns: &'life2 Namespace,
node_id: &'life3 NodeId,
) -> Pin<Box<dyn Future<Output = Result<usize>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
'life3: 'async_trait,
fn delete_node<'life0, 'life1, 'life2, 'life3, 'async_trait>(
&'life0 self,
_ctx: &'life1 ExecutionContext,
ns: &'life2 Namespace,
node_id: &'life3 NodeId,
) -> Pin<Box<dyn Future<Output = Result<usize>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
'life3: 'async_trait,
Self::delete_edge and
then call this. Returns the count of removed edges so
callers can log or expose cleanup metrics; 0 when the
node had no edges (or was absent — the operation is
idempotent). Read moreSource§fn prune_older_than<'life0, 'life1, 'life2, 'async_trait>(
&'life0 self,
_ctx: &'life1 ExecutionContext,
ns: &'life2 Namespace,
ttl: Duration,
) -> Pin<Box<dyn Future<Output = Result<usize>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
fn prune_older_than<'life0, 'life1, 'life2, 'async_trait>(
&'life0 self,
_ctx: &'life1 ExecutionContext,
ns: &'life2 Namespace,
ttl: Duration,
) -> Pin<Box<dyn Future<Output = Result<usize>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
ns whose timestamp is older than
ttl ago. Returns the count of removed edges so callers
can log or expose pruning metrics. Read moreAuto Trait Implementations§
impl<N, E> Freeze for PgGraphMemory<N, E>
impl<N, E> !RefUnwindSafe for PgGraphMemory<N, E>
impl<N, E> Send for PgGraphMemory<N, E>
impl<N, E> Sync for PgGraphMemory<N, E>
impl<N, E> Unpin for PgGraphMemory<N, E>
impl<N, E> UnsafeUnpin for PgGraphMemory<N, E>
impl<N, E> !UnwindSafe for PgGraphMemory<N, E>
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
Source§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
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 moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
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