Skip to main content

oxgraph_db/
read.rs

1//! Owned read-side views: whole-record reads with typed property access.
2//!
3//! [`Reader::element`](crate::Reader::element) and
4//! [`Reader::relation`](crate::Reader::relation) return these owned views in one
5//! call — id, labels, and every property together — replacing per-key `Cow`
6//! reads. Property access is typed through [`Key<T>`](crate::Key): a read of the
7//! wrong Rust type is a compile error.
8//!
9//! # Performance
10//!
11//! Building a view is `O(label count + property count)` for the subject; typed
12//! property access is `O(log property count)`.
13
14use std::collections::BTreeMap;
15
16use crate::{
17    ElementId, LabelId, PropertyKeyId, PropertyValue, RelationId, RelationTypeId,
18    error::DbError,
19    typed::{Key, Readable, ValueType},
20};
21
22/// A typed, owned property bag for one subject.
23///
24/// # Performance
25///
26/// `get`/`require` are `O(log property count)`; `value` is `O(log property
27/// count)`.
28#[derive(Clone, Debug, Default, Eq, PartialEq)]
29pub struct Properties {
30    /// Visible property values keyed by property key id, in key order.
31    values: BTreeMap<PropertyKeyId, PropertyValue>,
32}
33
34impl Properties {
35    /// Builds a property bag from `(key, value)` pairs.
36    ///
37    /// # Performance
38    ///
39    /// This function is `O(n log n)` in the pair count.
40    pub(crate) fn from_pairs(
41        pairs: impl IntoIterator<Item = (PropertyKeyId, PropertyValue)>,
42    ) -> Self {
43        Self {
44            values: pairs.into_iter().collect(),
45        }
46    }
47
48    /// Reads a typed property, or `None` when absent or type-mismatched.
49    ///
50    /// # Performance
51    ///
52    /// This method is `O(log property count)` (plus `O(value length)` for text).
53    #[must_use]
54    pub fn get<T: ValueType, V: Readable<T>>(&self, key: Key<T>) -> Option<V> {
55        self.values.get(&key.id()).and_then(V::read)
56    }
57
58    /// Reads a required typed property.
59    ///
60    /// # Errors
61    ///
62    /// Returns [`DbError::MissingProperty`] when the property is absent.
63    ///
64    /// # Performance
65    ///
66    /// This method is `O(log property count)`.
67    pub fn require<T: ValueType, V: Readable<T>>(&self, key: Key<T>) -> Result<V, DbError> {
68        self.get(key)
69            .ok_or_else(|| DbError::MissingProperty { key: key.id() })
70    }
71
72    /// Returns the raw value for an untyped key.
73    ///
74    /// # Performance
75    ///
76    /// This method is `O(log property count)`.
77    #[must_use]
78    pub fn value(&self, key: PropertyKeyId) -> Option<&PropertyValue> {
79        self.values.get(&key)
80    }
81
82    /// Returns the number of visible properties.
83    ///
84    /// # Performance
85    ///
86    /// This method is `O(1)`.
87    #[must_use]
88    pub fn len(&self) -> usize {
89        self.values.len()
90    }
91
92    /// Returns whether there are no visible properties.
93    ///
94    /// # Performance
95    ///
96    /// This method is `O(1)`.
97    #[must_use]
98    pub fn is_empty(&self) -> bool {
99        self.values.is_empty()
100    }
101
102    /// Iterates `(key, value)` pairs in ascending key order.
103    ///
104    /// # Performance
105    ///
106    /// Iteration is `O(property count)`.
107    pub fn iter(&self) -> impl Iterator<Item = (PropertyKeyId, &PropertyValue)> {
108        self.values.iter().map(|(key, value)| (*key, value))
109    }
110}
111
112/// An owned element view: id, labels, and all properties in one read.
113///
114/// # Performance
115///
116/// Cloning is `O(label count + property count)`.
117#[derive(Clone, Debug, Eq, PartialEq)]
118pub struct Element {
119    /// Canonical element id.
120    pub id: ElementId,
121    /// Labels assigned to this element, ascending.
122    pub labels: Vec<LabelId>,
123    /// The element's property bag.
124    properties: Properties,
125}
126
127impl Element {
128    /// Assembles an owned element view.
129    ///
130    /// # Performance
131    ///
132    /// This function is `O(1)`.
133    pub(crate) const fn new(id: ElementId, labels: Vec<LabelId>, properties: Properties) -> Self {
134        Self {
135            id,
136            labels,
137            properties,
138        }
139    }
140
141    /// Returns whether this element carries `label`.
142    ///
143    /// # Performance
144    ///
145    /// This method is `O(log label count)`.
146    #[must_use]
147    pub fn has(&self, label: LabelId) -> bool {
148        self.labels.binary_search(&label).is_ok()
149    }
150
151    /// Returns this element's property bag.
152    ///
153    /// # Performance
154    ///
155    /// This method is `O(1)`.
156    #[must_use]
157    pub const fn properties(&self) -> &Properties {
158        &self.properties
159    }
160}
161
162/// An owned relation view: id, type, labels, and all properties in one read.
163///
164/// # Performance
165///
166/// Cloning is `O(label count + property count)`.
167#[derive(Clone, Debug, Eq, PartialEq)]
168pub struct Relation {
169    /// Canonical relation id.
170    pub id: RelationId,
171    /// The relation's type, if set.
172    pub relation_type: Option<RelationTypeId>,
173    /// Labels assigned to this relation, ascending.
174    pub labels: Vec<LabelId>,
175    /// The relation's property bag.
176    properties: Properties,
177}
178
179impl Relation {
180    /// Assembles an owned relation view.
181    ///
182    /// # Performance
183    ///
184    /// This function is `O(1)`.
185    pub(crate) const fn new(
186        id: RelationId,
187        relation_type: Option<RelationTypeId>,
188        labels: Vec<LabelId>,
189        properties: Properties,
190    ) -> Self {
191        Self {
192            id,
193            relation_type,
194            labels,
195            properties,
196        }
197    }
198
199    /// Returns whether this relation carries `label`.
200    ///
201    /// # Performance
202    ///
203    /// This method is `O(log label count)`.
204    #[must_use]
205    pub fn has(&self, label: LabelId) -> bool {
206        self.labels.binary_search(&label).is_ok()
207    }
208
209    /// Returns this relation's property bag.
210    ///
211    /// # Performance
212    ///
213    /// This method is `O(1)`.
214    #[must_use]
215    pub const fn properties(&self) -> &Properties {
216        &self.properties
217    }
218}