Skip to main content

selene_graph/shared/
index_ddl.rs

1//! Shared graph index-DDL wrappers.
2
3use selene_core::{Change, DbString, HnswIndexConfig, SchemaChange};
4
5use super::SharedGraph;
6use crate::error::{GraphError, GraphResult};
7use crate::graph::PropertyIndexEntry;
8use crate::schema_index_kind::schema_kind_from;
9use crate::typed_index::TypedIndexKind;
10use crate::vector_index::{VectorIndexConfig, VectorIndexKind};
11
12impl SharedGraph {
13    /// Register a built-in node property index for `(label, property)`.
14    ///
15    /// The current node columns are scanned under the write lock and the
16    /// published snapshot is updated in one transaction.
17    ///
18    /// # Errors
19    ///
20    /// Returns [`GraphError::PropertyIndexAlreadyExists`] if the pair is
21    /// already registered, or [`GraphError::IndexValueRejected`] if any
22    /// existing node with `label` has a non-null value that does not match
23    /// `kind`.
24    pub fn create_property_index(
25        &self,
26        label: DbString,
27        property: DbString,
28        kind: TypedIndexKind,
29    ) -> GraphResult<()> {
30        self.create_property_index_named(label, property, kind, None)
31    }
32
33    /// Register a built-in node property index with optional catalog name.
34    pub fn create_property_index_named(
35        &self,
36        label: DbString,
37        property: DbString,
38        kind: TypedIndexKind,
39        name: Option<DbString>,
40    ) -> GraphResult<()> {
41        let mut txn = self.begin_write();
42        if txn
43            .read()
44            .property_index
45            .contains_key(&(label.clone(), property.clone()))
46        {
47            return Err(GraphError::PropertyIndexAlreadyExists { label, property });
48        }
49        let index = crate::property_index::build_property_index(
50            txn.read(),
51            label.clone(),
52            property.clone(),
53            kind,
54        )?;
55        txn.guard_mut().property_index.insert(
56            (label.clone(), property.clone()),
57            PropertyIndexEntry::new(index, name.clone()),
58        );
59        let graph = txn.read().graph_id();
60        txn.changes.push(Change::SchemaChanged {
61            graph,
62            change: SchemaChange::PropertyIndexCreatedNamed {
63                label,
64                property,
65                kind: schema_kind_from(kind),
66                name,
67            },
68        });
69        txn.commit()?;
70        Ok(())
71    }
72
73    /// Drop a built-in node property index.
74    ///
75    /// The operation is idempotent; dropping an absent index succeeds without
76    /// publishing a new snapshot.
77    pub fn drop_property_index(&self, label: DbString, property: DbString) -> GraphResult<()> {
78        let mut txn = self.begin_write();
79        if !txn
80            .read()
81            .property_index
82            .contains_key(&(label.clone(), property.clone()))
83        {
84            return Ok(());
85        }
86        txn.guard_mut()
87            .property_index
88            .remove(&(label.clone(), property.clone()));
89        let graph = txn.read().graph_id();
90        txn.changes.push(Change::SchemaChanged {
91            graph,
92            change: SchemaChange::PropertyIndexDropped { label, property },
93        });
94        txn.commit()?;
95        Ok(())
96    }
97
98    /// Register a built-in edge property index for `(label, property)`.
99    ///
100    /// The current edge columns are scanned under the write lock and the
101    /// published snapshot is updated in one transaction.
102    ///
103    /// # Errors
104    ///
105    /// Returns [`GraphError::PropertyIndexAlreadyExists`] if the pair is
106    /// already registered, or [`GraphError::IndexValueRejected`] if any
107    /// existing edge with `label` has a non-null value that does not match
108    /// `kind`.
109    pub fn create_edge_property_index(
110        &self,
111        label: DbString,
112        property: DbString,
113        kind: TypedIndexKind,
114    ) -> GraphResult<()> {
115        self.create_edge_property_index_named(label, property, kind, None)
116    }
117
118    /// Register a built-in edge property index with optional catalog name.
119    pub fn create_edge_property_index_named(
120        &self,
121        label: DbString,
122        property: DbString,
123        kind: TypedIndexKind,
124        name: Option<DbString>,
125    ) -> GraphResult<()> {
126        let mut txn = self.begin_write();
127        txn.mutator()
128            .create_edge_property_index_named(label, property, kind, name)?;
129        txn.commit()?;
130        Ok(())
131    }
132
133    /// Drop a built-in edge property index.
134    ///
135    /// The operation is idempotent; dropping an absent index succeeds without
136    /// publishing a new snapshot.
137    pub fn drop_edge_property_index(&self, label: DbString, property: DbString) -> GraphResult<()> {
138        let mut txn = self.begin_write();
139        if !txn
140            .read()
141            .edge_property_index
142            .contains_key(&(label.clone(), property.clone()))
143        {
144            return Ok(());
145        }
146        txn.mutator().drop_edge_property_index(label, property)?;
147        txn.commit()?;
148        Ok(())
149    }
150
151    /// Register a built-in node vector index for `(label, property)`.
152    ///
153    /// The current node columns are scanned under the write lock and the
154    /// published snapshot is updated in one transaction.
155    ///
156    /// # Errors
157    ///
158    /// Returns [`GraphError::VectorIndexAlreadyExists`] if the pair is already
159    /// registered, [`GraphError::VectorIndexInvalidDimension`] when `dimension`
160    /// is zero, or [`GraphError::VectorIndexValueRejected`] if any existing
161    /// node with `label` has a non-null value for `property` that is not a
162    /// vector with the declared dimension.
163    pub fn create_vector_index(
164        &self,
165        label: DbString,
166        property: DbString,
167        kind: VectorIndexKind,
168        dimension: u32,
169    ) -> GraphResult<()> {
170        self.create_vector_index_named(label, property, kind, dimension, None)
171    }
172
173    /// Register a built-in node vector index with optional catalog name.
174    pub fn create_vector_index_named(
175        &self,
176        label: DbString,
177        property: DbString,
178        kind: VectorIndexKind,
179        dimension: u32,
180        name: Option<DbString>,
181    ) -> GraphResult<()> {
182        self.create_vector_index_named_with_config(label, property, kind, dimension, name, None)
183    }
184
185    /// Register a built-in node vector index with optional HNSW construction config.
186    pub fn create_vector_index_named_with_config(
187        &self,
188        label: DbString,
189        property: DbString,
190        kind: VectorIndexKind,
191        dimension: u32,
192        name: Option<DbString>,
193        hnsw_config: Option<HnswIndexConfig>,
194    ) -> GraphResult<()> {
195        self.create_vector_index_named_with_configs(
196            label,
197            property,
198            kind,
199            dimension,
200            name,
201            VectorIndexConfig::new(hnsw_config, None),
202        )
203    }
204
205    /// Register a built-in node vector index with optional ANN construction config.
206    pub fn create_vector_index_named_with_configs(
207        &self,
208        label: DbString,
209        property: DbString,
210        kind: VectorIndexKind,
211        dimension: u32,
212        name: Option<DbString>,
213        config: VectorIndexConfig,
214    ) -> GraphResult<()> {
215        let mut txn = self.begin_write();
216        txn.mutator().create_vector_index_named_with_configs(
217            label, property, kind, dimension, name, config,
218        )?;
219        txn.commit()?;
220        Ok(())
221    }
222
223    /// Drop a built-in node vector index.
224    ///
225    /// The operation is idempotent; dropping an absent index succeeds without
226    /// publishing a new snapshot.
227    pub fn drop_vector_index(&self, label: DbString, property: DbString) -> GraphResult<()> {
228        let mut txn = self.begin_write();
229        txn.mutator().drop_vector_index(label, property)?;
230        txn.commit()?;
231        Ok(())
232    }
233
234    /// Register a built-in node text index for `(label, property)`.
235    ///
236    /// The current node columns are scanned under the write lock and the
237    /// published snapshot is updated in one transaction.
238    ///
239    /// # Errors
240    ///
241    /// Returns [`GraphError::TextIndexAlreadyExists`] if the pair is already
242    /// registered, or [`GraphError::Inconsistent`] if index construction
243    /// observes corrupt graph columns.
244    pub fn create_text_index(&self, label: DbString, property: DbString) -> GraphResult<()> {
245        self.create_text_index_named(label, property, None)
246    }
247
248    /// Register a built-in node text index with optional catalog name.
249    pub fn create_text_index_named(
250        &self,
251        label: DbString,
252        property: DbString,
253        name: Option<DbString>,
254    ) -> GraphResult<()> {
255        let mut txn = self.begin_write();
256        txn.mutator()
257            .create_text_index_named(label, property, name)?;
258        txn.commit()?;
259        Ok(())
260    }
261
262    /// Drop a built-in node text index.
263    ///
264    /// The operation is idempotent; dropping an absent index succeeds without
265    /// publishing a new snapshot.
266    pub fn drop_text_index(&self, label: DbString, property: DbString) -> GraphResult<()> {
267        let mut txn = self.begin_write();
268        txn.mutator().drop_text_index(label, property)?;
269        txn.commit()?;
270        Ok(())
271    }
272}