aboutsummaryrefslogtreecommitdiff
path: root/database/src
diff options
context:
space:
mode:
authormetamuffin <metamuffin@disroot.org>2026-02-04 16:23:29 +0100
committermetamuffin <metamuffin@disroot.org>2026-02-04 16:23:29 +0100
commit1af0468788c0a592a76398206e6c7479384853ec (patch)
tree1860ec77be705a0715cc4c90c51176b22fabfca8 /database/src
parent088b6b323130b0a9509c76f395c7ed4bf5609234 (diff)
downloadjellything-1af0468788c0a592a76398206e6c7479384853ec.tar
jellything-1af0468788c0a592a76398206e6c7479384853ec.tar.bz2
jellything-1af0468788c0a592a76398206e6c7479384853ec.tar.zst
stuff
Diffstat (limited to 'database/src')
-rw-r--r--database/src/counters.rs61
-rw-r--r--database/src/filter/binning.rs4
-rw-r--r--database/src/lib.rs1
-rw-r--r--database/src/query.rs4
-rw-r--r--database/src/sort/mod.rs32
-rw-r--r--database/src/sort/none.rs9
-rw-r--r--database/src/sort/value.rs9
-rw-r--r--database/src/table.rs74
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)
}
}