1use crate::api_core::{CoreError, CoreResult, IndexTarget, PropertyDef, SpaceConfig};
6use linkrs_core::types::{
7 EdgeTypeInfo, Index, IndexField, IndexStatus, IndexType, SpaceInfo, TagInfo,
8};
9use linkrs_storage::StorageClient;
10use parking_lot::RwLock;
11use std::sync::Arc;
12
13pub struct SchemaApi<S: StorageClient> {
15 storage: Arc<RwLock<S>>,
16}
17
18impl<S: StorageClient> SchemaApi<S> {
19 pub fn new(storage: Arc<RwLock<S>>) -> Self {
21 Self { storage }
22 }
23
24 pub fn create_space(&self, name: &str, config: SpaceConfig) -> CoreResult<()> {
30 let mut space_info = SpaceInfo::new(name.to_string())
31 .with_vid_type(config.vid_type)
32 .with_comment(config.comment);
33
34 let mut storage = self.storage.write();
35 storage
36 .create_space(&mut space_info)
37 .map_err(|e| CoreError::StorageError(e.to_string()))?;
38
39 log::info!("Created graph space successfully: {}", name);
40 Ok(())
41 }
42
43 pub fn drop_space(&self, name: &str) -> CoreResult<()> {
48 let mut storage = self.storage.write();
49 let result = storage
50 .drop_space(name)
51 .map_err(|e| CoreError::StorageError(e.to_string()))?;
52
53 if result {
54 log::info!("Deleted graph space successfully: {}", name);
55 Ok(())
56 } else {
57 Err(CoreError::NotFound(format!(
58 "Graph space '{}' does not exist",
59 name
60 )))
61 }
62 }
63
64 pub fn use_space(&self, name: &str) -> CoreResult<u64> {
72 let storage = self.storage.write();
73 let space_id = storage
74 .get_space_id(name)
75 .map_err(|e| CoreError::StorageError(e.to_string()))?;
76
77 log::info!("Using graph space: {} (ID: {})", name, space_id);
78 Ok(space_id)
79 }
80
81 pub fn list_spaces(&self) -> CoreResult<Vec<linkrs_core::types::SpaceInfo>> {
86 let storage = self.storage.write();
87 let spaces = storage
88 .list_spaces()
89 .map_err(|e| CoreError::StorageError(e.to_string()))?;
90
91 log::info!("Listed {} graph spaces", spaces.len());
92 Ok(spaces)
93 }
94
95 pub fn create_tag(
102 &self,
103 space_id: u64,
104 name: &str,
105 properties: Vec<PropertyDef>,
106 ) -> CoreResult<()> {
107 let space_name = self.get_space_name_by_id(space_id)?;
109
110 let core_properties: Vec<linkrs_core::types::PropertyDef> =
112 properties.into_iter().map(|p| p.into()).collect();
113
114 let tag_info = TagInfo::new(name.to_string()).with_properties(core_properties);
115
116 let mut storage = self.storage.write();
117 let result = storage
118 .create_tag(&space_name, &tag_info)
119 .map_err(|e| CoreError::StorageError(e.to_string()))?;
120
121 log::info!(
122 "Created tag successfully: {} in space {} with id {}",
123 name,
124 space_id,
125 result
126 );
127 Ok(())
128 }
129
130 pub fn drop_tag(&self, space_id: u64, name: &str) -> CoreResult<()> {
136 let space_name = self.get_space_name_by_id(space_id)?;
137
138 let mut storage = self.storage.write();
139 let result = storage
140 .drop_tag(&space_name, name)
141 .map_err(|e| CoreError::StorageError(e.to_string()))?;
142
143 if result {
144 log::info!("Deleted tag successfully: {} from space {}", name, space_id);
145 Ok(())
146 } else {
147 Err(CoreError::NotFound(format!(
148 "Tag '{}' does not exist",
149 name
150 )))
151 }
152 }
153
154 pub fn alter_tag(
162 &self,
163 space_id: u64,
164 tag_name: &str,
165 additions: Vec<PropertyDef>,
166 deletions: Vec<String>,
167 ) -> CoreResult<()> {
168 let space_name = self.get_space_name_by_id(space_id)?;
169
170 let core_additions: Vec<linkrs_core::types::PropertyDef> =
172 additions.into_iter().map(|p| p.into()).collect();
173
174 let mut storage = self.storage.write();
175 let result = storage
176 .alter_tag(&space_name, tag_name, core_additions, deletions, None)
177 .map_err(|e| CoreError::StorageError(e.to_string()))?;
178
179 if result {
180 log::info!(
181 "Altered tag successfully: {} in space {}",
182 tag_name,
183 space_id
184 );
185 Ok(())
186 } else {
187 Err(CoreError::NotFound(format!(
188 "Tag '{}' does not exist or no changes made",
189 tag_name
190 )))
191 }
192 }
193
194 pub fn create_edge_type(
201 &self,
202 space_id: u64,
203 name: &str,
204 properties: Vec<PropertyDef>,
205 ) -> CoreResult<()> {
206 let space_name = self.get_space_name_by_id(space_id)?;
207
208 let core_properties: Vec<linkrs_core::types::PropertyDef> =
210 properties.into_iter().map(|p| p.into()).collect();
211
212 let edge_type_info = EdgeTypeInfo::new(name.to_string()).with_properties(core_properties);
213
214 let mut storage = self.storage.write();
215 let result = storage
216 .create_edge_type(&space_name, &edge_type_info)
217 .map_err(|e| CoreError::StorageError(e.to_string()))?;
218
219 log::info!(
220 "Created edge type successfully: {} in space {} with id {}",
221 name,
222 space_id,
223 result
224 );
225 Ok(())
226 }
227
228 pub fn drop_edge_type(&self, space_id: u64, name: &str) -> CoreResult<()> {
234 let space_name = self.get_space_name_by_id(space_id)?;
235
236 let mut storage = self.storage.write();
237 let result = storage
238 .drop_edge_type(&space_name, name)
239 .map_err(|e| CoreError::StorageError(e.to_string()))?;
240
241 if result {
242 log::info!(
243 "Deleted edge type successfully: {} from space {}",
244 name,
245 space_id
246 );
247 Ok(())
248 } else {
249 Err(CoreError::NotFound(format!(
250 "Edge type '{}' does not exist",
251 name
252 )))
253 }
254 }
255
256 pub fn alter_edge_type(
264 &self,
265 space_id: u64,
266 edge_type_name: &str,
267 additions: Vec<PropertyDef>,
268 deletions: Vec<String>,
269 ) -> CoreResult<()> {
270 let space_name = self.get_space_name_by_id(space_id)?;
271
272 let core_additions: Vec<linkrs_core::types::PropertyDef> =
274 additions.into_iter().map(|p| p.into()).collect();
275
276 let mut storage = self.storage.write();
277 let result = storage
278 .alter_edge_type(&space_name, edge_type_name, core_additions, deletions, None)
279 .map_err(|e| CoreError::StorageError(e.to_string()))?;
280
281 if result {
282 log::info!(
283 "Altered edge type successfully: {} in space {}",
284 edge_type_name,
285 space_id
286 );
287 Ok(())
288 } else {
289 Err(CoreError::NotFound(format!(
290 "Edge type '{}' does not exist or no changes made",
291 edge_type_name
292 )))
293 }
294 }
295
296 pub fn create_index(&self, space_id: u64, name: &str, target: IndexTarget) -> CoreResult<()> {
303 let space_name = self.get_space_name_by_id(space_id)?;
304
305 let (schema_name, fields, index_type) = match target {
307 IndexTarget::Tag {
308 name: tag_name,
309 fields,
310 } => {
311 let storage = self.storage.read();
313 let tag_info = storage
314 .get_tag(&space_name, &tag_name)
315 .map_err(|e| CoreError::StorageError(e.to_string()))?;
316
317 let tag_info = tag_info.ok_or_else(|| {
318 CoreError::NotFound(format!("Tag '{}' does not exist", tag_name))
319 })?;
320
321 let index_fields = self.build_index_fields(&fields, &tag_info.properties)?;
323 (tag_name, index_fields, IndexType::TagIndex)
324 }
325 IndexTarget::Edge {
326 name: edge_name,
327 fields,
328 } => {
329 let storage = self.storage.read();
331 let edge_info = storage
332 .get_edge_type(&space_name, &edge_name)
333 .map_err(|e| CoreError::StorageError(e.to_string()))?;
334
335 let edge_info = edge_info.ok_or_else(|| {
336 CoreError::NotFound(format!("Edge type '{}' does not exist", edge_name))
337 })?;
338
339 let index_fields = self.build_index_fields(&fields, &edge_info.properties)?;
341 (edge_name, index_fields, IndexType::EdgeIndex)
342 }
343 };
344
345 let mut storage = self.storage.write();
347 let result = match index_type {
348 IndexType::TagIndex => {
349 let index = Index {
350 id: 0, name: name.to_string(),
352 space_id,
353 schema_name,
354 fields,
355 properties: Vec::new(),
356 index_type: IndexType::TagIndex,
357 status: IndexStatus::Active,
358 is_unique: false,
359 comment: None,
360 covering: false,
361 partial_condition: None,
362 };
363 storage.create_tag_index(&space_name, &index)
364 }
365 IndexType::EdgeIndex => {
366 let index = Index {
367 id: 0, name: name.to_string(),
369 space_id,
370 schema_name,
371 fields,
372 properties: Vec::new(),
373 index_type: IndexType::EdgeIndex,
374 status: IndexStatus::Active,
375 is_unique: false,
376 comment: None,
377 covering: false,
378 partial_condition: None,
379 };
380 storage.create_edge_index(&space_name, &index)
381 }
382 }
383 .map_err(|e| CoreError::StorageError(e.to_string()))?;
384
385 if result {
386 log::info!(
387 "Created index successfully: {} in space {:?}",
388 name,
389 space_id
390 );
391 Ok(())
392 } else {
393 Err(CoreError::SchemaOperationFailed(format!(
394 "Failed to create index '{}'",
395 name
396 )))
397 }
398 }
399
400 pub fn drop_index(&self, space_id: u64, name: &str) -> CoreResult<()> {
406 let space_name = self.get_space_name_by_id(space_id)?;
407
408 let mut storage = self.storage.write();
409
410 if let Ok(Some(_)) = storage.get_tag_index(&space_name, name) {
412 let result = storage
413 .drop_tag_index(&space_name, name)
414 .map_err(|e| CoreError::StorageError(e.to_string()))?;
415 if result {
416 log::info!(
417 "Deleted tag index successfully: {} from space {}",
418 name,
419 space_id
420 );
421 return Ok(());
422 }
423 }
424
425 if let Ok(Some(_)) = storage.get_edge_index(&space_name, name) {
427 let result = storage
428 .drop_edge_index(&space_name, name)
429 .map_err(|e| CoreError::StorageError(e.to_string()))?;
430 if result {
431 log::info!(
432 "Deleted edge index successfully: {} from space {}",
433 name,
434 space_id
435 );
436 return Ok(());
437 }
438 }
439
440 Err(CoreError::NotFound(format!(
441 "Index '{}' does not exist",
442 name
443 )))
444 }
445
446 pub fn rebuild_index(&self, space_id: u64, index_name: &str) -> CoreResult<()> {
452 let space_name = self.get_space_name_by_id(space_id)?;
453
454 let mut storage = self.storage.write();
455
456 if let Ok(Some(_)) = storage.get_tag_index(&space_name, index_name) {
458 let result = storage
459 .rebuild_tag_index(&space_name, index_name)
460 .map_err(|e| CoreError::StorageError(e.to_string()))?;
461 if result {
462 log::info!(
463 "Rebuilt tag index successfully: {} in space {}",
464 index_name,
465 space_id
466 );
467 return Ok(());
468 }
469 }
470
471 if let Ok(Some(_)) = storage.get_edge_index(&space_name, index_name) {
473 let result = storage
474 .rebuild_edge_index(&space_name, index_name)
475 .map_err(|e| CoreError::StorageError(e.to_string()))?;
476 if result {
477 log::info!(
478 "Rebuilt edge index successfully: {} in space {}",
479 index_name,
480 space_id
481 );
482 return Ok(());
483 }
484 }
485
486 Err(CoreError::NotFound(format!(
487 "Index '{}' does not exist",
488 index_name
489 )))
490 }
491}
492
493impl<S: StorageClient> SchemaApi<S> {
495 fn get_space_name_by_id(&self, space_id: u64) -> CoreResult<String> {
497 let storage = self.storage.read();
498 let space_info = storage
499 .get_space_by_id(space_id)
500 .map_err(|e| CoreError::StorageError(e.to_string()))?
501 .ok_or_else(|| CoreError::NotFound(format!("Space ID {} does not exist", space_id)))?;
502 Ok(space_info.space_name)
503 }
504
505 fn build_index_fields(
507 &self,
508 field_names: &[String],
509 properties: &[linkrs_core::types::PropertyDef],
510 ) -> CoreResult<Vec<IndexField>> {
511 let mut fields = Vec::new();
512
513 for field_name in field_names {
514 let prop = properties
515 .iter()
516 .find(|p| &p.name == field_name)
517 .ok_or_else(|| {
518 CoreError::InvalidParameter(format!("Field '{}' does not exist", field_name))
519 })?;
520
521 let value_type = Self::datatype_to_value(&prop.data_type);
523
524 fields.push(IndexField::new(
525 field_name.clone(),
526 value_type,
527 prop.nullable,
528 ));
529 }
530
531 Ok(fields)
532 }
533
534 fn datatype_to_value(data_type: &linkrs_core::DataType) -> linkrs_core::Value {
536 use linkrs_core::value::date_time::{DateTimeValue, DateValue, TimeValue};
537 use linkrs_core::value::NullType;
538 use linkrs_core::DataType;
539 use linkrs_core::Value;
540
541 match data_type {
542 DataType::SmallInt => Value::SmallInt(0),
543 DataType::Int => Value::Int(0),
544 DataType::BigInt => Value::BigInt(0),
545 DataType::Float => Value::Float(0.0),
546 DataType::Double => Value::Double(0.0),
547 DataType::String | DataType::FixedString(_) => Value::string(""),
548 DataType::Bool => Value::Bool(false),
549 DataType::Date => Value::Date(DateValue {
550 year: 1970,
551 month: 1,
552 day: 1,
553 }),
554 DataType::DateTime => Value::DateTime(DateTimeValue {
555 year: 1970,
556 month: 1,
557 day: 1,
558 hour: 0,
559 minute: 0,
560 sec: 0,
561 microsec: 0,
562 }),
563 DataType::Time => Value::Time(TimeValue {
564 hour: 0,
565 minute: 0,
566 sec: 0,
567 microsec: 0,
568 }),
569 _ => Value::Null(NullType::Null),
570 }
571 }
572}
573
574impl<S: StorageClient> Clone for SchemaApi<S> {
575 fn clone(&self) -> Self {
576 Self {
577 storage: Arc::clone(&self.storage),
578 }
579 }
580}
581
582impl From<PropertyDef> for linkrs_core::types::PropertyDef {
584 fn from(prop: PropertyDef) -> Self {
585 Self {
586 name: prop.name,
587 data_type: prop.data_type,
588 nullable: prop.nullable,
589 default: prop.default_value,
590 comment: prop.comment,
591 serial: false,
592 }
593 }
594}
595
596#[cfg(test)]
597mod tests {
598 use super::*;
599 use linkrs_storage::MockStorage;
600
601 fn create_mock_storage() -> Arc<RwLock<MockStorage>> {
602 Arc::new(RwLock::new(
603 MockStorage::new().expect("Failed to create MockStorage"),
604 ))
605 }
606
607 #[test]
608 fn test_schema_api_new() {
609 let storage = create_mock_storage();
610 let _schema_api = SchemaApi::new(storage);
611 }
613
614 #[test]
615 fn test_schema_api_clone() {
616 let storage = create_mock_storage();
617 let schema_api = SchemaApi::new(storage);
618 let _cloned = schema_api.clone();
619 }
621
622 #[test]
623 fn test_property_def_conversion() {
624 let api_prop = PropertyDef {
625 name: "test".to_string(),
626 data_type: linkrs_core::DataType::String,
627 nullable: true,
628 default_value: None,
629 comment: Some("test comment".to_string()),
630 };
631
632 let core_prop: linkrs_core::types::PropertyDef = api_prop.into();
633 assert_eq!(core_prop.name, "test");
634 assert_eq!(core_prop.data_type, linkrs_core::DataType::String);
635 assert!(core_prop.nullable);
636 assert_eq!(core_prop.comment, Some("test comment".to_string()));
637 }
638}