uqa_storage/catalog/graph_observations/
topology.rs1use std::ops::Bound;
10
11use sha2::{Digest, Sha256};
12
13use super::GraphIdentifierNamespace;
14use crate::mvcc::{SerializableKeySpace, SerializablePredicate};
15use crate::{GraphEntityFilter, GraphEntityKind, KeyValueBatch, StorageBackendResult};
16
17#[derive(Clone, Copy, Debug, PartialEq, Eq)]
19pub enum GraphEntityTopology<'a> {
20 Vertex {
21 label: &'a str,
22 },
23 Edge {
24 label: &'a str,
25 source: u64,
26 target: u64,
27 },
28}
29
30impl GraphEntityTopology<'_> {
31 pub fn kind(self) -> GraphEntityKind {
32 match self {
33 Self::Vertex { .. } => GraphEntityKind::Vertex,
34 Self::Edge { .. } => GraphEntityKind::Edge,
35 }
36 }
37
38 pub fn observe_write(
40 self,
41 namespace: GraphIdentifierNamespace,
42 id: u64,
43 graph: Option<&str>,
44 batch: &mut dyn KeyValueBatch,
45 ) -> StorageBackendResult<()> {
46 let (label, endpoints) = match self {
47 Self::Vertex { label } => (label, None),
48 Self::Edge {
49 label,
50 source,
51 target,
52 } => (label, Some((source, target))),
53 };
54 for mask in 0..if endpoints.is_some() { 8 } else { 2 } {
55 let mut filter = GraphEntityFilter::new(self.kind(), graph);
56 filter.label = (mask & 1 != 0).then_some(label);
57 if let Some((source, target)) = endpoints {
58 filter.source = (mask & 2 != 0).then_some(source);
59 filter.target = (mask & 4 != 0).then_some(target);
60 }
61 let key = GraphSelectionKey::new(namespace, filter, None)?;
62 let mut point = key.lower;
63 point[33..].copy_from_slice(&id.to_be_bytes());
64 batch.observe_serializable_write(SerializablePredicate::point(
65 key.object,
66 SerializableKeySpace::Graph,
67 &point,
68 ))?;
69 }
70 Ok(())
71 }
72}
73
74pub struct GraphSelectionKey {
76 object: [u8; 16],
77 lower: [u8; 41],
78 upper: [u8; 41],
79 after: bool,
80}
81
82impl GraphSelectionKey {
83 pub fn new(
84 namespace: GraphIdentifierNamespace,
85 filter: GraphEntityFilter<'_>,
86 after: Option<u64>,
87 ) -> StorageBackendResult<Self> {
88 filter.validate()?;
89 let mut digest = Sha256::new();
90 digest.update([kind_tag(filter.kind)]);
91 text(&mut digest, filter.graph);
92 text(&mut digest, filter.label);
93 for id in [filter.source, filter.target] {
94 digest.update([u8::from(id.is_some())]);
95 digest.update(id.unwrap_or_default().to_be_bytes());
96 }
97 let mut lower = [0; 41];
98 lower[0] = b's';
99 lower[1..33].copy_from_slice(&digest.finalize());
100 let mut upper = lower;
101 lower[33..].copy_from_slice(&after.unwrap_or_default().to_be_bytes());
102 upper[33..].copy_from_slice(&u64::MAX.to_be_bytes());
103 Ok(Self {
104 object: namespace.serializable_entity_object(),
105 lower,
106 upper,
107 after: after.is_some(),
108 })
109 }
110
111 pub fn predicate(&self) -> SerializablePredicate<'_> {
112 SerializablePredicate::range(
113 self.object,
114 SerializableKeySpace::Graph,
115 if self.after {
116 Bound::Excluded(&self.lower)
117 } else {
118 Bound::Included(&self.lower)
119 },
120 Bound::Included(&self.upper),
121 )
122 }
123}
124
125pub struct GraphMembershipKey {
127 object: [u8; 16],
128 lower: [u8; 42],
129 upper: [u8; 42],
130 point: bool,
131}
132
133impl GraphMembershipKey {
134 pub fn new(
135 namespace: GraphIdentifierNamespace,
136 kind: GraphEntityKind,
137 id: u64,
138 graph: Option<&str>,
139 ) -> Self {
140 let mut lower = [0; 42];
141 lower[0] = b'm';
142 lower[1] = kind_tag(kind);
143 lower[2..10].copy_from_slice(&id.to_be_bytes());
144 let mut upper = lower;
145 if let Some(graph) = graph {
146 lower[10..].copy_from_slice(&Sha256::digest(graph.as_bytes()));
147 } else {
148 upper[10..].fill(u8::MAX);
149 }
150 Self {
151 object: namespace.serializable_entity_object(),
152 lower,
153 upper,
154 point: graph.is_some(),
155 }
156 }
157
158 pub fn predicate(&self) -> SerializablePredicate<'_> {
159 if self.point {
160 SerializablePredicate::point(self.object, SerializableKeySpace::Graph, &self.lower)
161 } else {
162 SerializablePredicate::range(
163 self.object,
164 SerializableKeySpace::Graph,
165 Bound::Included(&self.lower),
166 Bound::Included(&self.upper),
167 )
168 }
169 }
170}
171
172fn kind_tag(kind: GraphEntityKind) -> u8 {
173 match kind {
174 GraphEntityKind::Vertex => b'v',
175 GraphEntityKind::Edge => b'e',
176 }
177}
178
179fn text(digest: &mut Sha256, value: Option<&str>) {
180 digest.update([u8::from(value.is_some())]);
181 let value = value.unwrap_or_default().as_bytes();
182 digest.update(
183 u64::try_from(value.len())
184 .expect("graph selector length")
185 .to_be_bytes(),
186 );
187 digest.update(value);
188}