Skip to main content

TraversalBuilder

Struct TraversalBuilder 

Source
pub struct TraversalBuilder {
    pub start_node: String,
    pub max_depth: usize,
    pub edge_types: Vec<String>,
    pub min_weight: f64,
    pub attribute_mode: Option<AttributeMode>,
    pub as_of: Option<String>,
}
Expand description

Recursive CTE traversal query builder (§5.2).

Fields§

§start_node: String§max_depth: usize§edge_types: Vec<String>§min_weight: f64§attribute_mode: Option<AttributeMode>

None means defaulted, not Current (T3.2, D-085).

The distinction is the whole mechanism. Current chosen by a caller who knows what it means is a legitimate, fast answer; Current arrived at by never touching the setting, on a query about the past, is a wrong answer nobody asked for. Those two produce identical behaviour and must not be stored identically, so the field records which happened.

Public for construction-by-struct-literal, which is why it is an Option here rather than a private bool beside the mode: a caller building the struct directly should have to write down the same thing the builder method records.

§as_of: Option<String>

The valid-time instant to traverse at, if it is not the present.

Added in 0.6.0 so “as of Tuesday” is expressible. Before this, the instant arrived as execute’s now_ts parameter and a historical traversal was indistinguishable from a live one — which is why the mismatch with AttributeMode::Current could only ever be a warn!: nothing in the call had the information needed to raise an error.

Implementations§

Source§

impl TraversalBuilder

Source

pub fn new(start_node: impl Into<String>) -> Self

Source

pub fn max_depth(self, depth: usize) -> Self

Source

pub fn edge_types(self, types: Vec<String>) -> Self

Source

pub fn min_weight(self, weight: f64) -> Self

Source

pub fn attribute_mode(self, mode: AttributeMode) -> Self

State the attribute mode explicitly.

Calling this is what turns Current from a default into a decision, and Self::execute treats the two differently on a historical traversal — see Self::as_of.

Source

pub fn as_of(self, ts: impl Into<String>) -> Self

Traverse the graph as it was at ts rather than at the present (§5.2).

§Setting this makes the attribute mode a required decision (T3.2, D-085)

A historical traversal has two independent temporal questions, and until 0.6.0 only one of them was asked. The topology comes from ts. The node attributes — titles, content — come from wherever AttributeMode says, and the default said Current, which is live text. So as_of(Tuesday) returned Tuesday’s graph wearing today’s titles, and reported that through a tracing::warn! — invisible in any application that has not configured a subscriber, which is most of them at first run.

So: with as_of set and no Self::attribute_mode call, Self::execute returns [DbError::AttributeModeUnstated] rather than guessing. Both answers stay available and neither is silent:

// Tuesday's graph with Tuesday's titles — usually what was meant.
let then = TraversalBuilder::new("a")
    .as_of("2026-01-06T00:00:00.000000Z")
    .attribute_mode(AttributeMode::AtTime)
    .execute(conn, now)
    .await?;

// Tuesday's topology with today's titles — legitimate, and now stated.
let mixed = TraversalBuilder::new("a")
    .as_of("2026-01-06T00:00:00.000000Z")
    .attribute_mode(AttributeMode::Current)
    .execute(conn, now)
    .await?;

A traversal with no as_of is a query about now, where Current and AtTime agree about which text to return, so the default stands and no caller has to change.

Source

pub fn build_sql(&self) -> String

Compile the recursive CTE query string as specified in §5.2.

Edge types become bind placeholders, not quoted literals. An earlier version spliced them in with format!("'{t}'"), which made any caller string a SQL fragment on the read path — and the only validation in the crate, super::edge::validate_edge_type, runs in super::EdgeAssertion::normalized on the write path, so a traversal never passed through it. Binding removes the question rather than answering it: unlike a table name, an edge type is a value, and values can be parameters.

Source

pub async fn execute_ids( &self, conn: &Connection, now_ts: &str, ) -> Result<Vec<String>>

Node ids reachable under this traversal, in id order (§5.2).

Reads at Self::as_of when set, else at now_ts. No attribute mode is involved, so this never returns [DbError::AttributeModeUnstated]: topology at an instant is unambiguous, and it is only the pairing with live attributes that needed a decision.

Source

pub async fn execute( &self, conn: &Connection, now_ts: &str, ) -> Result<Vec<NodeAttributes>>

Execute the traversal and hydrate attributes per Self::attribute_mode (§5.2).

The hydration is a second step rather than a join in the CTE because the three modes read from two different places: Current and Omit from concepts, AtTime from transaction_log. The previous version always emitted the concepts join, so attribute_mode was stored, exposed by a builder method, and never read — a caller asking for AtTime got live attributes with no indication that the mode had been ignored. That is the exact failure Doctrine II exists to prevent, arriving as a silent wrong answer rather than as an error.

AttributeMode::Omit returns Ok(vec![]) here, which is indistinguishable from a traversal that reached nothing. That is a limitation of this method’s return type rather than of the mode; callers wanting topology only should use Self::execute_ids, which says what it found.

§Errors

[DbError::AttributeModeUnstated] when Self::as_of is set and Self::attribute_mode is not — see as_of for why that combination is a question rather than a default (T3.2, D-085).

now_ts is the caller’s present. A traversal with as_of set reads topology at that instant instead; now_ts is still what an AttributeMode::Current hydrate means by “current”.

Trait Implementations§

Source§

impl Clone for TraversalBuilder

Source§

fn clone(&self) -> TraversalBuilder

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for TraversalBuilder

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. 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<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> FromRef<T> for T
where T: Clone,

Source§

fn from_ref(input: &T) -> T

Converts to this type from a reference to the input type.
Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
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> IntoRequest<T> for T

Source§

fn into_request(self) -> Request<T>

Wrap the input message T in a tonic::Request
Source§

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. 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.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V

Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more