Skip to main content

selene_graph/mutator/
property_index.rs

1//! Property-index mutation methods for the transaction mutator.
2
3use selene_core::{Change, DbString, SchemaChange};
4
5use crate::graph::PropertyIndexEntry;
6use crate::schema_index_kind::schema_kind_from;
7use crate::{GraphError, GraphResult, Mutator, TypedIndexKind};
8
9impl<'tx, 'g> Mutator<'tx, 'g> {
10    /// Register a durable node property index in the active write transaction.
11    ///
12    /// # Errors
13    ///
14    /// Returns [`GraphError::PropertyIndexAlreadyExists`] if the pair already
15    /// exists, or [`GraphError::IndexValueRejected`] if any existing non-null
16    /// value for `(label, property)` cannot be admitted to `kind`.
17    pub fn create_property_index(
18        &mut self,
19        label: DbString,
20        property: DbString,
21        kind: TypedIndexKind,
22    ) -> GraphResult<()> {
23        self.create_property_index_named(label, property, kind, None)
24    }
25
26    /// Register a durable node property index with optional catalog name.
27    pub fn create_property_index_named(
28        &mut self,
29        label: DbString,
30        property: DbString,
31        kind: TypedIndexKind,
32        name: Option<DbString>,
33    ) -> GraphResult<()> {
34        if self
35            .txn
36            .read()
37            .property_index
38            .contains_key(&(label.clone(), property.clone()))
39        {
40            return Err(GraphError::PropertyIndexAlreadyExists { label, property });
41        }
42        let index = crate::property_index::build_property_index(
43            self.txn.read(),
44            label.clone(),
45            property.clone(),
46            kind,
47        )?;
48        let graph_id = self.txn.read().graph_id();
49        self.txn.guard_mut().property_index.insert(
50            (label.clone(), property.clone()),
51            PropertyIndexEntry::new(index, name.clone()),
52        );
53        self.txn.changes.push(Change::SchemaChanged {
54            graph: graph_id,
55            change: SchemaChange::PropertyIndexCreatedNamed {
56                label,
57                property,
58                kind: schema_kind_from(kind),
59                name,
60            },
61        });
62        Ok(())
63    }
64
65    /// Register a durable edge property index in the active write transaction.
66    ///
67    /// # Errors
68    ///
69    /// Returns [`GraphError::PropertyIndexAlreadyExists`] if the pair already
70    /// exists, or [`GraphError::IndexValueRejected`] if any existing non-null
71    /// edge value for `(label, property)` cannot be admitted to `kind`.
72    pub fn create_edge_property_index(
73        &mut self,
74        label: DbString,
75        property: DbString,
76        kind: TypedIndexKind,
77    ) -> GraphResult<()> {
78        self.create_edge_property_index_named(label, property, kind, None)
79    }
80
81    /// Register a durable edge property index with optional catalog name.
82    pub fn create_edge_property_index_named(
83        &mut self,
84        label: DbString,
85        property: DbString,
86        kind: TypedIndexKind,
87        name: Option<DbString>,
88    ) -> GraphResult<()> {
89        if self
90            .txn
91            .read()
92            .edge_property_index
93            .contains_key(&(label.clone(), property.clone()))
94        {
95            return Err(GraphError::PropertyIndexAlreadyExists { label, property });
96        }
97        let index = crate::property_index::build_edge_property_index(
98            self.txn.read(),
99            label.clone(),
100            property.clone(),
101            kind,
102        )?;
103        let graph_id = self.txn.read().graph_id();
104        self.txn.guard_mut().edge_property_index.insert(
105            (label.clone(), property.clone()),
106            PropertyIndexEntry::new(index, name.clone()),
107        );
108        self.txn.changes.push(Change::SchemaChanged {
109            graph: graph_id,
110            change: SchemaChange::EdgePropertyIndexCreated {
111                label,
112                property,
113                kind: schema_kind_from(kind),
114                name,
115            },
116        });
117        Ok(())
118    }
119
120    /// Drop a durable edge property index from the active write transaction.
121    ///
122    /// The operation is idempotent. Dropping an absent index succeeds and emits
123    /// no WAL change.
124    pub fn drop_edge_property_index(
125        &mut self,
126        label: DbString,
127        property: DbString,
128    ) -> GraphResult<()> {
129        if !self
130            .txn
131            .read()
132            .edge_property_index
133            .contains_key(&(label.clone(), property.clone()))
134        {
135            return Ok(());
136        }
137        let graph_id = self.txn.read().graph_id();
138        self.txn
139            .guard_mut()
140            .edge_property_index
141            .remove(&(label.clone(), property.clone()));
142        self.txn.changes.push(Change::SchemaChanged {
143            graph: graph_id,
144            change: SchemaChange::EdgePropertyIndexDropped { label, property },
145        });
146        Ok(())
147    }
148
149    /// Drop a durable node property index from the active write transaction.
150    ///
151    /// The operation is idempotent. Dropping an absent index succeeds and emits
152    /// no WAL change.
153    pub fn drop_property_index(&mut self, label: DbString, property: DbString) -> GraphResult<()> {
154        if !self
155            .txn
156            .read()
157            .property_index
158            .contains_key(&(label.clone(), property.clone()))
159        {
160            return Ok(());
161        }
162        let graph_id = self.txn.read().graph_id();
163        self.txn
164            .guard_mut()
165            .property_index
166            .remove(&(label.clone(), property.clone()));
167        self.txn.changes.push(Change::SchemaChanged {
168            graph: graph_id,
169            change: SchemaChange::PropertyIndexDropped { label, property },
170        });
171        Ok(())
172    }
173}
174
175#[cfg(test)]
176#[path = "property_index/tests.rs"]
177mod tests;