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