Skip to main content

sz_orm_graph/
model.rs

1//! # Model — 声明式建模
2//!
3//! GraphNodeModel + GraphRelationModel + GraphPropertyDef
4
5use crate::query::GraphResult;
6
7/// 属性值类型
8#[derive(Debug, Clone, PartialEq, Eq)]
9pub enum GraphValueType {
10    String,
11    Integer,
12    Float,
13    Boolean,
14    DateTime,
15    Duration,
16}
17
18/// 图属性定义
19#[derive(Debug, Clone)]
20pub struct GraphPropertyDef {
21    pub name: String,
22    pub value_type: GraphValueType,
23    pub nullable: bool,
24    pub default: Option<serde_json::Value>,
25}
26
27impl GraphPropertyDef {
28    pub fn new(name: &str, value_type: GraphValueType) -> Self {
29        Self {
30            name: name.to_string(),
31            value_type,
32            nullable: false,
33            default: None,
34        }
35    }
36
37    pub fn nullable(mut self) -> Self {
38        self.nullable = true;
39        self
40    }
41
42    pub fn with_default(mut self, value: serde_json::Value) -> Self {
43        self.default = Some(value);
44        self
45    }
46}
47
48/// 关系方向
49#[derive(Debug, Clone, PartialEq, Eq)]
50pub enum RelationDirection {
51    Outgoing,
52    Incoming,
53    Undirected,
54}
55
56/// 图节点模型(声明式建模)
57#[derive(Debug, Clone)]
58pub struct GraphNodeModel {
59    pub label: String,
60    pub properties: Vec<GraphPropertyDef>,
61}
62
63impl GraphNodeModel {
64    pub fn new(label: &str) -> Self {
65        Self {
66            label: label.to_string(),
67            properties: Vec::new(),
68        }
69    }
70
71    pub fn property(mut self, name: &str, value_type: GraphValueType) -> Self {
72        self.properties
73            .push(GraphPropertyDef::new(name, value_type));
74        self
75    }
76
77    /// 生成 MATCH 子句
78    pub fn match_clause(&self, alias: &str) -> String {
79        format!("MATCH ({}:{})", alias, self.label)
80    }
81
82    /// 生成 CREATE 子句
83    pub fn create_clause(&self, alias: &str) -> String {
84        let prop_names: Vec<String> = self
85            .properties
86            .iter()
87            .map(|p| format!("{}: ${}", p.name, p.name))
88            .collect();
89        if prop_names.is_empty() {
90            format!("CREATE ({}:{})", alias, self.label)
91        } else {
92            format!(
93                "CREATE ({}:{} {{{}}})",
94                alias,
95                self.label,
96                prop_names.join(", ")
97            )
98        }
99    }
100}
101
102/// 图关系模型
103#[derive(Debug, Clone)]
104pub struct GraphRelationModel {
105    pub rel_type: String,
106    pub direction: RelationDirection,
107    pub from_label: String,
108    pub to_label: String,
109    pub properties: Vec<GraphPropertyDef>,
110}
111
112impl GraphRelationModel {
113    pub fn new(rel_type: &str, from_label: &str, to_label: &str) -> Self {
114        Self {
115            rel_type: rel_type.to_string(),
116            direction: RelationDirection::Outgoing,
117            from_label: from_label.to_string(),
118            to_label: to_label.to_string(),
119            properties: Vec::new(),
120        }
121    }
122
123    pub fn direction(mut self, dir: RelationDirection) -> Self {
124        self.direction = dir;
125        self
126    }
127
128    pub fn property(mut self, name: &str, value_type: GraphValueType) -> Self {
129        self.properties
130            .push(GraphPropertyDef::new(name, value_type));
131        self
132    }
133
134    /// 生成 MATCH 关系子句
135    pub fn match_clause(&self, from: &str, rel: &str, to: &str) -> String {
136        match self.direction {
137            RelationDirection::Outgoing => format!(
138                "MATCH ({from}:{from_label})-[{rel}:{rel_type}]->({to}:{to_label})",
139                from = from,
140                from_label = self.from_label,
141                rel = rel,
142                rel_type = self.rel_type,
143                to = to,
144                to_label = self.to_label
145            ),
146            RelationDirection::Incoming => format!(
147                "MATCH ({from}:{from_label})<-[{rel}:{rel_type}]-({to}:{to_label})",
148                from = from,
149                from_label = self.from_label,
150                rel = rel,
151                rel_type = self.rel_type,
152                to = to,
153                to_label = self.to_label
154            ),
155            RelationDirection::Undirected => format!(
156                "MATCH ({from}:{from_label})-[{rel}:{rel_type}]-({to}:{to_label})",
157                from = from,
158                from_label = self.from_label,
159                rel = rel,
160                rel_type = self.rel_type,
161                to = to,
162                to_label = self.to_label
163            ),
164        }
165    }
166}
167
168/// 从 GraphResult 提取节点属性
169pub fn extract_node_properties(result: &GraphResult) -> Option<&serde_json::Value> {
170    result.as_node().map(|n| &n.properties)
171}