Skip to main content

oxgraph_db/
catalog.rs

1//! Catalog metadata for names, projections, property keys, and indexes.
2
3use std::collections::{BTreeMap, BTreeSet};
4
5use serde::{Deserialize, Serialize};
6
7use crate::{
8    DbError, IndexId, LabelId, ProjectionId, PropertyKeyId, RelationTypeId, RoleId,
9    value::PropertyType,
10};
11
12/// Catalog entry for one structural incidence role.
13///
14/// # Performance
15///
16/// Cloning is `O(name length)`.
17#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
18pub struct RoleDefinition {
19    /// Stable role identifier.
20    pub id: RoleId,
21    /// Human-readable unique role name.
22    pub name: String,
23}
24
25/// Catalog entry for one element or relation label.
26///
27/// # Performance
28///
29/// Cloning is `O(name length)`.
30#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
31pub struct LabelDefinition {
32    /// Stable label identifier.
33    pub id: LabelId,
34    /// Human-readable unique label name.
35    pub name: String,
36}
37
38/// Catalog entry for one relation type.
39///
40/// # Performance
41///
42/// Cloning is `O(name length)`.
43#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
44pub struct RelationTypeDefinition {
45    /// Stable relation type identifier.
46    pub id: RelationTypeId,
47    /// Human-readable unique relation type name.
48    pub name: String,
49}
50
51/// Subject family accepted by a property key.
52///
53/// # Performance
54///
55/// Copying and comparing are `O(1)`.
56#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
57pub enum PropertyFamily {
58    /// Property applies to canonical elements.
59    Element,
60    /// Property applies to canonical relations.
61    Relation,
62    /// Property applies to canonical incidences.
63    Incidence,
64}
65
66/// Catalog entry for one typed property key.
67///
68/// # Performance
69///
70/// Cloning is `O(name length)`.
71#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
72pub struct PropertyKeyDefinition {
73    /// Stable property key identifier.
74    pub id: PropertyKeyId,
75    /// Human-readable unique key name.
76    pub name: String,
77    /// Subject family this key can be attached to.
78    pub family: PropertyFamily,
79    /// Required scalar value type.
80    pub value_type: PropertyType,
81}
82
83/// Graph projection definition.
84///
85/// Graph projections materialize binary relations as CSR outgoing and CSC
86/// incoming arrays over canonical topology IDs.
87///
88/// # Performance
89///
90/// Cloning is `O(r)` for `r` selected relation types plus the name length.
91#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
92pub struct GraphProjectionDefinition {
93    /// Unique projection name.
94    pub name: String,
95    /// Relation types visible as binary graph edges.
96    pub relation_types: BTreeSet<RelationTypeId>,
97    /// Role identifying the source endpoint.
98    pub source_role: RoleId,
99    /// Role identifying the target endpoint.
100    pub target_role: RoleId,
101}
102
103/// Hypergraph projection definition.
104///
105/// Hypergraph projections materialize many-participant directed relations as
106/// BCSR-style relation-major and vertex-major arrays.
107///
108/// # Performance
109///
110/// Cloning is `O(r + s + t)` for relation type and role set sizes plus the
111/// name length.
112#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
113pub struct HypergraphProjectionDefinition {
114    /// Unique projection name.
115    pub name: String,
116    /// Relation types visible as hyperedges.
117    pub relation_types: BTreeSet<RelationTypeId>,
118    /// Roles treated as source-side participants.
119    pub source_roles: BTreeSet<RoleId>,
120    /// Roles treated as target-side participants.
121    pub target_roles: BTreeSet<RoleId>,
122}
123
124/// Physical projection definition stored in the catalog.
125///
126/// # Performance
127///
128/// Cloning is `O(definition size)`.
129#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
130pub enum ProjectionDefinition {
131    /// Binary graph projection.
132    Graph(GraphProjectionDefinition),
133    /// Directed hypergraph projection.
134    Hypergraph(HypergraphProjectionDefinition),
135}
136
137impl ProjectionDefinition {
138    /// Returns the unique projection name.
139    ///
140    /// # Performance
141    ///
142    /// This method is `O(1)`.
143    #[must_use]
144    pub fn name(&self) -> &str {
145        match self {
146            Self::Graph(definition) => &definition.name,
147            Self::Hypergraph(definition) => &definition.name,
148        }
149    }
150}
151
152/// Index definition stored in the catalog.
153///
154/// # Performance
155///
156/// Cloning is `O(key count)` for composite indexes and `O(1)` otherwise.
157#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
158pub enum IndexDefinition {
159    /// Element label membership index.
160    Label {
161        /// Indexed label.
162        label: LabelId,
163    },
164    /// Relation type membership index.
165    RelationType {
166        /// Indexed relation type.
167        relation_type: RelationTypeId,
168    },
169    /// Equality index over one property key.
170    PropertyEquality {
171        /// Indexed property key.
172        key: PropertyKeyId,
173    },
174    /// Range index over one ordered property key.
175    PropertyRange {
176        /// Indexed property key.
177        key: PropertyKeyId,
178    },
179    /// Composite equality index over ordered property keys.
180    CompositeEquality {
181        /// Indexed property keys in tuple order.
182        keys: Vec<PropertyKeyId>,
183    },
184    /// Projection-materialization index metadata.
185    Projection {
186        /// Indexed projection.
187        projection: ProjectionId,
188    },
189}
190
191/// Catalog entry for one index.
192///
193/// # Performance
194///
195/// Cloning is `O(name length + definition size)`.
196#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
197pub struct IndexEntry {
198    /// Stable index identifier.
199    pub id: IndexId,
200    /// Human-readable unique index name.
201    pub name: String,
202    /// Logical index definition.
203    pub definition: IndexDefinition,
204}
205
206/// Catalog entry for one projection.
207///
208/// # Performance
209///
210/// Cloning is `O(name length + definition size)`.
211#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
212pub struct ProjectionEntry {
213    /// Stable projection identifier.
214    pub id: ProjectionId,
215    /// Physical projection definition.
216    pub definition: ProjectionDefinition,
217}
218
219/// Db catalog for names, schemas, projections, and indexes.
220///
221/// # Performance
222///
223/// Cloning is `O(catalog entries + string bytes)`.
224#[derive(Clone, Debug, Eq, PartialEq)]
225pub struct Catalog {
226    /// Roles by stable ID.
227    roles: BTreeMap<RoleId, RoleDefinition>,
228    /// Role IDs by name.
229    role_names: BTreeMap<String, RoleId>,
230    /// Labels by stable ID.
231    labels: BTreeMap<LabelId, LabelDefinition>,
232    /// Label IDs by name.
233    label_names: BTreeMap<String, LabelId>,
234    /// Relation types by stable ID.
235    relation_types: BTreeMap<RelationTypeId, RelationTypeDefinition>,
236    /// Relation type IDs by name.
237    relation_type_names: BTreeMap<String, RelationTypeId>,
238    /// Property keys by stable ID.
239    property_keys: BTreeMap<PropertyKeyId, PropertyKeyDefinition>,
240    /// Property key IDs by name.
241    property_key_names: BTreeMap<String, PropertyKeyId>,
242    /// Projections by stable ID.
243    projections: BTreeMap<ProjectionId, ProjectionEntry>,
244    /// Projection IDs by name.
245    projection_names: BTreeMap<String, ProjectionId>,
246    /// Indexes by stable ID.
247    indexes: BTreeMap<IndexId, IndexEntry>,
248    /// Index IDs by name.
249    index_names: BTreeMap<String, IndexId>,
250}
251
252impl Catalog {
253    /// Creates an empty catalog.
254    ///
255    /// # Performance
256    ///
257    /// This function is `O(1)`.
258    #[must_use]
259    pub(crate) const fn empty() -> Self {
260        Self {
261            roles: BTreeMap::new(),
262            role_names: BTreeMap::new(),
263            labels: BTreeMap::new(),
264            label_names: BTreeMap::new(),
265            relation_types: BTreeMap::new(),
266            relation_type_names: BTreeMap::new(),
267            property_keys: BTreeMap::new(),
268            property_key_names: BTreeMap::new(),
269            projections: BTreeMap::new(),
270            projection_names: BTreeMap::new(),
271            indexes: BTreeMap::new(),
272            index_names: BTreeMap::new(),
273        }
274    }
275
276    /// Returns a role definition.
277    ///
278    /// # Performance
279    ///
280    /// This method is `O(log r)`.
281    #[must_use]
282    pub fn role(&self, id: RoleId) -> Option<&RoleDefinition> {
283        self.roles.get(&id)
284    }
285
286    /// Returns a label definition.
287    ///
288    /// # Performance
289    ///
290    /// This method is `O(log l)`.
291    #[must_use]
292    pub fn label(&self, id: LabelId) -> Option<&LabelDefinition> {
293        self.labels.get(&id)
294    }
295
296    /// Returns a relation type definition.
297    ///
298    /// # Performance
299    ///
300    /// This method is `O(log t)`.
301    #[must_use]
302    pub fn relation_type(&self, id: RelationTypeId) -> Option<&RelationTypeDefinition> {
303        self.relation_types.get(&id)
304    }
305
306    /// Returns a property key definition.
307    ///
308    /// # Performance
309    ///
310    /// This method is `O(log p)`.
311    #[must_use]
312    pub fn property_key(&self, id: PropertyKeyId) -> Option<&PropertyKeyDefinition> {
313        self.property_keys.get(&id)
314    }
315
316    /// Returns a projection entry.
317    ///
318    /// # Performance
319    ///
320    /// This method is `O(log p)`.
321    #[must_use]
322    pub fn projection(&self, id: ProjectionId) -> Option<&ProjectionEntry> {
323        self.projections.get(&id)
324    }
325
326    /// Returns an index entry.
327    ///
328    /// # Performance
329    ///
330    /// This method is `O(log i)`.
331    #[must_use]
332    pub fn index(&self, id: IndexId) -> Option<&IndexEntry> {
333        self.indexes.get(&id)
334    }
335
336    /// Resolves a role name.
337    ///
338    /// # Performance
339    ///
340    /// This method is `O(log r + name length)`.
341    #[must_use]
342    pub fn role_id(&self, name: &str) -> Option<RoleId> {
343        self.role_names.get(name).copied()
344    }
345
346    /// Resolves a label name.
347    ///
348    /// # Performance
349    ///
350    /// This method is `O(log l + name length)`.
351    #[must_use]
352    pub fn label_id(&self, name: &str) -> Option<LabelId> {
353        self.label_names.get(name).copied()
354    }
355
356    /// Resolves a relation type name.
357    ///
358    /// # Performance
359    ///
360    /// This method is `O(log t + name length)`.
361    #[must_use]
362    pub fn relation_type_id(&self, name: &str) -> Option<RelationTypeId> {
363        self.relation_type_names.get(name).copied()
364    }
365
366    /// Resolves a property key name.
367    ///
368    /// # Performance
369    ///
370    /// This method is `O(log p + name length)`.
371    #[must_use]
372    pub fn property_key_id(&self, name: &str) -> Option<PropertyKeyId> {
373        self.property_key_names.get(name).copied()
374    }
375
376    /// Resolves a projection name.
377    ///
378    /// # Performance
379    ///
380    /// This method is `O(log p + name length)`.
381    #[must_use]
382    pub fn projection_id(&self, name: &str) -> Option<ProjectionId> {
383        self.projection_names.get(name).copied()
384    }
385
386    /// Resolves an index name.
387    ///
388    /// # Performance
389    ///
390    /// This method is `O(log i + name length)`.
391    #[must_use]
392    pub fn index_id(&self, name: &str) -> Option<IndexId> {
393        self.index_names.get(name).copied()
394    }
395
396    /// Iterates role definitions in ID order.
397    ///
398    /// # Performance
399    ///
400    /// Creating the iterator is `O(1)`.
401    pub fn roles(&self) -> impl Iterator<Item = &RoleDefinition> {
402        self.roles.values()
403    }
404
405    /// Iterates label definitions in ID order.
406    ///
407    /// # Performance
408    ///
409    /// Creating the iterator is `O(1)`.
410    pub fn labels(&self) -> impl Iterator<Item = &LabelDefinition> {
411        self.labels.values()
412    }
413
414    /// Iterates relation type definitions in ID order.
415    ///
416    /// # Performance
417    ///
418    /// Creating the iterator is `O(1)`.
419    pub fn relation_types(&self) -> impl Iterator<Item = &RelationTypeDefinition> {
420        self.relation_types.values()
421    }
422
423    /// Iterates property key definitions in ID order.
424    ///
425    /// # Performance
426    ///
427    /// Creating the iterator is `O(1)`.
428    pub fn property_keys(&self) -> impl Iterator<Item = &PropertyKeyDefinition> {
429        self.property_keys.values()
430    }
431
432    /// Iterates projection entries in ID order.
433    ///
434    /// # Performance
435    ///
436    /// Creating the iterator is `O(1)`.
437    pub fn projections(&self) -> impl Iterator<Item = &ProjectionEntry> {
438        self.projections.values()
439    }
440
441    /// Iterates index entries in ID order.
442    ///
443    /// # Performance
444    ///
445    /// Creating the iterator is `O(1)`.
446    pub fn indexes(&self) -> impl Iterator<Item = &IndexEntry> {
447        self.indexes.values()
448    }
449
450    /// Registers a structural role.
451    pub(crate) fn insert_role(&mut self, id: RoleId, name: String) -> Result<(), DbError> {
452        insert_named(&mut self.role_names, &name, id)?;
453        self.roles.insert(id, RoleDefinition { id, name });
454        Ok(())
455    }
456
457    /// Registers a label.
458    pub(crate) fn insert_label(&mut self, id: LabelId, name: String) -> Result<(), DbError> {
459        insert_named(&mut self.label_names, &name, id)?;
460        self.labels.insert(id, LabelDefinition { id, name });
461        Ok(())
462    }
463
464    /// Registers a relation type.
465    pub(crate) fn insert_relation_type(
466        &mut self,
467        id: RelationTypeId,
468        name: String,
469    ) -> Result<(), DbError> {
470        insert_named(&mut self.relation_type_names, &name, id)?;
471        self.relation_types
472            .insert(id, RelationTypeDefinition { id, name });
473        Ok(())
474    }
475
476    /// Registers a typed property key.
477    pub(crate) fn insert_property_key(
478        &mut self,
479        definition: PropertyKeyDefinition,
480    ) -> Result<(), DbError> {
481        insert_named(
482            &mut self.property_key_names,
483            &definition.name,
484            definition.id,
485        )?;
486        self.property_keys.insert(definition.id, definition);
487        Ok(())
488    }
489
490    /// Registers a projection definition.
491    pub(crate) fn insert_projection(
492        &mut self,
493        id: ProjectionId,
494        definition: ProjectionDefinition,
495    ) -> Result<(), DbError> {
496        insert_named(&mut self.projection_names, definition.name(), id)?;
497        self.projections
498            .insert(id, ProjectionEntry { id, definition });
499        Ok(())
500    }
501
502    /// Registers an index definition.
503    pub(crate) fn insert_index(
504        &mut self,
505        id: IndexId,
506        name: String,
507        definition: IndexDefinition,
508    ) -> Result<(), DbError> {
509        insert_named(&mut self.index_names, &name, id)?;
510        self.indexes.insert(
511            id,
512            IndexEntry {
513                id,
514                name,
515                definition,
516            },
517        );
518        Ok(())
519    }
520}
521
522/// Inserts one unique name into a catalog name map.
523fn insert_named<Id: Copy>(
524    names: &mut BTreeMap<String, Id>,
525    name: &str,
526    id: Id,
527) -> Result<(), DbError> {
528    if names.contains_key(name) {
529        return Err(DbError::DuplicateCatalogName);
530    }
531    names.insert(name.to_owned(), id);
532    Ok(())
533}