1#[derive(Debug, Clone, PartialEq, Eq)]
2pub struct Version {
3 pub major: u32,
4 pub minor: u32,
5 pub patch: u32,
6 pub producer: String,
7}
8
9pub const PLAN_FORMAT_MAJOR: u32 = 0;
10pub const PLAN_FORMAT_MINOR: u32 = 3;
11pub const PLAN_FORMAT_PATCH: u32 = 0;
12
13pub fn current_plan_version(producer: impl Into<String>) -> Version {
14 Version {
15 major: PLAN_FORMAT_MAJOR,
16 minor: PLAN_FORMAT_MINOR,
17 patch: PLAN_FORMAT_PATCH,
18 producer: producer.into(),
19 }
20}
21
22#[derive(Debug, Clone, Copy, PartialEq, Eq)]
23pub enum ColKind {
24 Node,
25 Rel,
26 Value,
27 Bool,
28 Int64,
29 Float64,
30 String,
31 Null,
32 List,
33 Map,
34 Path,
35 Graph,
36 Vector,
37}
38
39#[derive(Debug, Clone, PartialEq, Eq, Default)]
42pub enum LogicalType {
43 #[default]
44 Unknown,
45 Bool,
46 Int64,
47 Float64,
48 String,
49 Null,
50 List,
51 Map,
52 NodeRef,
53 RelRef,
54 Path,
55 Vector {
56 element_type: std::string::String,
57 dimension: Option<u32>,
58 },
59}
60
61impl LogicalType {
62 pub fn from_legacy_str(s: &str) -> Self {
64 match s {
65 "bool" => Self::Bool,
66 "int64" => Self::Int64,
67 "float64" => Self::Float64,
68 "string" => Self::String,
69 "null" => Self::Null,
70 "map" => Self::Map,
71 "node" | "node_ref" => Self::NodeRef,
72 "rel" | "rel_ref" => Self::RelRef,
73 "path" => Self::Path,
74 _ if s.starts_with("list") => Self::List,
75 _ if s.starts_with("vector") => {
76 let inner = s
78 .strip_prefix("vector<")
79 .and_then(|r| r.strip_suffix('>'))
80 .unwrap_or("");
81 let mut parts = inner.splitn(2, ',');
82 let element_type = parts.next().unwrap_or("float32").to_string();
83 let dimension = parts.next().and_then(|d| d.parse().ok());
84 Self::Vector {
85 element_type,
86 dimension,
87 }
88 }
89 _ => Self::Unknown,
90 }
91 }
92
93 pub fn as_legacy_str(&self) -> Option<&str> {
95 match self {
96 Self::Unknown => None,
97 Self::Bool => Some("bool"),
98 Self::Int64 => Some("int64"),
99 Self::Float64 => Some("float64"),
100 Self::String => Some("string"),
101 Self::Null => Some("null"),
102 Self::List => Some("list<value>"),
103 Self::Map => Some("map"),
104 Self::NodeRef => Some("node_ref"),
105 Self::RelRef => Some("rel_ref"),
106 Self::Path => Some("path"),
107 Self::Vector { .. } => Some("vector"),
108 }
109 }
110}
111
112#[derive(Debug, Clone, PartialEq, Eq)]
113pub struct ColDef {
114 pub name: String,
115 pub kind: ColKind,
116 pub logical_type: LogicalType,
117}
118
119#[derive(Debug, Clone, Copy, PartialEq, Eq)]
120pub enum CmpOp {
121 Eq,
122 Ne,
123 Lt,
124 Gt,
125 Le,
126 Ge,
127}
128
129#[derive(Debug, Clone, Copy, PartialEq, Eq)]
130pub enum AggFn {
131 CountStar,
132 Count,
133 Sum,
134 Avg,
135 Min,
136 Max,
137 Collect,
138}
139
140#[derive(Debug, Clone, Copy, PartialEq, Eq)]
141pub enum ArithOp {
142 Add,
143 Sub,
144 Mul,
145 Div,
146}
147
148#[derive(Debug, Clone, Copy, PartialEq, Eq)]
149pub enum VectorMetric {
150 Cosine,
151 L2,
152 DotProduct,
153}
154
155#[derive(Debug, Clone, Copy, PartialEq, Eq)]
156pub enum ExpandDir {
157 Out,
158 In,
159 Both,
160}
161
162#[derive(Debug, Clone, Copy, PartialEq, Eq)]
163pub enum SortDir {
164 Asc,
165 Desc,
166}
167
168#[derive(Debug, Clone, PartialEq)]
169pub enum Expr {
170 ColRef {
171 idx: u32,
172 },
173 PropAccess {
174 col: u32,
175 prop: String,
176 },
177 IntLiteral(i64),
178 FloatLiteral(f64),
179 BoolLiteral(bool),
180 StringLiteral(String),
181 NullLiteral,
182 Cmp {
183 op: CmpOp,
184 lhs: Box<Expr>,
185 rhs: Box<Expr>,
186 },
187 And {
188 lhs: Box<Expr>,
189 rhs: Box<Expr>,
190 },
191 Or {
192 lhs: Box<Expr>,
193 rhs: Box<Expr>,
194 },
195 Not {
196 expr: Box<Expr>,
197 },
198 IsNull {
199 expr: Box<Expr>,
200 },
201 IsNotNull {
202 expr: Box<Expr>,
203 },
204 StartsWith {
205 expr: Box<Expr>,
206 pattern: String,
207 },
208 EndsWith {
209 expr: Box<Expr>,
210 pattern: String,
211 },
212 Contains {
213 expr: Box<Expr>,
214 pattern: String,
215 },
216 In {
217 expr: Box<Expr>,
218 items: Vec<Expr>,
219 },
220 ListLiteral {
221 items: Vec<Expr>,
222 },
223 MapLiteral {
224 entries: Vec<(String, Expr)>,
225 },
226 Exists {
227 expr: Box<Expr>,
228 },
229 ListComprehension {
230 list: Box<Expr>,
231 var: String,
232 predicate: Option<Box<Expr>>,
233 map: Box<Expr>,
234 },
235 Agg {
236 fn_: AggFn,
237 expr: Option<Box<Expr>>,
238 },
239 Arith {
240 op: ArithOp,
241 lhs: Box<Expr>,
242 rhs: Box<Expr>,
243 },
244 Param {
245 name: String,
246 expected_type: Option<String>,
247 },
248 Case {
249 arms: Vec<(Expr, Expr)>,
250 else_expr: Option<Box<Expr>>,
251 },
252 VectorSimilarity {
253 metric: VectorMetric,
254 lhs: Box<Expr>,
255 rhs: Box<Expr>,
256 },
257}
258
259#[derive(Debug, Clone, PartialEq)]
260pub enum Op {
261 ScanNodes {
262 labels: Vec<String>,
263 schema: Vec<ColDef>,
264 must_labels: Vec<String>,
265 forbidden_labels: Vec<String>,
266 est_rows: i64,
267 selectivity: f64,
268 graph_ref: Option<String>,
269 },
270 ScanRels {
271 types: Vec<String>,
272 schema: Vec<ColDef>,
273 src_labels: Vec<String>,
274 dst_labels: Vec<String>,
275 est_rows: i64,
276 selectivity: f64,
277 },
278 Expand {
279 input: u32,
280 src_col: u32,
281 types: Vec<String>,
282 dir: ExpandDir,
283 schema: Vec<ColDef>,
284 src_var: String,
285 rel_var: String,
286 dst_var: String,
287 legal_src_labels: Vec<String>,
288 legal_dst_labels: Vec<String>,
289 est_degree: f64,
290 graph_ref: Option<String>,
291 },
292 OptionalExpand {
293 input: u32,
294 src_col: u32,
295 types: Vec<String>,
296 dir: ExpandDir,
297 schema: Vec<ColDef>,
298 src_var: String,
299 rel_var: String,
300 dst_var: String,
301 legal_src_labels: Vec<String>,
302 legal_dst_labels: Vec<String>,
303 graph_ref: Option<String>,
304 },
305 SemiExpand {
306 input: u32,
307 src_col: u32,
308 types: Vec<String>,
309 dir: ExpandDir,
310 schema: Vec<ColDef>,
311 legal_src_labels: Vec<String>,
312 legal_dst_labels: Vec<String>,
313 },
314 ExpandVarLen {
315 input: u32,
316 src_col: u32,
317 types: Vec<String>,
318 dir: ExpandDir,
319 min_hops: i32,
320 max_hops: i32,
321 schema: Vec<ColDef>,
322 src_var: String,
323 path_var: String,
324 dst_var: String,
325 graph_ref: Option<String>,
326 },
327 Filter {
328 input: u32,
329 predicate: Expr,
330 },
331 BlockMarker {
332 input: u32,
333 block_id: i32,
334 branch_id: i32,
335 },
336 Project {
337 input: u32,
338 exprs: Vec<Expr>,
339 schema: Vec<ColDef>,
340 },
341 Aggregate {
342 input: u32,
343 keys: Vec<u32>,
344 aggs: Vec<Expr>,
345 schema: Vec<ColDef>,
346 },
347 Sort {
348 input: u32,
349 keys: Vec<u32>,
350 dirs: Vec<SortDir>,
351 },
352 Limit {
353 input: u32,
354 count: i64,
355 skip: i64,
356 },
357 Unwind {
358 input: u32,
359 list_expr: Expr,
360 out_var: String,
361 schema: Vec<ColDef>,
362 },
363 PathConstruct {
364 input: u32,
365 rel_cols: Vec<u32>,
366 schema: Vec<ColDef>,
367 },
368 Union {
369 lhs: u32,
370 rhs: u32,
371 all: bool,
372 schema: Vec<ColDef>,
373 },
374 CreateNode {
375 input: u32,
376 labels: Vec<String>,
377 props: Expr,
378 schema: Vec<ColDef>,
379 out_var: String,
380 },
381 CreateRel {
382 input: u32,
383 src_col: i32,
384 dst_col: i32,
385 rel_type: String,
386 props: Expr,
387 schema: Vec<ColDef>,
388 out_var: String,
389 },
390 Merge {
391 input: u32,
392 pattern: Expr,
393 on_create_props: Expr,
394 on_match_props: Expr,
395 schema: Vec<ColDef>,
396 },
397 Delete {
398 input: u32,
399 target_col: i32,
400 detach: bool,
401 schema: Vec<ColDef>,
402 },
403 SetProperty {
404 input: u32,
405 target_col: i32,
406 key: String,
407 value_expr: Expr,
408 schema: Vec<ColDef>,
409 },
410 RemoveProperty {
411 input: u32,
412 target_col: i32,
413 key: String,
414 schema: Vec<ColDef>,
415 },
416 VectorScan {
417 input: u32,
418 collection: String,
419 query_vector: Expr,
420 metric: VectorMetric,
421 top_k: u32,
422 approx_hint: bool,
423 schema: Vec<ColDef>,
424 },
425 Rerank {
426 input: u32,
427 score_expr: Expr,
428 top_k: u32,
429 schema: Vec<ColDef>,
430 },
431 Return {
432 input: u32,
433 },
434 ConstRow,
437}
438
439#[derive(Debug, Clone, PartialEq)]
440pub struct Plan {
441 pub version: Version,
442 pub ops: Vec<Op>,
443 pub root_op: u32,
444}
445
446#[derive(Debug, Clone, Copy, PartialEq, Eq)]
447pub struct CapabilitySemver {
448 pub major: u32,
449 pub minor: u32,
450 pub patch: u32,
451}
452
453#[derive(Debug, Clone, Copy, PartialEq, Eq)]
454pub struct CapabilityVersionRange {
455 pub min_supported: CapabilitySemver,
456 pub max_supported: CapabilitySemver,
457}
458
459#[derive(Debug, Clone, Copy, PartialEq, Eq)]
460pub enum CapabilityOrderingContract {
461 EngineDefinedStable,
462 StablePassThrough,
463 FanOutDeterministicPerInput,
464 DeterministicSortStableTies,
465 UnspecifiedWithoutSort,
466 StableConcatOrUnspecifiedDistinct,
467 ScoreDescStableTies,
468}
469
470#[derive(Debug, Clone, PartialEq, Eq)]
471pub struct OpOrderingDecl {
472 pub op: String,
473 pub contract: CapabilityOrderingContract,
474}
475
476#[derive(Debug, Clone, PartialEq, Eq)]
477pub struct EngineCapabilityDecl {
478 pub version_range: CapabilityVersionRange,
479 pub supported_ops: Vec<String>,
480 pub supported_exprs: Vec<String>,
481 pub op_ordering: Vec<OpOrderingDecl>,
482 pub supports_graph_ref: bool,
483 pub supports_multi_graph: bool,
484 pub supports_graph_params: bool,
485}
486
487#[derive(Debug)]
488pub enum SerdeError {
489 InvalidFlatbuffer(flatbuffers::InvalidFlatbuffer),
490 MissingField(&'static str),
491 Unsupported(&'static str),
492}
493
494impl core::fmt::Display for SerdeError {
495 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
496 match self {
497 Self::InvalidFlatbuffer(e) => write!(f, "invalid flatbuffer: {e}"),
498 Self::MissingField(x) => write!(f, "missing required field: {x}"),
499 Self::Unsupported(x) => write!(f, "unsupported payload: {x}"),
500 }
501 }
502}
503
504impl std::error::Error for SerdeError {}
505
506impl From<flatbuffers::InvalidFlatbuffer> for SerdeError {
507 fn from(value: flatbuffers::InvalidFlatbuffer) -> Self {
508 Self::InvalidFlatbuffer(value)
509 }
510}