Skip to main content

fathomdb_query/
ast.rs

1use crate::TextQuery;
2
3/// Abstract syntax tree representing a graph query.
4#[derive(Clone, Debug, PartialEq, Eq)]
5pub struct QueryAst {
6    /// Node kind used as the root of the query.
7    pub root_kind: String,
8    /// Ordered pipeline of search, traversal, and filter steps.
9    pub steps: Vec<QueryStep>,
10    /// Named expansion slots evaluated per root result in grouped queries.
11    pub expansions: Vec<ExpansionSlot>,
12    /// Named edge-projecting expansion slots evaluated per root result in
13    /// grouped queries. Sibling to `expansions`; vec membership is the
14    /// discriminator between node- and edge-expansion slots. Slot names
15    /// must be unique across both vecs.
16    pub edge_expansions: Vec<EdgeExpansionSlot>,
17    /// Optional hard cap on the number of result rows.
18    pub final_limit: Option<usize>,
19}
20
21/// An edge-projecting expansion slot.
22///
23/// Emits `(EdgeRow, NodeRow)` tuples per root on execution. The endpoint
24/// node is the target on `Out` traversal, source on `In`. For
25/// `max_depth > 1`, each emitted tuple reflects the final-hop edge
26/// leading to the emitted endpoint node.
27#[derive(Clone, Debug, PartialEq, Eq)]
28pub struct EdgeExpansionSlot {
29    /// Slot name used to key the expansion results. Must be unique across
30    /// both node-expansion and edge-expansion slots in the same query.
31    pub slot: String,
32    /// Direction to traverse edges.
33    pub direction: TraverseDirection,
34    /// Edge kind (label) to follow.
35    pub label: String,
36    /// Maximum traversal depth.
37    pub max_depth: usize,
38    /// Optional predicate filtering the endpoint node (the target side on
39    /// `Out`, the source side on `In`). Reuses the `Predicate` enum.
40    pub endpoint_filter: Option<Predicate>,
41    /// Optional predicate filtering the traversed edges. Only
42    /// `EdgePropertyEq` and `EdgePropertyCompare` are valid here.
43    pub edge_filter: Option<Predicate>,
44}
45
46/// A named expansion slot that traverses edges per root result.
47#[derive(Clone, Debug, PartialEq, Eq)]
48pub struct ExpansionSlot {
49    /// Slot name used to key the expansion results.
50    pub slot: String,
51    /// Direction to traverse edges.
52    pub direction: TraverseDirection,
53    /// Edge kind (label) to follow.
54    pub label: String,
55    /// Maximum traversal depth.
56    pub max_depth: usize,
57    /// Optional predicate to filter target nodes in this expansion slot.
58    /// `None` is exactly equivalent to pre-Pack-2 behavior.
59    /// `Some(_)` is not yet implemented; see Pack 3.
60    pub filter: Option<Predicate>,
61    /// Optional predicate to filter the traversed edges in this expansion slot.
62    /// Only `EdgePropertyEq` and `EdgePropertyCompare` are valid here.
63    /// `None` preserves pre-Pack-D behavior (no edge filtering).
64    pub edge_filter: Option<Predicate>,
65}
66
67/// A single step in the query pipeline.
68#[derive(Clone, Debug, PartialEq, Eq)]
69pub enum QueryStep {
70    /// Unified adaptive retrieval entry step consumed by the Phase 12
71    /// retrieval planner.
72    ///
73    /// Carries the caller's raw query string (not a parsed [`TextQuery`]):
74    /// the planner decides how to interpret and route it across the text
75    /// strict, text relaxed, and (future) vector branches. See
76    /// `crate::compile_retrieval_plan` for the planner entry point.
77    Search {
78        /// The raw caller-supplied query string.
79        query: String,
80        /// Maximum number of candidate rows requested by the caller.
81        limit: usize,
82    },
83    /// Nearest-neighbor search over vector embeddings.
84    VectorSearch {
85        /// The search query text (to be embedded by the caller).
86        query: String,
87        /// Maximum number of candidate rows from the vector index.
88        limit: usize,
89    },
90    /// Full-text search over indexed chunk content using `FathomDB`'s supported
91    /// safe text-query subset.
92    TextSearch {
93        /// Parsed text-search intent to be lowered into safe FTS5 syntax.
94        query: TextQuery,
95        /// Maximum number of candidate rows from the FTS index.
96        limit: usize,
97    },
98    /// Graph traversal following edges of the given label.
99    Traverse {
100        /// Direction to traverse.
101        direction: TraverseDirection,
102        /// Edge kind to follow.
103        label: String,
104        /// Maximum hops from each candidate.
105        max_depth: usize,
106        /// Optional predicate to filter traversal results.
107        /// `None` is exactly equivalent to the pre-Pack-2 behavior.
108        /// `Some(_)` is not yet implemented; see Pack 3.
109        filter: Option<Predicate>,
110    },
111    /// Row-level filter predicate.
112    Filter(Predicate),
113}
114
115/// A filter predicate applied to candidate nodes.
116#[derive(Clone, Debug, PartialEq, Eq)]
117pub enum Predicate {
118    /// Match nodes with the exact logical ID.
119    LogicalIdEq(String),
120    /// Match nodes with the exact kind.
121    KindEq(String),
122    /// Equality check on a JSON property at the given path.
123    JsonPathEq {
124        /// JSON path expression (e.g. `$.status`).
125        path: String,
126        /// Value to compare against.
127        value: ScalarValue,
128    },
129    /// Ordered comparison on a JSON property at the given path.
130    JsonPathCompare {
131        /// JSON path expression.
132        path: String,
133        /// Comparison operator.
134        op: ComparisonOp,
135        /// Value to compare against.
136        value: ScalarValue,
137    },
138    /// Match nodes with the exact `source_ref`.
139    SourceRefEq(String),
140    /// Match nodes where `content_ref` is not NULL (i.e. content proxy nodes).
141    ContentRefNotNull,
142    /// Match nodes with the exact `content_ref` URI.
143    ContentRefEq(String),
144    /// Fused equality check on a JSON text property at the given path.
145    ///
146    /// Unlike [`Predicate::JsonPathEq`], this variant is classified as
147    /// **fusable** by [`crate::fusion::is_fusable`] and is pushed into
148    /// the search CTE's inner `WHERE` clause so the CTE `LIMIT` applies
149    /// after the predicate runs. The caller opts into fusion by
150    /// registering an FTS property schema that covers the path; the
151    /// tethered builder enforces that gate at filter-add time.
152    JsonPathFusedEq {
153        /// JSON path expression (e.g. `$.status`).
154        path: String,
155        /// Text value to compare against.
156        value: String,
157    },
158    /// Fused ordered comparison on a JSON integer/timestamp property at
159    /// the given path. See [`Predicate::JsonPathFusedEq`] for the fusion
160    /// contract.
161    JsonPathFusedTimestampCmp {
162        /// JSON path expression.
163        path: String,
164        /// Comparison operator.
165        op: ComparisonOp,
166        /// Integer value to compare against (epoch seconds for
167        /// timestamp semantics).
168        value: i64,
169    },
170    /// Fused equality check on a JSON boolean property at the given path.
171    /// See [`Predicate::JsonPathFusedEq`] for the fusion contract.
172    /// The boolean is stored as `SQLite` integer 1/0.
173    JsonPathFusedBoolEq {
174        /// JSON path expression (e.g. `$.resolved`).
175        path: String,
176        /// Boolean value to compare against (stored as 1 or 0).
177        value: bool,
178    },
179    /// Equality check on a JSON property of the traversed edge at the given path.
180    ///
181    /// Structurally identical to [`Predicate::JsonPathEq`] but targets
182    /// `e.properties` on the edge row rather than `n.properties` on the
183    /// target node. Only valid inside an expansion slot's `edge_filter`.
184    EdgePropertyEq {
185        /// JSON path expression (e.g. `$.rel`).
186        path: String,
187        /// Value to compare against.
188        value: ScalarValue,
189    },
190    /// Ordered comparison on a JSON property of the traversed edge at the given path.
191    ///
192    /// Structurally identical to [`Predicate::JsonPathCompare`] but targets
193    /// `e.properties` on the edge row rather than `n.properties` on the
194    /// target node. Only valid inside an expansion slot's `edge_filter`.
195    EdgePropertyCompare {
196        /// JSON path expression.
197        path: String,
198        /// Comparison operator.
199        op: ComparisonOp,
200        /// Value to compare against.
201        value: ScalarValue,
202    },
203    /// Fused IN-set check on a JSON text property at the given path.
204    ///
205    /// Like [`Predicate::JsonPathFusedEq`], this variant is classified as
206    /// **fusable** and is pushed into the search CTE's inner `WHERE` clause.
207    /// The caller must have a registered FTS property schema for the path.
208    JsonPathFusedIn {
209        /// JSON path expression (e.g. `$.status`).
210        path: String,
211        /// Non-empty set of text values; the node must match at least one.
212        values: Vec<String>,
213    },
214    /// IN-set check on a JSON property at the given path.
215    ///
216    /// Unlike [`Predicate::JsonPathFusedIn`], this variant is **not** fusable
217    /// and is applied as a residual WHERE clause on the Nodes driver scan.
218    JsonPathIn {
219        /// JSON path expression (e.g. `$.category`).
220        path: String,
221        /// Non-empty set of values; the node must match at least one.
222        values: Vec<ScalarValue>,
223    },
224}
225
226/// Ordered comparison operator for JSON property filters.
227#[derive(Clone, Copy, Debug, PartialEq, Eq)]
228pub enum ComparisonOp {
229    /// Greater than.
230    Gt,
231    /// Greater than or equal.
232    Gte,
233    /// Less than.
234    Lt,
235    /// Less than or equal.
236    Lte,
237}
238
239/// A typed scalar value used in query predicates.
240#[derive(Clone, Debug, PartialEq, Eq)]
241pub enum ScalarValue {
242    /// A UTF-8 text value.
243    Text(String),
244    /// A 64-bit signed integer.
245    Integer(i64),
246    /// A boolean value.
247    Bool(bool),
248}
249
250/// Direction for graph traversal steps and expansion slots.
251#[derive(Clone, Copy, Debug, PartialEq, Eq)]
252pub enum TraverseDirection {
253    /// Follow edges pointing toward the current node.
254    In,
255    /// Follow edges pointing away from the current node.
256    Out,
257}