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).ok_or_else(|| {
69            DbError::Query(crate::error::QueryError::MissingProperty { key: key.id() })
70        })
71    }
72
73    /// Returns the raw value for an untyped key.
74    ///
75    /// # Performance
76    ///
77    /// This method is `O(log property count)`.
78    #[must_use]
79    pub fn value(&self, key: PropertyKeyId) -> Option<&PropertyValue> {
80        self.values.get(&key)
81    }
82
83    /// Returns the number of visible properties.
84    ///
85    /// # Performance
86    ///
87    /// This method is `O(1)`.
88    #[must_use]
89    pub fn len(&self) -> usize {
90        self.values.len()
91    }
92
93    /// Returns whether there are no visible properties.
94    ///
95    /// # Performance
96    ///
97    /// This method is `O(1)`.
98    #[must_use]
99    pub fn is_empty(&self) -> bool {
100        self.values.is_empty()
101    }
102
103    /// Iterates `(key, value)` pairs in ascending key order.
104    ///
105    /// # Performance
106    ///
107    /// Iteration is `O(property count)`.
108    pub fn iter(&self) -> impl Iterator<Item = (PropertyKeyId, &PropertyValue)> {
109        self.values.iter().map(|(key, value)| (*key, value))
110    }
111}
112
113/// An owned element view: id, labels, and all properties in one read.
114///
115/// # Performance
116///
117/// Cloning is `O(label count + property count)`.
118#[derive(Clone, Debug, Eq, PartialEq)]
119pub struct Element {
120    /// Canonical element id.
121    pub id: ElementId,
122    /// Labels assigned to this element, ascending.
123    pub labels: Vec<LabelId>,
124    /// The element's property bag.
125    properties: Properties,
126}
127
128impl Element {
129    /// Assembles an owned element view.
130    ///
131    /// # Performance
132    ///
133    /// This function is `O(1)`.
134    pub(crate) const fn new(id: ElementId, labels: Vec<LabelId>, properties: Properties) -> Self {
135        Self {
136            id,
137            labels,
138            properties,
139        }
140    }
141
142    /// Returns whether this element carries `label`.
143    ///
144    /// # Performance
145    ///
146    /// This method is `O(log label count)`.
147    #[must_use]
148    pub fn has(&self, label: LabelId) -> bool {
149        self.labels.binary_search(&label).is_ok()
150    }
151
152    /// Returns this element's property bag.
153    ///
154    /// # Performance
155    ///
156    /// This method is `O(1)`.
157    #[must_use]
158    pub const fn properties(&self) -> &Properties {
159        &self.properties
160    }
161}
162
163/// An owned relation view: id, type, labels, and all properties in one read.
164///
165/// # Performance
166///
167/// Cloning is `O(label count + property count)`.
168#[derive(Clone, Debug, Eq, PartialEq)]
169pub struct Relation {
170    /// Canonical relation id.
171    pub id: RelationId,
172    /// The relation's type, if set.
173    pub relation_type: Option<RelationTypeId>,
174    /// Labels assigned to this relation, ascending.
175    pub labels: Vec<LabelId>,
176    /// The relation's property bag.
177    properties: Properties,
178}
179
180impl Relation {
181    /// Assembles an owned relation view.
182    ///
183    /// # Performance
184    ///
185    /// This function is `O(1)`.
186    pub(crate) const fn new(
187        id: RelationId,
188        relation_type: Option<RelationTypeId>,
189        labels: Vec<LabelId>,
190        properties: Properties,
191    ) -> Self {
192        Self {
193            id,
194            relation_type,
195            labels,
196            properties,
197        }
198    }
199
200    /// Returns whether this relation carries `label`.
201    ///
202    /// # Performance
203    ///
204    /// This method is `O(log label count)`.
205    #[must_use]
206    pub fn has(&self, label: LabelId) -> bool {
207        self.labels.binary_search(&label).is_ok()
208    }
209
210    /// Returns this relation's property bag.
211    ///
212    /// # Performance
213    ///
214    /// This method is `O(1)`.
215    #[must_use]
216    pub const fn properties(&self) -> &Properties {
217        &self.properties
218    }
219}