1use std::any::Any;
9use std::borrow::Borrow;
10use std::collections::{BTreeMap, BTreeSet, HashMap};
11use std::hash::Hash;
12use std::ops::RangeBounds;
13
14use super::{IndexKey, View};
15use crate::event::{Body, Event};
16use crate::projection::Projection;
17
18pub enum Change<K, V> {
21 Put(K, V),
23 Delete(K),
25}
26
27impl<K, V> Change<K, V> {
28 pub fn put(key: K, value: V) -> Self {
30 Change::Put(key, value)
31 }
32 pub fn delete(key: K) -> Self {
34 Change::Delete(key)
35 }
36}
37
38type Projector<K, V, B> = Box<dyn Fn(&Event<B>) -> Option<Change<K, V>>>;
41type Indexer<V> = Box<dyn Fn(&V) -> Vec<IndexKey>>;
42
43pub struct IndexedView<K, V, B> {
48 project: Projector<K, V, B>,
49 indexers: Vec<(String, Indexer<V>)>,
50 primary: BTreeMap<K, V>,
51 indexes: HashMap<String, BTreeMap<IndexKey, BTreeSet<K>>>,
52 indexed_as: HashMap<K, Vec<(String, IndexKey)>>,
55 cursor: u64,
56}
57
58pub struct IndexedViewBuilder<K, V, B> {
63 project: Option<Projector<K, V, B>>,
64 indexers: Vec<(String, Indexer<V>)>,
65}
66
67impl<K, V, B> IndexedView<K, V, B> {
68 pub fn builder() -> IndexedViewBuilder<K, V, B> {
71 IndexedViewBuilder {
72 project: None,
73 indexers: Vec::new(),
74 }
75 }
76}
77
78impl<K, V, B> IndexedViewBuilder<K, V, B> {
79 pub fn project(mut self, f: impl Fn(&Event<B>) -> Option<Change<K, V>> + 'static) -> Self {
82 self.project = Some(Box::new(f));
83 self
84 }
85
86 pub fn index(mut self, name: &str, f: impl Fn(&V) -> Vec<IndexKey> + 'static) -> Self {
90 self.indexers.push((name.to_string(), Box::new(f)));
91 self
92 }
93
94 pub fn build(self) -> IndexedView<K, V, B> {
96 IndexedView {
97 project: self
98 .project
99 .expect("IndexedView::build requires a .project() closure"),
100 indexers: self.indexers,
101 primary: BTreeMap::new(),
102 indexes: HashMap::new(),
103 indexed_as: HashMap::new(),
104 cursor: 0,
105 }
106 }
107}
108
109impl<K, V, B> IndexedView<K, V, B>
112where
113 K: Ord + Hash + Clone,
114{
115 fn record(&mut self, event: &Event<B>) {
120 if let Some(change) = (self.project)(event) {
121 match change {
122 Change::Put(k, v) => self.put(k, v),
123 Change::Delete(k) => self.delete(&k),
124 }
125 }
126 self.cursor = event.offset + 1;
127 }
128
129 fn put(&mut self, key: K, value: V) {
130 if let Some(stale) = self.indexed_as.remove(&key) {
133 self.prune_index_entries(&key, &stale);
134 }
135
136 let mut recorded: Vec<(String, IndexKey)> = Vec::new();
137 for (name, indexer) in &self.indexers {
138 for index_key in indexer(&value) {
139 recorded.push((name.clone(), index_key));
140 }
141 }
142 for (name, index_key) in &recorded {
143 self.indexes
144 .entry(name.clone())
145 .or_default()
146 .entry(index_key.clone())
147 .or_default()
148 .insert(key.clone());
149 }
150
151 self.indexed_as.insert(key.clone(), recorded);
152 self.primary.insert(key, value);
153 }
154
155 fn delete(&mut self, key: &K) {
156 if let Some(stale) = self.indexed_as.remove(key) {
157 self.prune_index_entries(key, &stale);
158 }
159 self.primary.remove(key);
160 }
161
162 fn prune_index_entries(&mut self, key: &K, entries: &[(String, IndexKey)]) {
165 for (name, index_key) in entries {
166 if let Some(index) = self.indexes.get_mut(name) {
167 if let Some(set) = index.get_mut(index_key) {
168 set.remove(key);
169 if set.is_empty() {
170 index.remove(index_key);
171 }
172 }
173 }
174 }
175 }
176}
177
178impl<K, V, B> IndexedView<K, V, B>
181where
182 K: Ord,
183{
184 pub fn get<Q>(&self, key: &Q) -> Option<&V>
186 where
187 K: Borrow<Q>,
188 Q: Ord + ?Sized,
189 {
190 self.primary.get(key)
191 }
192
193 pub fn range<T, R>(&self, range: R) -> impl Iterator<Item = (&K, &V)>
196 where
197 T: Ord + ?Sized,
198 K: Borrow<T>,
199 R: RangeBounds<T>,
200 {
201 self.primary.range(range)
202 }
203
204 pub fn by(&self, index: &str, index_key: &[u8]) -> Vec<&V> {
208 self.indexes
209 .get(index)
210 .and_then(|idx| idx.get(index_key))
211 .into_iter()
212 .flatten()
213 .filter_map(|k| self.primary.get(k))
214 .collect()
215 }
216
217 pub fn len(&self) -> usize {
219 self.primary.len()
220 }
221
222 pub fn is_empty(&self) -> bool {
224 self.primary.is_empty()
225 }
226}
227
228impl<V, B> IndexedView<String, V, B> {
232 pub fn prefix<'a>(&'a self, prefix: &'a str) -> impl Iterator<Item = (&'a String, &'a V)> + 'a {
235 self.primary
236 .range(prefix.to_string()..)
237 .take_while(move |(k, _)| k.starts_with(prefix))
238 }
239}
240
241impl<V, B> IndexedView<Vec<u8>, V, B> {
242 pub fn prefix<'a>(
244 &'a self,
245 prefix: &'a [u8],
246 ) -> impl Iterator<Item = (&'a Vec<u8>, &'a V)> + 'a {
247 self.primary
248 .range(prefix.to_vec()..)
249 .take_while(move |(k, _)| k.starts_with(prefix))
250 }
251}
252
253impl<K, V, B> View<B> for IndexedView<K, V, B>
256where
257 K: Ord + Hash + Clone + 'static,
258 V: 'static,
259 B: 'static,
260{
261 fn apply(&mut self, event: &Event<B>) {
262 self.record(event);
263 }
264 fn cursor(&self) -> u64 {
265 self.cursor
266 }
267 fn as_any(&self) -> &dyn Any {
268 self
269 }
270}
271
272impl<K, V, B> Projection for IndexedView<K, V, B>
273where
274 K: Ord + Hash + Clone + 'static,
275 V: 'static,
276 B: Body,
277{
278 type Body = B;
279 type State = BTreeMap<K, V>;
280
281 fn apply(&mut self, event: &Event<B>) {
282 self.record(event);
283 }
284 fn cursor(&self) -> u64 {
285 self.cursor
286 }
287 fn state(&self) -> &Self::State {
288 &self.primary
289 }
290}
291
292#[cfg(test)]
293mod tests {
294 use super::*;
295
296 #[derive(Clone)]
300 enum Ev {
301 Set(&'static str, u32),
302 Del(&'static str),
303 }
304
305 fn ev(offset: u64, body: Ev) -> Event<Ev> {
306 Event {
307 offset,
308 timestamp_ms: 0,
309 namespace: "ns".to_string(),
310 body,
311 }
312 }
313
314 fn view() -> IndexedView<String, u32, Ev> {
315 IndexedView::builder()
316 .project(|e: &Event<Ev>| match &e.body {
317 Ev::Set(k, v) => Some(Change::put(k.to_string(), *v)),
318 Ev::Del(k) => Some(Change::delete(k.to_string())),
319 })
320 .index("by_val", |v: &u32| vec![v.to_le_bytes().to_vec()])
321 .build()
322 }
323
324 fn k(n: u32) -> Vec<u8> {
325 n.to_le_bytes().to_vec()
326 }
327
328 #[test]
329 fn get_and_range() {
330 let mut v = view();
331 v.record(&ev(0, Ev::Set("a", 1)));
332 v.record(&ev(1, Ev::Set("b", 2)));
333 v.record(&ev(2, Ev::Set("c", 3)));
334
335 assert_eq!(v.get("a"), Some(&1));
336 assert_eq!(v.get("z"), None);
337 assert_eq!(v.len(), 3);
338 assert_eq!(v.cursor(), 3);
339
340 let scan: Vec<_> = v
341 .range("a".to_string().."c".to_string())
342 .map(|(k, val)| (k.clone(), *val))
343 .collect();
344 assert_eq!(scan, vec![("a".to_string(), 1), ("b".to_string(), 2)]);
345 }
346
347 #[test]
348 fn prefix_scan() {
349 let mut v = view();
350 v.record(&ev(0, Ev::Set("tool:a", 1)));
351 v.record(&ev(1, Ev::Set("tool:b", 2)));
352 v.record(&ev(2, Ev::Set("user:c", 3)));
353
354 let hits: Vec<_> = v.prefix("tool:").map(|(k, _)| k.clone()).collect();
355 assert_eq!(hits, vec!["tool:a".to_string(), "tool:b".to_string()]);
356 }
357
358 #[test]
359 fn by_returns_all_keys_sharing_an_index_key() {
360 let mut v = view();
361 v.record(&ev(0, Ev::Set("a", 1)));
362 v.record(&ev(1, Ev::Set("b", 1)));
363 v.record(&ev(2, Ev::Set("c", 2)));
364
365 assert_eq!(v.by("by_val", &k(1)), vec![&1, &1]);
367 assert_eq!(v.by("by_val", &k(2)), vec![&2]);
368 assert!(v.by("by_val", &k(9)).is_empty());
369 assert!(v.by("no_such_index", &k(1)).is_empty());
370 }
371
372 #[test]
373 fn update_moves_the_secondary_index_entry() {
374 let mut v = view();
375 v.record(&ev(0, Ev::Set("a", 1)));
376 assert_eq!(v.by("by_val", &k(1)), vec![&1]);
377
378 v.record(&ev(1, Ev::Set("a", 5)));
380 assert!(
381 v.by("by_val", &k(1)).is_empty(),
382 "stale index entry left behind"
383 );
384 assert_eq!(v.by("by_val", &k(5)), vec![&5]);
385 assert_eq!(v.get("a"), Some(&5));
386 }
387
388 #[test]
389 fn delete_removes_primary_and_all_index_entries() {
390 let mut v = view();
391 v.record(&ev(0, Ev::Set("a", 1)));
392 v.record(&ev(1, Ev::Set("b", 1)));
393 v.record(&ev(2, Ev::Del("a")));
394
395 assert_eq!(v.get("a"), None);
396 assert_eq!(v.by("by_val", &k(1)), vec![&1]); v.record(&ev(3, Ev::Del("b")));
399 assert!(v.by("by_val", &k(1)).is_empty());
400
401 assert!(!v.indexes["by_val"].contains_key(&k(1)));
404 assert!(v.indexed_as.is_empty());
405 }
406
407 #[test]
408 fn churn_leaves_no_orphaned_bookkeeping() {
409 let mut v = view();
410 v.record(&ev(0, Ev::Set("a", 1)));
412 v.record(&ev(1, Ev::Set("a", 2)));
413 v.record(&ev(2, Ev::Set("b", 2)));
414 v.record(&ev(3, Ev::Set("a", 3)));
415 v.record(&ev(4, Ev::Del("a")));
416 v.record(&ev(5, Ev::Del("b")));
417
418 assert!(v.is_empty());
419 assert!(v.indexed_as.is_empty());
420 assert!(v.indexes.values().all(|idx| idx.is_empty()));
422 }
423}