1use std::marker::PhantomData;
4
5use serde::{Deserialize, Serialize};
6
7use crate::expr::{Predicate, SourcePredicate, StreamBound};
8use crate::graph::{EdgeRef, NodeRef};
9use crate::index::IndexSpec;
10use crate::projection::{
11 validate_binding_name, validate_binding_projections, BindingProjection, Projection,
12};
13use crate::value::{PropertyInput, PropertyValue};
14pub trait TraversalState: private::Sealed {}
16
17mod private {
18 pub trait Sealed {}
19 impl Sealed for super::Empty {}
20 impl Sealed for super::OnNodes {}
21 impl Sealed for super::OnEdges {}
22 impl Sealed for super::Terminal {}
23 impl Sealed for super::ReadOnly {}
24 impl Sealed for super::WriteEnabled {}
25}
26
27#[derive(Debug, Clone, Copy, PartialEq, Eq)]
29pub struct Empty;
30
31#[derive(Debug, Clone, Copy, PartialEq, Eq)]
33pub struct OnNodes;
34
35#[derive(Debug, Clone, Copy, PartialEq, Eq)]
37pub struct OnEdges;
38
39#[derive(Debug, Clone, Copy, PartialEq, Eq)]
41pub struct Terminal;
42
43impl TraversalState for Empty {}
44impl TraversalState for OnNodes {}
45impl TraversalState for OnEdges {}
46impl TraversalState for Terminal {}
47
48pub trait MutationMode: private::Sealed {}
50
51#[derive(Debug, Clone, Copy, PartialEq, Eq)]
53pub struct ReadOnly;
54
55#[derive(Debug, Clone, Copy, PartialEq, Eq)]
57pub struct WriteEnabled;
58
59impl MutationMode for ReadOnly {}
60impl MutationMode for WriteEnabled {}
61#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
63#[serde(rename_all = "snake_case")]
64pub enum Order {
65 #[default]
67 Asc,
68 Desc,
70}
71
72#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
74#[serde(rename_all = "snake_case")]
75pub enum ShortestPathDirection {
76 #[default]
78 Out,
79 In,
81 Both,
83}
84
85#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
87#[serde(rename_all = "snake_case")]
88pub enum EmitBehavior {
89 #[default]
91 None,
92 Before,
94 After,
96 All,
98}
99
100#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
102#[serde(rename_all = "snake_case")]
103pub enum AggregateFunction {
104 Count,
106 Sum,
108 Min,
110 Max,
112 Mean,
114}
115#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
117#[serde(rename_all = "snake_case")]
118pub enum AstNode {
119 Context,
121 Nodes { reference: NodeRef },
123 NodesWhere { predicate: SourcePredicate },
125 Edges { reference: EdgeRef },
127 EdgesWhere { predicate: SourcePredicate },
129 VectorSearchNodes {
131 label: String,
133 property: String,
135 #[serde(default, skip_serializing_if = "Option::is_none")]
137 tenant_value: Option<PropertyInput>,
138 query_vector: PropertyInput,
140 k: StreamBound,
142 },
143 TextSearchNodes {
145 label: String,
147 property: String,
149 #[serde(default, skip_serializing_if = "Option::is_none")]
151 tenant_value: Option<PropertyInput>,
152 query_text: PropertyInput,
154 k: StreamBound,
156 },
157 VectorSearchEdges {
159 label: String,
161 property: String,
163 #[serde(default, skip_serializing_if = "Option::is_none")]
165 tenant_value: Option<PropertyInput>,
166 query_vector: PropertyInput,
168 k: StreamBound,
170 },
171 TextSearchEdges {
173 label: String,
175 property: String,
177 #[serde(default, skip_serializing_if = "Option::is_none")]
179 tenant_value: Option<PropertyInput>,
180 query_text: PropertyInput,
182 k: StreamBound,
184 },
185 VectorSearchNodesWithin {
187 input: Box<AstNode>,
189 label: String,
191 property: String,
193 #[serde(default, skip_serializing_if = "Option::is_none")]
195 tenant_value: Option<PropertyInput>,
196 query_vector: PropertyInput,
198 k: StreamBound,
200 },
201 VectorSearchEdgesWithin {
203 input: Box<AstNode>,
205 label: String,
207 property: String,
209 #[serde(default, skip_serializing_if = "Option::is_none")]
211 tenant_value: Option<PropertyInput>,
212 query_vector: PropertyInput,
214 k: StreamBound,
216 },
217 Out {
219 input: Box<AstNode>,
221 #[serde(default, skip_serializing_if = "Option::is_none")]
223 label: Option<String>,
224 },
225 In {
227 input: Box<AstNode>,
229 #[serde(default, skip_serializing_if = "Option::is_none")]
231 label: Option<String>,
232 },
233 Both {
235 input: Box<AstNode>,
237 #[serde(default, skip_serializing_if = "Option::is_none")]
239 label: Option<String>,
240 },
241 OutE {
243 input: Box<AstNode>,
245 #[serde(default, skip_serializing_if = "Option::is_none")]
247 label: Option<String>,
248 },
249 InE {
251 input: Box<AstNode>,
253 #[serde(default, skip_serializing_if = "Option::is_none")]
255 label: Option<String>,
256 },
257 BothE {
259 input: Box<AstNode>,
261 #[serde(default, skip_serializing_if = "Option::is_none")]
263 label: Option<String>,
264 },
265 OutN { input: Box<AstNode> },
267 InN { input: Box<AstNode> },
269 OtherN { input: Box<AstNode> },
271 Has {
273 input: Box<AstNode>,
275 property: String,
277 value: PropertyValue,
279 },
280 HasLabel {
282 input: Box<AstNode>,
284 label: String,
286 },
287 HasKey {
289 input: Box<AstNode>,
291 property: String,
293 },
294 Where {
296 input: Box<AstNode>,
298 predicate: Predicate,
300 },
301 Dedup { input: Box<AstNode> },
303 Within {
305 input: Box<AstNode>,
307 variable: String,
309 },
310 Without {
312 input: Box<AstNode>,
314 variable: String,
316 },
317 EdgeHas {
319 input: Box<AstNode>,
321 property: String,
323 value: PropertyInput,
325 },
326 EdgeHasLabel {
328 input: Box<AstNode>,
330 label: String,
332 },
333 Limit {
335 input: Box<AstNode>,
337 count: StreamBound,
339 },
340 Skip {
342 input: Box<AstNode>,
344 count: StreamBound,
346 },
347 Range {
349 input: Box<AstNode>,
351 start: StreamBound,
353 end: StreamBound,
355 },
356 As { input: Box<AstNode>, name: String },
358 Store { input: Box<AstNode>, name: String },
360 Select { input: Box<AstNode>, name: String },
362 Bind { input: Box<AstNode>, name: String },
364 Inject {
366 #[serde(default, skip_serializing_if = "Option::is_none")]
368 input: Option<Box<AstNode>>,
369 variable: String,
371 },
372 Count { input: Box<AstNode> },
374 Exists { input: Box<AstNode> },
376 Id { input: Box<AstNode> },
378 Label { input: Box<AstNode> },
380 Values {
382 input: Box<AstNode>,
384 properties: Vec<String>,
386 },
387 ValueMap {
389 input: Box<AstNode>,
391 #[serde(default, skip_serializing_if = "Option::is_none")]
393 properties: Option<Vec<String>>,
394 },
395 Project {
397 input: Box<AstNode>,
399 projections: Vec<Projection>,
401 },
402 ProjectBindings {
404 input: Box<AstNode>,
406 projections: Vec<BindingProjection>,
408 distinct: bool,
410 },
411 EdgeProperties { input: Box<AstNode> },
413 CreateIndex {
415 spec: IndexSpec,
417 if_not_exists: bool,
419 },
420 DropIndex {
422 spec: IndexSpec,
424 },
425 GetIndexOperation {
427 operation_id: String,
429 },
430 RetryIndexOperation {
432 operation_id: String,
434 },
435 AbortIndexOperation {
437 operation_id: String,
439 },
440 AddN {
442 #[serde(default, skip_serializing_if = "Option::is_none")]
444 input: Option<Box<AstNode>>,
445 label: String,
447 properties: Vec<(String, PropertyInput)>,
449 },
450 AddE {
452 input: Box<AstNode>,
454 label: String,
456 to: NodeRef,
458 properties: Vec<(String, PropertyInput)>,
460 },
461 SetProperty {
463 input: Box<AstNode>,
465 name: String,
467 value: PropertyInput,
469 },
470 RemoveProperty {
472 input: Box<AstNode>,
474 name: String,
476 },
477 Drop { input: Box<AstNode> },
479 DropEdge {
481 input: Box<AstNode>,
483 to: NodeRef,
485 },
486 DropEdgeLabeled {
488 input: Box<AstNode>,
490 to: NodeRef,
492 label: String,
494 },
495 DropEdgeById {
497 #[serde(default, skip_serializing_if = "Option::is_none")]
499 input: Option<Box<AstNode>>,
500 edges: EdgeRef,
502 },
503 OrderBy {
505 input: Box<AstNode>,
507 property: String,
509 order: Order,
511 },
512 OrderByMultiple {
514 input: Box<AstNode>,
516 orderings: Vec<(String, Order)>,
518 },
519 Repeat {
521 input: Box<AstNode>,
523 config: RepeatConfig,
525 },
526 Union {
528 input: Box<AstNode>,
530 traversals: Vec<SubTraversal>,
532 },
533 Choose {
535 input: Box<AstNode>,
537 condition: Predicate,
539 then_traversal: SubTraversal,
541 #[serde(default, skip_serializing_if = "Option::is_none")]
543 else_traversal: Option<SubTraversal>,
544 },
545 Coalesce {
547 input: Box<AstNode>,
549 traversals: Vec<SubTraversal>,
551 },
552 Optional {
554 input: Box<AstNode>,
556 traversal: SubTraversal,
558 },
559 Group {
561 input: Box<AstNode>,
563 property: String,
565 },
566 GroupCount {
568 input: Box<AstNode>,
570 property: String,
572 },
573 AggregateBy {
575 input: Box<AstNode>,
577 function: AggregateFunction,
579 property: String,
581 },
582 Fold { input: Box<AstNode> },
584 Unfold { input: Box<AstNode> },
586 Path { input: Box<AstNode> },
588 SimplePath { input: Box<AstNode> },
590 WithSack {
592 input: Box<AstNode>,
594 initial: PropertyValue,
596 },
597 SackSet {
599 input: Box<AstNode>,
601 property: String,
603 },
604 SackAdd {
606 input: Box<AstNode>,
608 property: String,
610 },
611 SackGet { input: Box<AstNode> },
613 ShortestPath {
615 source: NodeRef,
617 target: NodeRef,
619 #[serde(default, skip_serializing_if = "Option::is_none")]
621 label: Option<String>,
622 direction: ShortestPathDirection,
624 max_depth: usize,
626 },
627}
628
629impl AstNode {
630 pub fn is_read_only(&self) -> bool {
635 match self {
636 Self::Context
637 | Self::Nodes { .. }
638 | Self::NodesWhere { .. }
639 | Self::Edges { .. }
640 | Self::EdgesWhere { .. }
641 | Self::VectorSearchNodes { .. }
642 | Self::TextSearchNodes { .. }
643 | Self::VectorSearchEdges { .. }
644 | Self::TextSearchEdges { .. }
645 | Self::GetIndexOperation { .. }
646 | Self::ShortestPath { .. } => true,
647 Self::CreateIndex { .. }
648 | Self::DropIndex { .. }
649 | Self::RetryIndexOperation { .. }
650 | Self::AbortIndexOperation { .. }
651 | Self::AddN { .. }
652 | Self::AddE { .. }
653 | Self::SetProperty { .. }
654 | Self::RemoveProperty { .. }
655 | Self::Drop { .. }
656 | Self::DropEdge { .. }
657 | Self::DropEdgeLabeled { .. }
658 | Self::DropEdgeById { .. } => false,
659 Self::VectorSearchNodesWithin { input, .. }
660 | Self::VectorSearchEdgesWithin { input, .. }
661 | Self::Out { input, .. }
662 | Self::In { input, .. }
663 | Self::Both { input, .. }
664 | Self::OutE { input, .. }
665 | Self::InE { input, .. }
666 | Self::BothE { input, .. }
667 | Self::OutN { input }
668 | Self::InN { input }
669 | Self::OtherN { input }
670 | Self::Has { input, .. }
671 | Self::HasLabel { input, .. }
672 | Self::HasKey { input, .. }
673 | Self::Where { input, .. }
674 | Self::Dedup { input }
675 | Self::Within { input, .. }
676 | Self::Without { input, .. }
677 | Self::EdgeHas { input, .. }
678 | Self::EdgeHasLabel { input, .. }
679 | Self::Limit { input, .. }
680 | Self::Skip { input, .. }
681 | Self::Range { input, .. }
682 | Self::As { input, .. }
683 | Self::Store { input, .. }
684 | Self::Select { input, .. }
685 | Self::Bind { input, .. }
686 | Self::Count { input }
687 | Self::Exists { input }
688 | Self::Id { input }
689 | Self::Label { input }
690 | Self::Values { input, .. }
691 | Self::ValueMap { input, .. }
692 | Self::Project { input, .. }
693 | Self::ProjectBindings { input, .. }
694 | Self::EdgeProperties { input }
695 | Self::OrderBy { input, .. }
696 | Self::OrderByMultiple { input, .. }
697 | Self::Group { input, .. }
698 | Self::GroupCount { input, .. }
699 | Self::AggregateBy { input, .. }
700 | Self::Fold { input }
701 | Self::Unfold { input }
702 | Self::Path { input }
703 | Self::SimplePath { input }
704 | Self::WithSack { input, .. }
705 | Self::SackSet { input, .. }
706 | Self::SackAdd { input, .. }
707 | Self::SackGet { input } => input.is_read_only(),
708 Self::Inject { input, .. } => input.as_deref().map(Self::is_read_only).unwrap_or(true),
709 Self::Repeat { input, config } => {
710 input.is_read_only() && config.traversal.root.is_read_only()
711 }
712 Self::Union { input, traversals } | Self::Coalesce { input, traversals } => {
713 input.is_read_only()
714 && traversals
715 .iter()
716 .all(|traversal| traversal.root.is_read_only())
717 }
718 Self::Choose {
719 input,
720 then_traversal,
721 else_traversal,
722 ..
723 } => {
724 input.is_read_only()
725 && then_traversal.root.is_read_only()
726 && else_traversal
727 .as_ref()
728 .map(|traversal| traversal.root.is_read_only())
729 .unwrap_or(true)
730 }
731 Self::Optional { input, traversal } => {
732 input.is_read_only() && traversal.root.is_read_only()
733 }
734 }
735 }
736
737 pub fn is_terminal(&self) -> bool {
739 matches!(
740 self,
741 Self::Count { .. }
742 | Self::Exists { .. }
743 | Self::Id { .. }
744 | Self::Label { .. }
745 | Self::Values { .. }
746 | Self::ValueMap { .. }
747 | Self::Project { .. }
748 | Self::ProjectBindings { .. }
749 | Self::EdgeProperties { .. }
750 | Self::CreateIndex { .. }
751 | Self::DropIndex { .. }
752 | Self::GetIndexOperation { .. }
753 | Self::RetryIndexOperation { .. }
754 | Self::AbortIndexOperation { .. }
755 | Self::Group { .. }
756 | Self::GroupCount { .. }
757 | Self::AggregateBy { .. }
758 | Self::ShortestPath { .. }
759 )
760 }
761}
762
763#[derive(Debug, Clone)]
764enum Operation {
765 Out(Option<String>),
766 In(Option<String>),
767 Both(Option<String>),
768 OutE(Option<String>),
769 InE(Option<String>),
770 BothE(Option<String>),
771 OutN,
772 InN,
773 OtherN,
774 Has(String, PropertyValue),
775 HasLabel(String),
776 HasKey(String),
777 Where(Predicate),
778 Dedup,
779 Within(String),
780 Without(String),
781 EdgeHas(String, PropertyInput),
782 EdgeHasLabel(String),
783 VectorSearchNodesWithin {
784 label: String,
785 property: String,
786 tenant_value: Option<PropertyInput>,
787 query_vector: PropertyInput,
788 k: StreamBound,
789 },
790 VectorSearchEdgesWithin {
791 label: String,
792 property: String,
793 tenant_value: Option<PropertyInput>,
794 query_vector: PropertyInput,
795 k: StreamBound,
796 },
797 Limit(StreamBound),
798 Skip(StreamBound),
799 Range(StreamBound, StreamBound),
800 As(String),
801 Store(String),
802 Select(String),
803 Bind(String),
804 Inject(String),
805 Count,
806 Exists,
807 Id,
808 Label,
809 Values(Vec<String>),
810 ValueMap(Option<Vec<String>>),
811 Project(Vec<Projection>),
812 ProjectBindings {
813 projections: Vec<BindingProjection>,
814 distinct: bool,
815 },
816 EdgeProperties,
817 AddN {
818 label: String,
819 properties: Vec<(String, PropertyInput)>,
820 },
821 AddE {
822 label: String,
823 to: NodeRef,
824 properties: Vec<(String, PropertyInput)>,
825 },
826 SetProperty(String, PropertyInput),
827 RemoveProperty(String),
828 Drop,
829 DropEdge(NodeRef),
830 DropEdgeLabeled {
831 to: NodeRef,
832 label: String,
833 },
834 DropEdgeById(EdgeRef),
835 OrderBy(String, Order),
836 OrderByMultiple(Vec<(String, Order)>),
837 Repeat(RepeatConfig),
838 Union(Vec<SubTraversal>),
839 Choose {
840 condition: Predicate,
841 then_traversal: SubTraversal,
842 else_traversal: Option<SubTraversal>,
843 },
844 Coalesce(Vec<SubTraversal>),
845 Optional(SubTraversal),
846 Group(String),
847 GroupCount(String),
848 AggregateBy(AggregateFunction, String),
849 Fold,
850 Unfold,
851 Path,
852 SimplePath,
853 WithSack(PropertyValue),
854 SackSet(String),
855 SackAdd(String),
856 SackGet,
857}
858
859impl Operation {
860 fn apply(self, input: AstNode) -> AstNode {
861 let input = Box::new(input);
862 match self {
863 Self::Out(label) => AstNode::Out { input, label },
864 Self::In(label) => AstNode::In { input, label },
865 Self::Both(label) => AstNode::Both { input, label },
866 Self::OutE(label) => AstNode::OutE { input, label },
867 Self::InE(label) => AstNode::InE { input, label },
868 Self::BothE(label) => AstNode::BothE { input, label },
869 Self::OutN => AstNode::OutN { input },
870 Self::InN => AstNode::InN { input },
871 Self::OtherN => AstNode::OtherN { input },
872 Self::Has(property, value) => AstNode::Has {
873 input,
874 property,
875 value,
876 },
877 Self::HasLabel(label) => AstNode::HasLabel { input, label },
878 Self::HasKey(property) => AstNode::HasKey { input, property },
879 Self::Where(predicate) => AstNode::Where { input, predicate },
880 Self::Dedup => AstNode::Dedup { input },
881 Self::Within(variable) => AstNode::Within { input, variable },
882 Self::Without(variable) => AstNode::Without { input, variable },
883 Self::EdgeHas(property, value) => AstNode::EdgeHas {
884 input,
885 property,
886 value,
887 },
888 Self::EdgeHasLabel(label) => AstNode::EdgeHasLabel { input, label },
889 Self::VectorSearchNodesWithin {
890 label,
891 property,
892 tenant_value,
893 query_vector,
894 k,
895 } => AstNode::VectorSearchNodesWithin {
896 input,
897 label,
898 property,
899 tenant_value,
900 query_vector,
901 k,
902 },
903 Self::VectorSearchEdgesWithin {
904 label,
905 property,
906 tenant_value,
907 query_vector,
908 k,
909 } => AstNode::VectorSearchEdgesWithin {
910 input,
911 label,
912 property,
913 tenant_value,
914 query_vector,
915 k,
916 },
917 Self::Limit(count) => AstNode::Limit { input, count },
918 Self::Skip(count) => AstNode::Skip { input, count },
919 Self::Range(start, end) => AstNode::Range { input, start, end },
920 Self::As(name) => AstNode::As { input, name },
921 Self::Store(name) => AstNode::Store { input, name },
922 Self::Select(name) => AstNode::Select { input, name },
923 Self::Bind(name) => AstNode::Bind { input, name },
924 Self::Inject(variable) => AstNode::Inject {
925 input: Some(input),
926 variable,
927 },
928 Self::Count => AstNode::Count { input },
929 Self::Exists => AstNode::Exists { input },
930 Self::Id => AstNode::Id { input },
931 Self::Label => AstNode::Label { input },
932 Self::Values(properties) => AstNode::Values { input, properties },
933 Self::ValueMap(properties) => AstNode::ValueMap { input, properties },
934 Self::Project(projections) => AstNode::Project { input, projections },
935 Self::ProjectBindings {
936 projections,
937 distinct,
938 } => AstNode::ProjectBindings {
939 input,
940 projections,
941 distinct,
942 },
943 Self::EdgeProperties => AstNode::EdgeProperties { input },
944 Self::AddN { label, properties } => AstNode::AddN {
945 input: Some(input),
946 label,
947 properties,
948 },
949 Self::AddE {
950 label,
951 to,
952 properties,
953 } => AstNode::AddE {
954 input,
955 label,
956 to,
957 properties,
958 },
959 Self::SetProperty(name, value) => AstNode::SetProperty { input, name, value },
960 Self::RemoveProperty(name) => AstNode::RemoveProperty { input, name },
961 Self::Drop => AstNode::Drop { input },
962 Self::DropEdge(to) => AstNode::DropEdge { input, to },
963 Self::DropEdgeLabeled { to, label } => AstNode::DropEdgeLabeled { input, to, label },
964 Self::DropEdgeById(edges) => AstNode::DropEdgeById {
965 input: Some(input),
966 edges,
967 },
968 Self::OrderBy(property, order) => AstNode::OrderBy {
969 input,
970 property,
971 order,
972 },
973 Self::OrderByMultiple(orderings) => AstNode::OrderByMultiple { input, orderings },
974 Self::Repeat(config) => AstNode::Repeat { input, config },
975 Self::Union(traversals) => AstNode::Union { input, traversals },
976 Self::Choose {
977 condition,
978 then_traversal,
979 else_traversal,
980 } => AstNode::Choose {
981 input,
982 condition,
983 then_traversal,
984 else_traversal,
985 },
986 Self::Coalesce(traversals) => AstNode::Coalesce { input, traversals },
987 Self::Optional(traversal) => AstNode::Optional { input, traversal },
988 Self::Group(property) => AstNode::Group { input, property },
989 Self::GroupCount(property) => AstNode::GroupCount { input, property },
990 Self::AggregateBy(function, property) => AstNode::AggregateBy {
991 input,
992 function,
993 property,
994 },
995 Self::Fold => AstNode::Fold { input },
996 Self::Unfold => AstNode::Unfold { input },
997 Self::Path => AstNode::Path { input },
998 Self::SimplePath => AstNode::SimplePath { input },
999 Self::WithSack(initial) => AstNode::WithSack { input, initial },
1000 Self::SackSet(property) => AstNode::SackSet { input, property },
1001 Self::SackAdd(property) => AstNode::SackAdd { input, property },
1002 Self::SackGet => AstNode::SackGet { input },
1003 }
1004 }
1005}
1006
1007#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1009pub struct SubTraversal {
1010 pub root: Box<AstNode>,
1012}
1013
1014impl Default for SubTraversal {
1015 fn default() -> Self {
1016 Self::new()
1017 }
1018}
1019
1020impl SubTraversal {
1021 pub fn new() -> Self {
1023 Self {
1024 root: Box::new(AstNode::Context),
1025 }
1026 }
1027
1028 fn push(mut self, operation: Operation) -> Self {
1029 self.root = Box::new(operation.apply(*self.root));
1030 self
1031 }
1032
1033 pub fn out(self, label: Option<impl Into<String>>) -> Self {
1035 self.push(Operation::Out(label.map(Into::into)))
1036 }
1037
1038 pub fn in_(self, label: Option<impl Into<String>>) -> Self {
1040 self.push(Operation::In(label.map(Into::into)))
1041 }
1042
1043 pub fn both(self, label: Option<impl Into<String>>) -> Self {
1045 self.push(Operation::Both(label.map(Into::into)))
1046 }
1047
1048 pub fn out_e(self, label: Option<impl Into<String>>) -> Self {
1050 self.push(Operation::OutE(label.map(Into::into)))
1051 }
1052
1053 pub fn in_e(self, label: Option<impl Into<String>>) -> Self {
1055 self.push(Operation::InE(label.map(Into::into)))
1056 }
1057
1058 pub fn both_e(self, label: Option<impl Into<String>>) -> Self {
1060 self.push(Operation::BothE(label.map(Into::into)))
1061 }
1062
1063 pub fn out_n(self) -> Self {
1065 self.push(Operation::OutN)
1066 }
1067
1068 pub fn in_n(self) -> Self {
1070 self.push(Operation::InN)
1071 }
1072
1073 pub fn other_n(self) -> Self {
1075 self.push(Operation::OtherN)
1076 }
1077
1078 pub fn has(self, property: impl Into<String>, value: impl Into<PropertyValue>) -> Self {
1080 self.push(Operation::Has(property.into(), value.into()))
1081 }
1082
1083 pub fn has_label(self, label: impl Into<String>) -> Self {
1085 self.push(Operation::HasLabel(label.into()))
1086 }
1087
1088 pub fn has_key(self, property: impl Into<String>) -> Self {
1090 self.push(Operation::HasKey(property.into()))
1091 }
1092
1093 pub fn where_(self, predicate: Predicate) -> Self {
1095 self.push(Operation::Where(predicate))
1096 }
1097
1098 pub fn dedup(self) -> Self {
1100 self.push(Operation::Dedup)
1101 }
1102
1103 pub fn within(self, var_name: impl Into<String>) -> Self {
1105 self.push(Operation::Within(var_name.into()))
1106 }
1107
1108 pub fn without(self, var_name: impl Into<String>) -> Self {
1110 self.push(Operation::Without(var_name.into()))
1111 }
1112
1113 pub fn edge_has(self, property: impl Into<String>, value: impl Into<PropertyInput>) -> Self {
1115 self.push(Operation::EdgeHas(property.into(), value.into()))
1116 }
1117
1118 pub fn edge_has_label(self, label: impl Into<String>) -> Self {
1120 self.push(Operation::EdgeHasLabel(label.into()))
1121 }
1122
1123 pub fn limit(self, n: impl Into<StreamBound>) -> Self {
1125 self.push(Operation::Limit(n.into()))
1126 }
1127
1128 pub fn skip(self, n: impl Into<StreamBound>) -> Self {
1130 self.push(Operation::Skip(n.into()))
1131 }
1132
1133 pub fn range(self, start: impl Into<StreamBound>, end: impl Into<StreamBound>) -> Self {
1135 self.push(Operation::Range(start.into(), end.into()))
1136 }
1137
1138 pub fn as_(self, name: impl Into<String>) -> Self {
1140 self.push(Operation::As(name.into()))
1141 }
1142
1143 pub fn store(self, name: impl Into<String>) -> Self {
1145 self.push(Operation::Store(name.into()))
1146 }
1147
1148 pub fn select(self, name: impl Into<String>) -> Self {
1150 self.push(Operation::Select(name.into()))
1151 }
1152
1153 pub fn bind(self, name: impl Into<String>) -> Self {
1155 self.push(Operation::Bind(validate_binding_name(name)))
1156 }
1157
1158 pub fn order_by(self, property: impl Into<String>, order: Order) -> Self {
1160 self.push(Operation::OrderBy(property.into(), order))
1161 }
1162
1163 pub fn order_by_multiple(self, orderings: Vec<(impl Into<String>, Order)>) -> Self {
1165 self.push(Operation::OrderByMultiple(
1166 orderings
1167 .into_iter()
1168 .map(|(property, order)| (property.into(), order))
1169 .collect(),
1170 ))
1171 }
1172
1173 pub fn path(self) -> Self {
1175 self.push(Operation::Path)
1176 }
1177
1178 pub fn simple_path(self) -> Self {
1180 self.push(Operation::SimplePath)
1181 }
1182}
1183
1184pub fn sub() -> SubTraversal {
1186 SubTraversal::new()
1187}
1188
1189#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1191pub struct RepeatConfig {
1192 pub traversal: SubTraversal,
1194 #[serde(default, skip_serializing_if = "Option::is_none")]
1196 pub times: Option<usize>,
1197 #[serde(default, skip_serializing_if = "Option::is_none")]
1199 pub until: Option<Predicate>,
1200 pub emit: EmitBehavior,
1202 #[serde(default, skip_serializing_if = "Option::is_none")]
1204 pub emit_predicate: Option<Predicate>,
1205 pub max_depth: usize,
1207}
1208
1209impl RepeatConfig {
1210 pub fn new(traversal: SubTraversal) -> Self {
1212 Self {
1213 traversal,
1214 times: None,
1215 until: None,
1216 emit: EmitBehavior::None,
1217 emit_predicate: None,
1218 max_depth: 100,
1219 }
1220 }
1221
1222 pub fn times(mut self, n: usize) -> Self {
1224 self.times = Some(n);
1225 self
1226 }
1227
1228 pub fn until(mut self, predicate: Predicate) -> Self {
1230 self.until = Some(predicate);
1231 self
1232 }
1233
1234 pub fn emit_all(mut self) -> Self {
1236 self.emit = EmitBehavior::All;
1237 self
1238 }
1239
1240 pub fn emit_before(mut self) -> Self {
1242 self.emit = EmitBehavior::Before;
1243 self
1244 }
1245
1246 pub fn emit_after(mut self) -> Self {
1248 self.emit = EmitBehavior::After;
1249 self
1250 }
1251
1252 pub fn emit_if(mut self, predicate: Predicate) -> Self {
1254 self.emit = EmitBehavior::After;
1255 self.emit_predicate = Some(predicate);
1256 self
1257 }
1258
1259 pub fn max_depth(mut self, depth: usize) -> Self {
1261 self.max_depth = depth;
1262 self
1263 }
1264}
1265
1266#[derive(Debug, Clone, PartialEq)]
1268pub struct Traversal<S: TraversalState = OnNodes, M: MutationMode = ReadOnly> {
1269 root: Option<AstNode>,
1270 _state: PhantomData<S>,
1271 _mode: PhantomData<M>,
1272}
1273
1274impl<S: TraversalState, M: MutationMode> Default for Traversal<S, M> {
1275 fn default() -> Self {
1276 Self {
1277 root: None,
1278 _state: PhantomData,
1279 _mode: PhantomData,
1280 }
1281 }
1282}
1283
1284impl<S: TraversalState, M: MutationMode> Traversal<S, M> {
1285 pub fn into_ast(self) -> AstNode {
1287 self.root
1288 .expect("traversal must contain at least one AST node before execution")
1289 }
1290
1291 pub fn root(&self) -> Option<&AstNode> {
1293 self.root.as_ref()
1294 }
1295
1296 pub fn has_terminal(&self) -> bool {
1298 self.root.as_ref().is_some_and(AstNode::is_terminal)
1299 }
1300
1301 fn from_root<T: TraversalState>(root: AstNode) -> Traversal<T, M> {
1302 Traversal {
1303 root: Some(root),
1304 _state: PhantomData,
1305 _mode: PhantomData,
1306 }
1307 }
1308
1309 fn push<T: TraversalState>(self, operation: Operation) -> Traversal<T, M> {
1310 let root = self
1311 .root
1312 .expect("cannot append traversal operation before a source node");
1313 Traversal::<T, M>::from_root(operation.apply(root))
1314 }
1315
1316 fn push_mutation<T: TraversalState>(self, operation: Operation) -> Traversal<T, WriteEnabled> {
1317 let root = self
1318 .root
1319 .expect("cannot append mutation operation before a source node");
1320 Traversal {
1321 root: Some(operation.apply(root)),
1322 _state: PhantomData,
1323 _mode: PhantomData,
1324 }
1325 }
1326}
1327
1328impl Traversal<Empty, ReadOnly> {
1329 pub fn new() -> Self {
1331 Self::default()
1332 }
1333
1334 fn source<T: TraversalState>(self, root: AstNode) -> Traversal<T, ReadOnly> {
1335 assert!(
1336 self.root.is_none(),
1337 "source operation cannot be appended to an existing traversal"
1338 );
1339 Traversal {
1340 root: Some(root),
1341 _state: PhantomData,
1342 _mode: PhantomData,
1343 }
1344 }
1345
1346 fn mutation_source<T: TraversalState>(self, root: AstNode) -> Traversal<T, WriteEnabled> {
1347 assert!(
1348 self.root.is_none(),
1349 "source mutation cannot be appended to an existing traversal"
1350 );
1351 Traversal {
1352 root: Some(root),
1353 _state: PhantomData,
1354 _mode: PhantomData,
1355 }
1356 }
1357
1358 pub fn n(self, nodes: impl Into<NodeRef>) -> Traversal<OnNodes> {
1360 self.source(AstNode::Nodes {
1361 reference: nodes.into(),
1362 })
1363 }
1364
1365 pub fn n_where(self, predicate: SourcePredicate) -> Traversal<OnNodes> {
1367 self.source(AstNode::NodesWhere { predicate })
1368 }
1369
1370 pub fn n_with_label(self, label: impl Into<String>) -> Traversal<OnNodes> {
1372 self.n_where(Predicate::eq("$label", label.into()))
1373 }
1374
1375 pub fn n_with_label_where(
1377 self,
1378 label: impl Into<String>,
1379 predicate: SourcePredicate,
1380 ) -> Traversal<OnNodes> {
1381 self.n_where(Predicate::and(vec![
1382 Predicate::eq("$label", label.into()),
1383 predicate,
1384 ]))
1385 }
1386
1387 pub fn vector_search_nodes(
1389 self,
1390 label: impl Into<String>,
1391 property: impl Into<String>,
1392 query_vector: Vec<f32>,
1393 k: usize,
1394 tenant_value: Option<PropertyValue>,
1395 ) -> Traversal<OnNodes> {
1396 self.vector_search_nodes_with(
1397 label,
1398 property,
1399 query_vector,
1400 k,
1401 tenant_value.map(PropertyInput::from),
1402 )
1403 }
1404
1405 pub fn vector_search_nodes_with(
1407 self,
1408 label: impl Into<String>,
1409 property: impl Into<String>,
1410 query_vector: impl Into<PropertyInput>,
1411 k: impl Into<StreamBound>,
1412 tenant_value: Option<PropertyInput>,
1413 ) -> Traversal<OnNodes> {
1414 self.source(AstNode::VectorSearchNodes {
1415 label: label.into(),
1416 property: property.into(),
1417 tenant_value,
1418 query_vector: query_vector.into(),
1419 k: k.into(),
1420 })
1421 }
1422
1423 pub fn text_search_nodes(
1425 self,
1426 label: impl Into<String>,
1427 property: impl Into<String>,
1428 query_text: impl Into<String>,
1429 k: usize,
1430 tenant_value: Option<PropertyValue>,
1431 ) -> Traversal<OnNodes> {
1432 self.text_search_nodes_with(
1433 label,
1434 property,
1435 PropertyInput::from(query_text.into()),
1436 k,
1437 tenant_value.map(PropertyInput::from),
1438 )
1439 }
1440
1441 pub fn text_search_nodes_with(
1443 self,
1444 label: impl Into<String>,
1445 property: impl Into<String>,
1446 query_text: impl Into<PropertyInput>,
1447 k: impl Into<StreamBound>,
1448 tenant_value: Option<PropertyInput>,
1449 ) -> Traversal<OnNodes> {
1450 self.source(AstNode::TextSearchNodes {
1451 label: label.into(),
1452 property: property.into(),
1453 tenant_value,
1454 query_text: query_text.into(),
1455 k: k.into(),
1456 })
1457 }
1458
1459 pub fn e(self, edges: impl Into<EdgeRef>) -> Traversal<OnEdges> {
1461 self.source(AstNode::Edges {
1462 reference: edges.into(),
1463 })
1464 }
1465
1466 pub fn e_where(self, predicate: SourcePredicate) -> Traversal<OnEdges> {
1468 self.source(AstNode::EdgesWhere { predicate })
1469 }
1470
1471 pub fn e_with_label(self, label: impl Into<String>) -> Traversal<OnEdges> {
1473 self.e_where(Predicate::eq("$label", label.into()))
1474 }
1475
1476 pub fn e_with_label_where(
1478 self,
1479 label: impl Into<String>,
1480 predicate: SourcePredicate,
1481 ) -> Traversal<OnEdges> {
1482 self.e_where(Predicate::and(vec![
1483 Predicate::eq("$label", label.into()),
1484 predicate,
1485 ]))
1486 }
1487
1488 pub fn vector_search_edges(
1490 self,
1491 label: impl Into<String>,
1492 property: impl Into<String>,
1493 query_vector: Vec<f32>,
1494 k: usize,
1495 tenant_value: Option<PropertyValue>,
1496 ) -> Traversal<OnEdges> {
1497 self.vector_search_edges_with(
1498 label,
1499 property,
1500 query_vector,
1501 k,
1502 tenant_value.map(PropertyInput::from),
1503 )
1504 }
1505
1506 pub fn vector_search_edges_with(
1508 self,
1509 label: impl Into<String>,
1510 property: impl Into<String>,
1511 query_vector: impl Into<PropertyInput>,
1512 k: impl Into<StreamBound>,
1513 tenant_value: Option<PropertyInput>,
1514 ) -> Traversal<OnEdges> {
1515 self.source(AstNode::VectorSearchEdges {
1516 label: label.into(),
1517 property: property.into(),
1518 tenant_value,
1519 query_vector: query_vector.into(),
1520 k: k.into(),
1521 })
1522 }
1523
1524 pub fn text_search_edges(
1526 self,
1527 label: impl Into<String>,
1528 property: impl Into<String>,
1529 query_text: impl Into<String>,
1530 k: usize,
1531 tenant_value: Option<PropertyValue>,
1532 ) -> Traversal<OnEdges> {
1533 self.text_search_edges_with(
1534 label,
1535 property,
1536 PropertyInput::from(query_text.into()),
1537 k,
1538 tenant_value.map(PropertyInput::from),
1539 )
1540 }
1541
1542 pub fn text_search_edges_with(
1544 self,
1545 label: impl Into<String>,
1546 property: impl Into<String>,
1547 query_text: impl Into<PropertyInput>,
1548 k: impl Into<StreamBound>,
1549 tenant_value: Option<PropertyInput>,
1550 ) -> Traversal<OnEdges> {
1551 self.source(AstNode::TextSearchEdges {
1552 label: label.into(),
1553 property: property.into(),
1554 tenant_value,
1555 query_text: query_text.into(),
1556 k: k.into(),
1557 })
1558 }
1559
1560 pub fn shortest_path(
1562 self,
1563 source: impl Into<NodeRef>,
1564 target: impl Into<NodeRef>,
1565 max_depth: usize,
1566 ) -> Traversal<Terminal> {
1567 self.shortest_path_with(
1568 source,
1569 target,
1570 None::<String>,
1571 ShortestPathDirection::Out,
1572 max_depth,
1573 )
1574 }
1575
1576 pub fn shortest_path_with(
1578 self,
1579 source: impl Into<NodeRef>,
1580 target: impl Into<NodeRef>,
1581 label: Option<impl Into<String>>,
1582 direction: ShortestPathDirection,
1583 max_depth: usize,
1584 ) -> Traversal<Terminal> {
1585 self.source(AstNode::ShortestPath {
1586 source: source.into(),
1587 target: target.into(),
1588 label: label.map(Into::into),
1589 direction,
1590 max_depth,
1591 })
1592 }
1593
1594 pub fn create_index_if_not_exists(self, spec: IndexSpec) -> Traversal<Terminal, WriteEnabled> {
1596 self.mutation_source(AstNode::CreateIndex {
1597 spec,
1598 if_not_exists: true,
1599 })
1600 }
1601
1602 pub fn drop_index(self, spec: IndexSpec) -> Traversal<Terminal, WriteEnabled> {
1604 self.mutation_source(AstNode::DropIndex { spec })
1605 }
1606
1607 pub fn get_index_operation(self, operation_id: impl Into<String>) -> Traversal<Terminal> {
1609 self.source(AstNode::GetIndexOperation {
1610 operation_id: operation_id.into(),
1611 })
1612 }
1613
1614 pub fn retry_index_operation(
1616 self,
1617 operation_id: impl Into<String>,
1618 ) -> Traversal<Terminal, WriteEnabled> {
1619 self.mutation_source(AstNode::RetryIndexOperation {
1620 operation_id: operation_id.into(),
1621 })
1622 }
1623
1624 pub fn abort_index_operation(
1626 self,
1627 operation_id: impl Into<String>,
1628 ) -> Traversal<Terminal, WriteEnabled> {
1629 self.mutation_source(AstNode::AbortIndexOperation {
1630 operation_id: operation_id.into(),
1631 })
1632 }
1633
1634 pub fn create_vector_index_nodes(
1636 self,
1637 label: impl Into<String>,
1638 property: impl Into<String>,
1639 dimension: std::num::NonZeroUsize,
1640 metric: crate::index::VectorDistanceMetric,
1641 tenant_property: Option<impl Into<String>>,
1642 ) -> Traversal<Terminal, WriteEnabled> {
1643 self.create_index_if_not_exists(IndexSpec::node_vector(
1644 label,
1645 property,
1646 dimension,
1647 metric,
1648 tenant_property,
1649 ))
1650 }
1651
1652 pub fn create_vector_index_edges(
1654 self,
1655 label: impl Into<String>,
1656 property: impl Into<String>,
1657 dimension: std::num::NonZeroUsize,
1658 metric: crate::index::VectorDistanceMetric,
1659 tenant_property: Option<impl Into<String>>,
1660 ) -> Traversal<Terminal, WriteEnabled> {
1661 self.create_index_if_not_exists(IndexSpec::edge_vector(
1662 label,
1663 property,
1664 dimension,
1665 metric,
1666 tenant_property,
1667 ))
1668 }
1669
1670 pub fn create_text_index_nodes(
1672 self,
1673 label: impl Into<String>,
1674 property: impl Into<String>,
1675 tenant_property: Option<impl Into<String>>,
1676 ) -> Traversal<Terminal, WriteEnabled> {
1677 self.create_index_if_not_exists(IndexSpec::node_text(label, property, tenant_property))
1678 }
1679
1680 pub fn create_text_index_edges(
1682 self,
1683 label: impl Into<String>,
1684 property: impl Into<String>,
1685 tenant_property: Option<impl Into<String>>,
1686 ) -> Traversal<Terminal, WriteEnabled> {
1687 self.create_index_if_not_exists(IndexSpec::edge_text(label, property, tenant_property))
1688 }
1689
1690 pub fn add_n<K, V>(
1692 self,
1693 label: impl Into<String>,
1694 properties: Vec<(K, V)>,
1695 ) -> Traversal<OnNodes, WriteEnabled>
1696 where
1697 K: Into<String>,
1698 V: Into<PropertyInput>,
1699 {
1700 self.mutation_source(AstNode::AddN {
1701 input: None,
1702 label: label.into(),
1703 properties: collect_properties(properties),
1704 })
1705 }
1706
1707 pub fn inject(self, var_name: impl Into<String>) -> Traversal<OnNodes, ReadOnly> {
1709 self.source(AstNode::Inject {
1710 input: None,
1711 variable: var_name.into(),
1712 })
1713 }
1714
1715 pub fn drop_edge_by_id(self, edges: impl Into<EdgeRef>) -> Traversal<OnNodes, WriteEnabled> {
1717 self.mutation_source(AstNode::DropEdgeById {
1718 input: None,
1719 edges: edges.into(),
1720 })
1721 }
1722}
1723
1724fn collect_properties<K, V>(properties: Vec<(K, V)>) -> Vec<(String, PropertyInput)>
1725where
1726 K: Into<String>,
1727 V: Into<PropertyInput>,
1728{
1729 properties
1730 .into_iter()
1731 .map(|(key, value)| (key.into(), value.into()))
1732 .collect()
1733}
1734
1735impl<M: MutationMode> Traversal<OnNodes, M> {
1736 pub fn out(self, label: Option<impl Into<String>>) -> Traversal<OnNodes, M> {
1738 self.push(Operation::Out(label.map(Into::into)))
1739 }
1740
1741 pub fn in_(self, label: Option<impl Into<String>>) -> Traversal<OnNodes, M> {
1743 self.push(Operation::In(label.map(Into::into)))
1744 }
1745
1746 pub fn both(self, label: Option<impl Into<String>>) -> Traversal<OnNodes, M> {
1748 self.push(Operation::Both(label.map(Into::into)))
1749 }
1750
1751 pub fn out_e(self, label: Option<impl Into<String>>) -> Traversal<OnEdges, M> {
1753 self.push(Operation::OutE(label.map(Into::into)))
1754 }
1755
1756 pub fn in_e(self, label: Option<impl Into<String>>) -> Traversal<OnEdges, M> {
1758 self.push(Operation::InE(label.map(Into::into)))
1759 }
1760
1761 pub fn both_e(self, label: Option<impl Into<String>>) -> Traversal<OnEdges, M> {
1763 self.push(Operation::BothE(label.map(Into::into)))
1764 }
1765
1766 pub fn has(self, property: impl Into<String>, value: impl Into<PropertyValue>) -> Self {
1768 self.push(Operation::Has(property.into(), value.into()))
1769 }
1770
1771 pub fn has_label(self, label: impl Into<String>) -> Self {
1773 self.push(Operation::HasLabel(label.into()))
1774 }
1775
1776 pub fn has_key(self, property: impl Into<String>) -> Self {
1778 self.push(Operation::HasKey(property.into()))
1779 }
1780
1781 pub fn where_(self, predicate: Predicate) -> Self {
1783 self.push(Operation::Where(predicate))
1784 }
1785
1786 pub fn vector_search(
1788 self,
1789 label: impl Into<String>,
1790 property: impl Into<String>,
1791 query_vector: Vec<f32>,
1792 k: usize,
1793 tenant_value: Option<PropertyValue>,
1794 ) -> Self {
1795 self.vector_search_with(
1796 label,
1797 property,
1798 query_vector,
1799 k,
1800 tenant_value.map(PropertyInput::from),
1801 )
1802 }
1803
1804 pub fn vector_search_with(
1806 self,
1807 label: impl Into<String>,
1808 property: impl Into<String>,
1809 query_vector: impl Into<PropertyInput>,
1810 k: impl Into<StreamBound>,
1811 tenant_value: Option<PropertyInput>,
1812 ) -> Self {
1813 self.push(Operation::VectorSearchNodesWithin {
1814 label: label.into(),
1815 property: property.into(),
1816 tenant_value,
1817 query_vector: query_vector.into(),
1818 k: k.into(),
1819 })
1820 }
1821
1822 pub fn dedup(self) -> Self {
1824 self.push(Operation::Dedup)
1825 }
1826
1827 pub fn within(self, var_name: impl Into<String>) -> Self {
1829 self.push(Operation::Within(var_name.into()))
1830 }
1831
1832 pub fn without(self, var_name: impl Into<String>) -> Self {
1834 self.push(Operation::Without(var_name.into()))
1835 }
1836
1837 pub fn limit(self, n: impl Into<StreamBound>) -> Self {
1839 self.push(Operation::Limit(n.into()))
1840 }
1841
1842 pub fn skip(self, n: impl Into<StreamBound>) -> Self {
1844 self.push(Operation::Skip(n.into()))
1845 }
1846
1847 pub fn range(self, start: impl Into<StreamBound>, end: impl Into<StreamBound>) -> Self {
1849 self.push(Operation::Range(start.into(), end.into()))
1850 }
1851
1852 pub fn as_(self, name: impl Into<String>) -> Self {
1854 self.push(Operation::As(name.into()))
1855 }
1856
1857 pub fn store(self, name: impl Into<String>) -> Self {
1859 self.push(Operation::Store(name.into()))
1860 }
1861
1862 pub fn select(self, name: impl Into<String>) -> Self {
1864 self.push(Operation::Select(name.into()))
1865 }
1866
1867 pub fn bind(self, name: impl Into<String>) -> Self {
1869 self.push(Operation::Bind(validate_binding_name(name)))
1870 }
1871
1872 pub fn inject(self, var_name: impl Into<String>) -> Self {
1874 self.push(Operation::Inject(var_name.into()))
1875 }
1876
1877 pub fn count(self) -> Traversal<Terminal, M> {
1879 self.push(Operation::Count)
1880 }
1881
1882 pub fn exists(self) -> Traversal<Terminal, M> {
1884 self.push(Operation::Exists)
1885 }
1886
1887 pub fn id(self) -> Traversal<Terminal, M> {
1889 self.push(Operation::Id)
1890 }
1891
1892 pub fn label(self) -> Traversal<Terminal, M> {
1894 self.push(Operation::Label)
1895 }
1896
1897 pub fn values(self, properties: Vec<impl Into<String>>) -> Traversal<Terminal, M> {
1899 self.push(Operation::Values(
1900 properties.into_iter().map(Into::into).collect(),
1901 ))
1902 }
1903
1904 pub fn value_map(self, properties: Option<Vec<impl Into<String>>>) -> Traversal<Terminal, M> {
1906 self.push(Operation::ValueMap(
1907 properties.map(|items| items.into_iter().map(Into::into).collect()),
1908 ))
1909 }
1910
1911 pub fn project<P>(self, projections: Vec<P>) -> Traversal<Terminal, M>
1913 where
1914 P: Into<Projection>,
1915 {
1916 self.push(Operation::Project(
1917 projections.into_iter().map(Into::into).collect(),
1918 ))
1919 }
1920
1921 pub fn project_bindings(self, projections: Vec<BindingProjection>) -> Traversal<Terminal, M> {
1923 self.push(Operation::ProjectBindings {
1924 projections: validate_binding_projections(projections),
1925 distinct: false,
1926 })
1927 }
1928
1929 pub fn project_distinct_bindings(
1931 self,
1932 projections: Vec<BindingProjection>,
1933 ) -> Traversal<Terminal, M> {
1934 self.push(Operation::ProjectBindings {
1935 projections: validate_binding_projections(projections),
1936 distinct: true,
1937 })
1938 }
1939
1940 pub fn order_by(self, property: impl Into<String>, order: Order) -> Self {
1942 self.push(Operation::OrderBy(property.into(), order))
1943 }
1944
1945 pub fn order_by_multiple(self, orderings: Vec<(impl Into<String>, Order)>) -> Self {
1947 self.push(Operation::OrderByMultiple(
1948 orderings
1949 .into_iter()
1950 .map(|(property, order)| (property.into(), order))
1951 .collect(),
1952 ))
1953 }
1954
1955 pub fn repeat(self, config: RepeatConfig) -> Self {
1957 self.push(Operation::Repeat(config))
1958 }
1959
1960 pub fn union(self, traversals: Vec<SubTraversal>) -> Self {
1962 self.push(Operation::Union(traversals))
1963 }
1964
1965 pub fn choose(
1967 self,
1968 condition: Predicate,
1969 then_traversal: SubTraversal,
1970 else_traversal: Option<SubTraversal>,
1971 ) -> Self {
1972 self.push(Operation::Choose {
1973 condition,
1974 then_traversal,
1975 else_traversal,
1976 })
1977 }
1978
1979 pub fn coalesce(self, traversals: Vec<SubTraversal>) -> Self {
1981 self.push(Operation::Coalesce(traversals))
1982 }
1983
1984 pub fn optional(self, traversal: SubTraversal) -> Self {
1986 self.push(Operation::Optional(traversal))
1987 }
1988
1989 pub fn group(self, property: impl Into<String>) -> Traversal<Terminal, M> {
1991 self.push(Operation::Group(property.into()))
1992 }
1993
1994 pub fn group_count(self, property: impl Into<String>) -> Traversal<Terminal, M> {
1996 self.push(Operation::GroupCount(property.into()))
1997 }
1998
1999 pub fn aggregate_by(
2001 self,
2002 function: AggregateFunction,
2003 property: impl Into<String>,
2004 ) -> Traversal<Terminal, M> {
2005 self.push(Operation::AggregateBy(function, property.into()))
2006 }
2007
2008 pub fn fold(self) -> Self {
2010 self.push(Operation::Fold)
2011 }
2012
2013 pub fn unfold(self) -> Self {
2015 self.push(Operation::Unfold)
2016 }
2017
2018 pub fn path(self) -> Self {
2020 self.push(Operation::Path)
2021 }
2022
2023 pub fn simple_path(self) -> Self {
2025 self.push(Operation::SimplePath)
2026 }
2027
2028 pub fn with_sack(self, initial: PropertyValue) -> Self {
2030 self.push(Operation::WithSack(initial))
2031 }
2032
2033 pub fn sack_set(self, property: impl Into<String>) -> Self {
2035 self.push(Operation::SackSet(property.into()))
2036 }
2037
2038 pub fn sack_add(self, property: impl Into<String>) -> Self {
2040 self.push(Operation::SackAdd(property.into()))
2041 }
2042
2043 pub fn sack_get(self) -> Self {
2045 self.push(Operation::SackGet)
2046 }
2047
2048 pub fn add_n<K, V>(
2050 self,
2051 label: impl Into<String>,
2052 properties: Vec<(K, V)>,
2053 ) -> Traversal<OnNodes, WriteEnabled>
2054 where
2055 K: Into<String>,
2056 V: Into<PropertyInput>,
2057 {
2058 self.push_mutation(Operation::AddN {
2059 label: label.into(),
2060 properties: collect_properties(properties),
2061 })
2062 }
2063
2064 pub fn add_e<K, V>(
2066 self,
2067 label: impl Into<String>,
2068 to: impl Into<NodeRef>,
2069 properties: Vec<(K, V)>,
2070 ) -> Traversal<OnNodes, WriteEnabled>
2071 where
2072 K: Into<String>,
2073 V: Into<PropertyInput>,
2074 {
2075 self.push_mutation(Operation::AddE {
2076 label: label.into(),
2077 to: to.into(),
2078 properties: collect_properties(properties),
2079 })
2080 }
2081
2082 pub fn set_property(
2084 self,
2085 name: impl Into<String>,
2086 value: impl Into<PropertyInput>,
2087 ) -> Traversal<OnNodes, WriteEnabled> {
2088 self.push_mutation(Operation::SetProperty(name.into(), value.into()))
2089 }
2090
2091 pub fn remove_property(self, name: impl Into<String>) -> Traversal<OnNodes, WriteEnabled> {
2093 self.push_mutation(Operation::RemoveProperty(name.into()))
2094 }
2095
2096 pub fn drop(self) -> Traversal<OnNodes, WriteEnabled> {
2098 self.push_mutation(Operation::Drop)
2099 }
2100
2101 pub fn drop_edge(self, to: impl Into<NodeRef>) -> Traversal<OnNodes, WriteEnabled> {
2103 self.push_mutation(Operation::DropEdge(to.into()))
2104 }
2105
2106 pub fn drop_edge_labeled(
2108 self,
2109 to: impl Into<NodeRef>,
2110 label: impl Into<String>,
2111 ) -> Traversal<OnNodes, WriteEnabled> {
2112 self.push_mutation(Operation::DropEdgeLabeled {
2113 to: to.into(),
2114 label: label.into(),
2115 })
2116 }
2117
2118 pub fn drop_edge_by_id(self, edges: impl Into<EdgeRef>) -> Traversal<OnNodes, WriteEnabled> {
2120 self.push_mutation(Operation::DropEdgeById(edges.into()))
2121 }
2122}
2123
2124impl<M: MutationMode> Traversal<OnEdges, M> {
2125 pub fn out_n(self) -> Traversal<OnNodes, M> {
2127 self.push(Operation::OutN)
2128 }
2129
2130 pub fn in_n(self) -> Traversal<OnNodes, M> {
2132 self.push(Operation::InN)
2133 }
2134
2135 pub fn other_n(self) -> Traversal<OnNodes, M> {
2137 self.push(Operation::OtherN)
2138 }
2139
2140 pub fn has(self, property: impl Into<String>, value: impl Into<PropertyValue>) -> Self {
2142 self.push(Operation::Has(property.into(), value.into()))
2143 }
2144
2145 pub fn has_label(self, label: impl Into<String>) -> Self {
2147 self.push(Operation::HasLabel(label.into()))
2148 }
2149
2150 pub fn has_key(self, property: impl Into<String>) -> Self {
2152 self.push(Operation::HasKey(property.into()))
2153 }
2154
2155 pub fn where_(self, predicate: Predicate) -> Self {
2157 self.push(Operation::Where(predicate))
2158 }
2159
2160 pub fn vector_search(
2162 self,
2163 label: impl Into<String>,
2164 property: impl Into<String>,
2165 query_vector: Vec<f32>,
2166 k: usize,
2167 tenant_value: Option<PropertyValue>,
2168 ) -> Self {
2169 self.vector_search_with(
2170 label,
2171 property,
2172 query_vector,
2173 k,
2174 tenant_value.map(PropertyInput::from),
2175 )
2176 }
2177
2178 pub fn vector_search_with(
2180 self,
2181 label: impl Into<String>,
2182 property: impl Into<String>,
2183 query_vector: impl Into<PropertyInput>,
2184 k: impl Into<StreamBound>,
2185 tenant_value: Option<PropertyInput>,
2186 ) -> Self {
2187 self.push(Operation::VectorSearchEdgesWithin {
2188 label: label.into(),
2189 property: property.into(),
2190 tenant_value,
2191 query_vector: query_vector.into(),
2192 k: k.into(),
2193 })
2194 }
2195
2196 pub fn edge_has(self, property: impl Into<String>, value: impl Into<PropertyInput>) -> Self {
2198 self.push(Operation::EdgeHas(property.into(), value.into()))
2199 }
2200
2201 pub fn edge_has_label(self, label: impl Into<String>) -> Self {
2203 self.push(Operation::EdgeHasLabel(label.into()))
2204 }
2205
2206 pub fn set_property(
2208 self,
2209 name: impl Into<String>,
2210 value: impl Into<PropertyInput>,
2211 ) -> Traversal<OnEdges, WriteEnabled> {
2212 self.push_mutation(Operation::SetProperty(name.into(), value.into()))
2213 }
2214
2215 pub fn remove_property(self, name: impl Into<String>) -> Traversal<OnEdges, WriteEnabled> {
2217 self.push_mutation(Operation::RemoveProperty(name.into()))
2218 }
2219
2220 pub fn dedup(self) -> Self {
2222 self.push(Operation::Dedup)
2223 }
2224
2225 pub fn limit(self, n: impl Into<StreamBound>) -> Self {
2227 self.push(Operation::Limit(n.into()))
2228 }
2229
2230 pub fn skip(self, n: impl Into<StreamBound>) -> Self {
2232 self.push(Operation::Skip(n.into()))
2233 }
2234
2235 pub fn range(self, start: impl Into<StreamBound>, end: impl Into<StreamBound>) -> Self {
2237 self.push(Operation::Range(start.into(), end.into()))
2238 }
2239
2240 pub fn as_(self, name: impl Into<String>) -> Self {
2242 self.push(Operation::As(name.into()))
2243 }
2244
2245 pub fn store(self, name: impl Into<String>) -> Self {
2247 self.push(Operation::Store(name.into()))
2248 }
2249
2250 pub fn bind(self, name: impl Into<String>) -> Self {
2252 self.push(Operation::Bind(validate_binding_name(name)))
2253 }
2254
2255 pub fn count(self) -> Traversal<Terminal, M> {
2257 self.push(Operation::Count)
2258 }
2259
2260 pub fn exists(self) -> Traversal<Terminal, M> {
2262 self.push(Operation::Exists)
2263 }
2264
2265 pub fn id(self) -> Traversal<Terminal, M> {
2267 self.push(Operation::Id)
2268 }
2269
2270 pub fn label(self) -> Traversal<Terminal, M> {
2272 self.push(Operation::Label)
2273 }
2274
2275 pub fn values(self, properties: Vec<impl Into<String>>) -> Traversal<Terminal, M> {
2277 self.push(Operation::Values(
2278 properties.into_iter().map(Into::into).collect(),
2279 ))
2280 }
2281
2282 pub fn value_map(self, properties: Option<Vec<impl Into<String>>>) -> Traversal<Terminal, M> {
2284 self.push(Operation::ValueMap(
2285 properties.map(|items| items.into_iter().map(Into::into).collect()),
2286 ))
2287 }
2288
2289 pub fn project<P>(self, projections: Vec<P>) -> Traversal<Terminal, M>
2291 where
2292 P: Into<Projection>,
2293 {
2294 self.push(Operation::Project(
2295 projections.into_iter().map(Into::into).collect(),
2296 ))
2297 }
2298
2299 pub fn project_bindings(self, projections: Vec<BindingProjection>) -> Traversal<Terminal, M> {
2301 self.push(Operation::ProjectBindings {
2302 projections: validate_binding_projections(projections),
2303 distinct: false,
2304 })
2305 }
2306
2307 pub fn project_distinct_bindings(
2309 self,
2310 projections: Vec<BindingProjection>,
2311 ) -> Traversal<Terminal, M> {
2312 self.push(Operation::ProjectBindings {
2313 projections: validate_binding_projections(projections),
2314 distinct: true,
2315 })
2316 }
2317
2318 pub fn edge_properties(self) -> Traversal<Terminal, M> {
2320 self.push(Operation::EdgeProperties)
2321 }
2322
2323 pub fn order_by(self, property: impl Into<String>, order: Order) -> Self {
2325 self.push(Operation::OrderBy(property.into(), order))
2326 }
2327}
2328
2329pub fn g() -> Traversal<Empty> {
2331 Traversal::new()
2332}