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 cursor: Option<Vec<u8>>,
359 emit_cursor: bool,
362 },
363 Unwind {
364 input: u32,
365 list_expr: Expr,
366 out_var: String,
367 schema: Vec<ColDef>,
368 },
369 PathConstruct {
370 input: u32,
371 rel_cols: Vec<u32>,
372 schema: Vec<ColDef>,
373 },
374 Union {
375 lhs: u32,
376 rhs: u32,
377 all: bool,
378 schema: Vec<ColDef>,
379 },
380 CreateNode {
381 input: u32,
382 labels: Vec<String>,
383 props: Expr,
384 schema: Vec<ColDef>,
385 out_var: String,
386 },
387 CreateRel {
388 input: u32,
389 src_col: i32,
390 dst_col: i32,
391 rel_type: String,
392 props: Expr,
393 schema: Vec<ColDef>,
394 out_var: String,
395 },
396 Merge {
397 input: u32,
398 pattern: Expr,
399 on_create_props: Expr,
400 on_match_props: Expr,
401 schema: Vec<ColDef>,
402 },
403 Delete {
404 input: u32,
405 target_col: i32,
406 detach: bool,
407 schema: Vec<ColDef>,
408 },
409 SetProperty {
410 input: u32,
411 target_col: i32,
412 key: String,
413 value_expr: Expr,
414 schema: Vec<ColDef>,
415 },
416 RemoveProperty {
417 input: u32,
418 target_col: i32,
419 key: String,
420 schema: Vec<ColDef>,
421 },
422 VectorScan {
423 input: u32,
424 collection: String,
425 query_vector: Expr,
426 metric: VectorMetric,
427 top_k: u32,
428 approx_hint: bool,
429 schema: Vec<ColDef>,
430 },
431 Rerank {
432 input: u32,
433 score_expr: Expr,
434 top_k: u32,
435 schema: Vec<ColDef>,
436 },
437 Return {
438 input: u32,
439 },
440 ConstRow,
443}
444
445#[derive(Debug, Clone, PartialEq)]
446pub struct Plan {
447 pub version: Version,
448 pub ops: Vec<Op>,
449 pub root_op: u32,
450}
451
452#[derive(Debug, Clone, Copy, PartialEq, Eq)]
453pub struct CapabilitySemver {
454 pub major: u32,
455 pub minor: u32,
456 pub patch: u32,
457}
458
459#[derive(Debug, Clone, Copy, PartialEq, Eq)]
460pub struct CapabilityVersionRange {
461 pub min_supported: CapabilitySemver,
462 pub max_supported: CapabilitySemver,
463}
464
465#[derive(Debug, Clone, Copy, PartialEq, Eq)]
466pub enum CapabilityOrderingContract {
467 EngineDefinedStable,
468 StablePassThrough,
469 FanOutDeterministicPerInput,
470 DeterministicSortStableTies,
471 UnspecifiedWithoutSort,
472 StableConcatOrUnspecifiedDistinct,
473 ScoreDescStableTies,
474}
475
476#[derive(Debug, Clone, PartialEq, Eq)]
477pub struct OpOrderingDecl {
478 pub op: String,
479 pub contract: CapabilityOrderingContract,
480}
481
482#[derive(Debug, Clone, PartialEq, Eq)]
483pub struct EngineCapabilityDecl {
484 pub version_range: CapabilityVersionRange,
485 pub supported_ops: Vec<String>,
486 pub supported_exprs: Vec<String>,
487 pub op_ordering: Vec<OpOrderingDecl>,
488 pub supports_graph_ref: bool,
489 pub supports_multi_graph: bool,
490 pub supports_graph_params: bool,
491}
492
493#[derive(Debug)]
494pub enum SerdeError {
495 InvalidFlatbuffer(flatbuffers::InvalidFlatbuffer),
496 MissingField(&'static str),
497 Unsupported(&'static str),
498}
499
500impl core::fmt::Display for SerdeError {
501 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
502 match self {
503 Self::InvalidFlatbuffer(e) => write!(f, "invalid flatbuffer: {e}"),
504 Self::MissingField(x) => write!(f, "missing required field: {x}"),
505 Self::Unsupported(x) => write!(f, "unsupported payload: {x}"),
506 }
507 }
508}
509
510impl std::error::Error for SerdeError {}
511
512impl From<flatbuffers::InvalidFlatbuffer> for SerdeError {
513 fn from(value: flatbuffers::InvalidFlatbuffer) -> Self {
514 Self::InvalidFlatbuffer(value)
515 }
516}