diff options
| author | metamuffin <metamuffin@disroot.org> | 2026-02-04 16:23:29 +0100 |
|---|---|---|
| committer | metamuffin <metamuffin@disroot.org> | 2026-02-04 16:23:29 +0100 |
| commit | 1af0468788c0a592a76398206e6c7479384853ec (patch) | |
| tree | 1860ec77be705a0715cc4c90c51176b22fabfca8 /database/src | |
| parent | 088b6b323130b0a9509c76f395c7ed4bf5609234 (diff) | |
| download | jellything-1af0468788c0a592a76398206e6c7479384853ec.tar jellything-1af0468788c0a592a76398206e6c7479384853ec.tar.bz2 jellything-1af0468788c0a592a76398206e6c7479384853ec.tar.zst | |
stuff
Diffstat (limited to 'database/src')
| -rw-r--r-- | database/src/counters.rs | 61 | ||||
| -rw-r--r-- | database/src/filter/binning.rs | 4 | ||||
| -rw-r--r-- | database/src/lib.rs | 1 | ||||
| -rw-r--r-- | database/src/query.rs | 4 | ||||
| -rw-r--r-- | database/src/sort/mod.rs | 32 | ||||
| -rw-r--r-- | database/src/sort/none.rs | 9 | ||||
| -rw-r--r-- | database/src/sort/value.rs | 9 | ||||
| -rw-r--r-- | database/src/table.rs | 74 |
8 files changed, 151 insertions, 43 deletions
diff --git a/database/src/counters.rs b/database/src/counters.rs new file mode 100644 index 0000000..17e2de8 --- /dev/null +++ b/database/src/counters.rs @@ -0,0 +1,61 @@ +/* + This file is part of jellything (https://codeberg.org/metamuffin/jellything) + which is licensed under the GNU Affero General Public License (version 3); see /COPYING. + Copyright (C) 2026 metamuffin <metamuffin.org> +*/ +use crate::{ + filter::binning::Binning, + query::Query, + table::{RowNum, TableNum}, +}; +use anyhow::Result; +use jellykv::{ReadTransaction, WriteTransaction}; +use jellyobject::Object; +use std::collections::HashMap; + +pub(crate) struct Counters(pub HashMap<Binning, TableNum>); +impl Counters { + pub fn update( + &self, + txn: &mut dyn WriteTransaction, + ob: Object<'_>, + remove: bool, + ) -> Result<()> { + for (b, &table) in &self.0 { + let mut k = vec![vec![]]; + b.apply(ob, &mut k); + if k.is_empty() { + continue; + } + let mut counter = read_counter(txn, table)?; + if remove { + counter += k.len() as u64; + } else { + counter -= k.len() as u64; + } + write_counter(txn, table, counter)?; + } + Ok(()) + } + pub fn count(&self, txn: &dyn ReadTransaction, query: &Query) -> Result<Option<u64>> { + let mut total = 0; + for binning in query.filter.get_bins() { + let Some(b) = self.0.get(&binning) else { + return Ok(None); + }; + total += read_counter(txn, *b)?; + } + Ok(Some(total)) + } +} + +pub fn write_counter(txn: &mut dyn WriteTransaction, t: TableNum, value: u64) -> Result<()> { + txn.set(&t.to_be_bytes(), &value.to_be_bytes()) +} +pub fn read_counter(txn: &dyn ReadTransaction, t: TableNum) -> Result<u64> { + Ok(txn + .get(&t.to_be_bytes())? + .map(|k| k.as_slice().try_into().map(RowNum::from_be_bytes).ok()) + .flatten() + .unwrap_or(0)) +} diff --git a/database/src/filter/binning.rs b/database/src/filter/binning.rs index 8588317..3265e1a 100644 --- a/database/src/filter/binning.rs +++ b/database/src/filter/binning.rs @@ -7,10 +7,10 @@ use jellyobject::{Object, Path}; /// Sorted list of components to bin objects by filterable values. -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Binning(Vec<BinningComponent>); -#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord)] +#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)] pub enum BinningComponent { Has(Path), Match(Path), diff --git a/database/src/lib.rs b/database/src/lib.rs index d5d6d21..00e6905 100644 --- a/database/src/lib.rs +++ b/database/src/lib.rs @@ -10,6 +10,7 @@ pub mod sort; pub mod table; #[cfg(test)] pub mod test_shared; +pub mod counters; pub type Pad32 = u32; diff --git a/database/src/query.rs b/database/src/query.rs index 41f4b83..002a872 100644 --- a/database/src/query.rs +++ b/database/src/query.rs @@ -18,14 +18,16 @@ pub enum Sort { None, Value(ValueSort), TextSearch(Path, String), - Count, } + pub struct ValueSort { pub order: SortOrder, pub path: Path, pub multi: MultiBehaviour, pub offset: Option<Vec<u8>>, } + +#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)] pub enum MultiBehaviour { First, ForEach, diff --git a/database/src/sort/mod.rs b/database/src/sort/mod.rs index e87630c..58d8eff 100644 --- a/database/src/sort/mod.rs +++ b/database/src/sort/mod.rs @@ -4,20 +4,34 @@ Copyright (C) 2026 metamuffin <metamuffin.org> */ -use crate::table::RowNum; +use crate::{ + query::{MultiBehaviour, Sort}, + table::IndexKey, +}; use anyhow::Result; use jellykv::WriteTransaction; -use jellyobject::Object; +use jellyobject::{Object, Path}; pub mod none; pub mod value; -pub trait Index: Send + Sync + 'static { - fn add(&self, db: &mut dyn WriteTransaction, row: RowNum, val: Object) -> Result<()>; - fn remove(&self, db: &mut dyn WriteTransaction, row: RowNum, val: Object) -> Result<()>; - /// Might return true if objects are identical for this index; false if not or uncertain - fn compare(&self, before: Object, after: Object) -> bool { - let _ = (before, after); - false +#[derive(Hash, PartialEq, Eq)] +pub enum SortKey { + None, + Value(Path, MultiBehaviour), + Text(Path), +} + +impl Sort { + pub fn key(&self) -> SortKey { + match self { + Sort::None => SortKey::None, + Sort::Value(vs) => SortKey::Value(vs.path.clone(), vs.multi), + Sort::TextSearch(p, _) => SortKey::Text(p.to_owned()), + } } } + +pub fn index_add(txn: &mut dyn WriteTransaction, ik: &IndexKey, ob: &Object) -> Result<()> { + +} diff --git a/database/src/sort/none.rs b/database/src/sort/none.rs index 65454a2..efaa7f8 100644 --- a/database/src/sort/none.rs +++ b/database/src/sort/none.rs @@ -6,11 +6,11 @@ use crate::{ filter::binning::Binning, - sort::Index, - table::{RowNum, TableNum}, + query::Sort, + table::{RowIter, RowNum, TableNum}, }; use anyhow::Result; -use jellykv::WriteTransaction; +use jellykv::{ReadTransaction, WriteTransaction}; use jellyobject::Object; pub struct UnsortedIndex { @@ -47,4 +47,7 @@ impl Index for UnsortedIndex { fn compare(&self, before: Object, after: Object) -> bool { self.keys(0, before) == self.keys(0, after) } + fn query(&self, txn: &mut dyn ReadTransaction, _sort: &Sort) -> Result<RowIter> { + todo!() + } } diff --git a/database/src/sort/value.rs b/database/src/sort/value.rs index 79bdc0b..6e42fd7 100644 --- a/database/src/sort/value.rs +++ b/database/src/sort/value.rs @@ -6,12 +6,12 @@ use crate::{ filter::binning::Binning, - query::{MultiBehaviour, ValueSort}, + query::{MultiBehaviour, Sort, ValueSort}, sort::Index, - table::{RowNum, TableNum}, + table::{RowIter, RowNum, TableNum}, }; use anyhow::Result; -use jellykv::WriteTransaction; +use jellykv::{ReadTransaction, WriteTransaction}; use jellyobject::Object; pub struct ValueIndex { @@ -50,6 +50,9 @@ impl Index for ValueIndex { fn compare(&self, before: Object, after: Object) -> bool { self.keys(0, before) == self.keys(0, after) } + fn query(&self, txn: &mut dyn ReadTransaction, sort: &Sort) -> Result<RowIter> { + todo!() + } } impl ValueSort { fn apply(&self, ob: Object, keys: &mut Vec<Vec<u8>>) { diff --git a/database/src/table.rs b/database/src/table.rs index ad3775a..ee8d66b 100644 --- a/database/src/table.rs +++ b/database/src/table.rs @@ -4,24 +4,35 @@ Copyright (C) 2026 metamuffin <metamuffin.org> */ -use crate::{query::Query, sort::Index}; +use crate::{ + counters::{read_counter, write_counter}, + filter::binning::Binning, + prefix_iterator::PrefixIterator, + query::Query, + sort::{Index, SortKey}, +}; use anyhow::{Result, anyhow}; use jellykv::{ReadTransaction, WriteTransaction}; use jellyobject::ObjectBuffer; use std::collections::HashMap; -pub type TableNum = u64; +pub type TableNum = u32; pub type RowNum = u64; +pub type RowIter = Box<dyn Iterator<Item = Result<RowNum>>>; +pub type IndexKey = (Binning, SortKey); + pub struct Table { - id: u32, - pub(crate) indices: HashMap<IndexKey, Box<dyn Index>>, + id: TableNum, + pub(crate) counters: HashMap<Binning, TableNum>, + pub(crate) indices: HashMap<IndexKey, TableNum>, } impl Table { pub fn new(id: u32) -> Self { Self { id, - indices: Vec::new(), + counters: HashMap::new(), + indices: HashMap::new(), } } fn key(&self, row: RowNum) -> Vec<u8> { @@ -30,29 +41,28 @@ impl Table { key.extend(row.to_be_bytes()); key } + pub fn iter(&self, txn: &dyn ReadTransaction) -> Result<impl Iterator<Item = Result<RowNum>>> { + Ok(PrefixIterator { + inner: txn.iter(&self.id.to_be_bytes(), false)?, + prefix: self.id.to_be_bytes().to_vec().into(), + } + .map(|r| r.map(|r| RowNum::from_be_bytes(r.try_into().unwrap())))) + } pub fn insert(&self, txn: &mut dyn WriteTransaction, entry: ObjectBuffer) -> Result<RowNum> { - let mut id_counter = txn - .get(&self.id.to_be_bytes())? - .map(|k| k.as_slice().try_into().map(RowNum::from_be_bytes).ok()) - .flatten() - .unwrap_or(0); + let mut id_counter = read_counter(txn, self.id)?; let row = id_counter; id_counter += 1; - txn.set(&self.id.to_be_bytes(), &id_counter.to_be_bytes())?; + write_counter(txn, self.id, id_counter)?; txn.set(&self.key(row), bytemuck::cast_slice(entry.0.as_slice()))?; let ob = entry.as_object(); - for idx in &self.indices { + for idx in self.indices.values() { idx.add(txn, row, ob)?; } Ok(row) } - pub fn add_index<T: Index + Clone + 'static>(&mut self, index: T) -> T { - self.indices.push(Box::new(index.clone())); - index - } pub fn get(&self, txn: &dyn ReadTransaction, row: RowNum) -> Result<Option<ObjectBuffer>> { Ok(txn.get(&self.key(row))?.map(ObjectBuffer::from)) } @@ -61,8 +71,8 @@ impl Table { return Ok(false); }; let ob = entry.as_object(); - for idx in &self.indices { - idx.remove(db, row, ob)?; + for index in self.indices.values() { + index.remove(db, row, ob)?; } db.del(&self.key(row))?; Ok(true) @@ -81,7 +91,7 @@ impl Table { txn.set(&self.key(row), bytemuck::cast_slice(entry.0.as_slice()))?; - for index in &self.indices { + for index in self.indices.values() { if !index.compare(before, after) { index.remove(txn, row, before)?; index.add(txn, row, after)?; @@ -90,15 +100,29 @@ impl Table { Ok(()) } - pub fn query( - &self, - txn: &dyn ReadTransaction, - query: Query, - ) -> Box<dyn Iterator<Item = Result<RowNum>>> { + pub fn query(&self, txn: &dyn ReadTransaction, query: Query) -> Result<RowIter> { + // query + // .filter + // .get_bins() + // .into_iter() + // .flat_map(|b| { + // let ikey = (b, query.sort.key()); + // self.indices.get(&ikey) + // }) + // .map(|i| i.query(txn, &query.sort)) todo!() } pub fn query_single(&self, txn: &dyn ReadTransaction, query: Query) -> Result<Option<RowNum>> { - self.query(txn, query).next().transpose() + self.query(txn, query)?.next().transpose() + } + pub fn count(&self, txn: &dyn ReadTransaction, query: Query) -> Result<u64> { + let mut total = 0; + for b in query.filter.get_bins() { + if let Some(&c) = self.counters.get(&b) { + total += read_counter(txn, c)?; + } + } + Ok(total) } } |