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}