From c1a1eba8b53c7f0dddad496e76403a9064037039 Mon Sep 17 00:00:00 2001 From: Tobie Morgan Hitchcock Date: Sun, 28 Aug 2022 23:19:59 +0100 Subject: [PATCH] Add support for Record ID ranges Closes #66 --- lib/src/dbs/channel.rs | 60 +++++++++++++++++++++++++++++++ lib/src/dbs/iterate.rs | 60 +++++++++++++++++++++++++++++++ lib/src/dbs/iterator.rs | 2 ++ lib/src/sql/mod.rs | 2 ++ lib/src/sql/range.rs | 62 ++++++++++++++++++++++++++++++++ lib/src/sql/statements/delete.rs | 1 + lib/src/sql/statements/select.rs | 1 + lib/src/sql/statements/update.rs | 1 + lib/src/sql/value/value.rs | 21 ++++++++--- 9 files changed, 206 insertions(+), 4 deletions(-) create mode 100644 lib/src/sql/range.rs diff --git a/lib/src/dbs/channel.rs b/lib/src/dbs/channel.rs index 050223ff..05bb7cf5 100644 --- a/lib/src/dbs/channel.rs +++ b/lib/src/dbs/channel.rs @@ -135,6 +135,66 @@ impl Iterable { break; } } + Iterable::Range(v) => { + // Check that the table exists + txn.lock().await.check_ns_db_tb(opt.ns(), opt.db(), &v.tb, opt.strict).await?; + // Prepare the start and end keys + let beg = thing::new(opt.ns(), opt.db(), &v.tb, &v.beg).encode().unwrap(); + let end = thing::new(opt.ns(), opt.db(), &v.tb, &v.end).encode().unwrap(); + // Prepare the next holder key + let mut nxt: Option> = None; + // Loop until no more keys + loop { + // Check if the context is finished + if ctx.is_done() { + break; + } + // Get the next 1000 key-value entries + let res = match nxt { + None => { + let min = beg.clone(); + let max = end.clone(); + txn.clone().lock().await.scan(min..max, 1000).await? + } + Some(ref mut beg) => { + beg.push(0x00); + let min = beg.clone(); + let max = end.clone(); + txn.clone().lock().await.scan(min..max, 1000).await? + } + }; + // If there are key-value entries then fetch them + if !res.is_empty() { + // Get total results + let n = res.len(); + // Exit when settled + if n == 0 { + break; + } + // Loop over results + for (i, (k, v)) in res.into_iter().enumerate() { + // Check the context + if ctx.is_done() { + break; + } + // Ready the next + if n == i + 1 { + nxt = Some(k.clone()); + } + // Parse the data from the store + let key: crate::key::thing::Thing = (&k).into(); + let val: crate::sql::value::Value = (&v).into(); + let rid = Thing::from((key.tb, key.id)); + // Create a new operable value + let val = Operable::Value(val); + // Process the record + chn.send((Some(rid), val)).await?; + } + continue; + } + break; + } + } Iterable::Edges(e) => { // Pull out options let ns = opt.ns(); diff --git a/lib/src/dbs/iterate.rs b/lib/src/dbs/iterate.rs index e979b631..712c4469 100644 --- a/lib/src/dbs/iterate.rs +++ b/lib/src/dbs/iterate.rs @@ -135,6 +135,66 @@ impl Iterable { break; } } + Iterable::Range(v) => { + // Check that the table exists + txn.lock().await.check_ns_db_tb(opt.ns(), opt.db(), &v.tb, opt.strict).await?; + // Prepare the start and end keys + let beg = thing::new(opt.ns(), opt.db(), &v.tb, &v.beg).encode().unwrap(); + let end = thing::new(opt.ns(), opt.db(), &v.tb, &v.end).encode().unwrap(); + // Prepare the next holder key + let mut nxt: Option> = None; + // Loop until no more keys + loop { + // Check if the context is finished + if ctx.is_done() { + break; + } + // Get the next 1000 key-value entries + let res = match nxt { + None => { + let min = beg.clone(); + let max = end.clone(); + txn.clone().lock().await.scan(min..max, 1000).await? + } + Some(ref mut beg) => { + beg.push(0x00); + let min = beg.clone(); + let max = end.clone(); + txn.clone().lock().await.scan(min..max, 1000).await? + } + }; + // If there are key-value entries then fetch them + if !res.is_empty() { + // Get total results + let n = res.len(); + // Exit when settled + if n == 0 { + break; + } + // Loop over results + for (i, (k, v)) in res.into_iter().enumerate() { + // Check the context + if ctx.is_done() { + break; + } + // Ready the next + if n == i + 1 { + nxt = Some(k.clone()); + } + // Parse the data from the store + let key: crate::key::thing::Thing = (&k).into(); + let val: crate::sql::value::Value = (&v).into(); + let rid = Thing::from((key.tb, key.id)); + // Create a new operable value + let val = Operable::Value(val); + // Process the record + ite.process(ctx, opt, txn, stm, Some(rid), val).await; + } + continue; + } + break; + } + } Iterable::Edges(e) => { // Pull out options let ns = opt.ns(); diff --git a/lib/src/dbs/iterator.rs b/lib/src/dbs/iterator.rs index 64bb501d..2858041c 100644 --- a/lib/src/dbs/iterator.rs +++ b/lib/src/dbs/iterator.rs @@ -10,6 +10,7 @@ use crate::sql::array::Array; use crate::sql::edges::Edges; use crate::sql::field::Field; use crate::sql::part::Part; +use crate::sql::range::Range; use crate::sql::table::Table; use crate::sql::thing::Thing; use crate::sql::value::Value; @@ -22,6 +23,7 @@ pub enum Iterable { Value(Value), Table(Table), Thing(Thing), + Range(Range), Edges(Edges), Mergeable(Thing, Value), Relatable(Thing, Thing, Thing), diff --git a/lib/src/sql/mod.rs b/lib/src/sql/mod.rs index 006fdbf8..c776a22f 100644 --- a/lib/src/sql/mod.rs +++ b/lib/src/sql/mod.rs @@ -37,6 +37,7 @@ pub(crate) mod part; pub(crate) mod paths; pub(crate) mod permission; pub(crate) mod query; +pub(crate) mod range; pub(crate) mod regex; pub(crate) mod script; pub(crate) mod serde; @@ -100,6 +101,7 @@ pub use self::part::Part; pub use self::permission::Permission; pub use self::permission::Permissions; pub use self::query::Query; +pub use self::range::Range; pub use self::regex::Regex; pub use self::script::Script; pub use self::split::Split; diff --git a/lib/src/sql/range.rs b/lib/src/sql/range.rs new file mode 100644 index 00000000..77c31825 --- /dev/null +++ b/lib/src/sql/range.rs @@ -0,0 +1,62 @@ +use crate::sql::error::IResult; +use crate::sql::id::{id, Id}; +use crate::sql::ident::ident_raw; +use nom::character::complete::char; +use serde::{Deserialize, Serialize}; +use std::fmt; + +#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Serialize, Deserialize)] +pub struct Range { + pub tb: String, + pub beg: Id, + pub end: Id, +} + +impl fmt::Display for Range { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}:{}..{}", self.tb, self.beg, self.end) + } +} + +pub fn range(i: &str) -> IResult<&str, Range> { + let (i, tb) = ident_raw(i)?; + let (i, _) = char(':')(i)?; + let (i, beg) = id(i)?; + let (i, _) = char('.')(i)?; + let (i, _) = char('.')(i)?; + let (i, end) = id(i)?; + Ok(( + i, + Range { + tb, + beg, + end, + }, + )) +} +#[cfg(test)] +mod tests { + + use super::*; + + #[test] + fn range_array() { + let sql = "person:['USA', 10]..['USA', 100]"; + let res = range(sql); + assert!(res.is_ok()); + let out = res.unwrap().1; + assert_eq!(r#"person:["USA", 10]..["USA", 100]"#, format!("{}", out)); + } + + #[test] + fn range_object() { + let sql = "person:{ country: 'USA', position: 10 }..{ country: 'USA', position: 100 }"; + let res = range(sql); + assert!(res.is_ok()); + let out = res.unwrap().1; + assert_eq!( + r#"person:{ country: "USA", position: 10 }..{ country: "USA", position: 100 }"#, + format!("{}", out) + ); + } +} diff --git a/lib/src/sql/statements/delete.rs b/lib/src/sql/statements/delete.rs index 33df0940..6f9e973e 100644 --- a/lib/src/sql/statements/delete.rs +++ b/lib/src/sql/statements/delete.rs @@ -55,6 +55,7 @@ impl DeleteStatement { match v { Value::Table(v) => i.ingest(Iterable::Table(v)), Value::Thing(v) => i.ingest(Iterable::Thing(v)), + Value::Range(v) => i.ingest(Iterable::Range(*v)), Value::Edges(v) => i.ingest(Iterable::Edges(*v)), Value::Model(v) => { for v in v { diff --git a/lib/src/sql/statements/select.rs b/lib/src/sql/statements/select.rs index 23d248e8..4e00f79e 100644 --- a/lib/src/sql/statements/select.rs +++ b/lib/src/sql/statements/select.rs @@ -94,6 +94,7 @@ impl SelectStatement { match v { Value::Table(v) => i.ingest(Iterable::Table(v)), Value::Thing(v) => i.ingest(Iterable::Thing(v)), + Value::Range(v) => i.ingest(Iterable::Range(*v)), Value::Edges(v) => i.ingest(Iterable::Edges(*v)), Value::Model(v) => { for v in v { diff --git a/lib/src/sql/statements/update.rs b/lib/src/sql/statements/update.rs index 53ce6c56..d056a3e9 100644 --- a/lib/src/sql/statements/update.rs +++ b/lib/src/sql/statements/update.rs @@ -56,6 +56,7 @@ impl UpdateStatement { match v { Value::Table(v) => i.ingest(Iterable::Table(v)), Value::Thing(v) => i.ingest(Iterable::Thing(v)), + Value::Range(v) => i.ingest(Iterable::Range(*v)), Value::Edges(v) => i.ingest(Iterable::Edges(*v)), Value::Model(v) => { for v in v { diff --git a/lib/src/sql/value/value.rs b/lib/src/sql/value/value.rs index 2f0f9056..945876a1 100644 --- a/lib/src/sql/value/value.rs +++ b/lib/src/sql/value/value.rs @@ -23,6 +23,7 @@ use crate::sql::object::{object, Object}; use crate::sql::operation::Operation; use crate::sql::param::{param, Param}; use crate::sql::part::Part; +use crate::sql::range::{range, Range}; use crate::sql::regex::{regex, Regex}; use crate::sql::serde::is_internal_serialization; use crate::sql::strand::{strand, Strand}; @@ -114,6 +115,7 @@ pub enum Value { Thing(Thing), Model(Model), Regex(Regex), + Range(Box), Edges(Box), Function(Box), Subquery(Box), @@ -227,6 +229,12 @@ impl From for Value { } } +impl From for Value { + fn from(v: Range) -> Self { + Value::Range(Box::new(v)) + } +} + impl From for Value { fn from(v: Edges) -> Self { Value::Edges(Box::new(v)) @@ -1071,6 +1079,7 @@ impl fmt::Display for Value { Value::Thing(v) => write!(f, "{}", v), Value::Model(v) => write!(f, "{}", v), Value::Regex(v) => write!(f, "{}", v), + Value::Range(v) => write!(f, "{}", v), Value::Edges(v) => write!(f, "{}", v), Value::Function(v) => write!(f, "{}", v), Value::Subquery(v) => write!(f, "{}", v), @@ -1142,10 +1151,11 @@ impl Serialize for Value { Value::Thing(v) => s.serialize_newtype_variant("Value", 15, "Thing", v), Value::Model(v) => s.serialize_newtype_variant("Value", 16, "Model", v), Value::Regex(v) => s.serialize_newtype_variant("Value", 17, "Regex", v), - Value::Edges(v) => s.serialize_newtype_variant("Value", 18, "Edges", v), - Value::Function(v) => s.serialize_newtype_variant("Value", 19, "Function", v), - Value::Subquery(v) => s.serialize_newtype_variant("Value", 20, "Subquery", v), - Value::Expression(v) => s.serialize_newtype_variant("Value", 21, "Expression", v), + Value::Range(v) => s.serialize_newtype_variant("Value", 18, "Range", v), + Value::Edges(v) => s.serialize_newtype_variant("Value", 19, "Edges", v), + Value::Function(v) => s.serialize_newtype_variant("Value", 20, "Function", v), + Value::Subquery(v) => s.serialize_newtype_variant("Value", 21, "Subquery", v), + Value::Expression(v) => s.serialize_newtype_variant("Value", 22, "Expression", v), } } else { match self { @@ -1275,6 +1285,7 @@ pub fn select(i: &str) -> IResult<&str, Value> { map(regex, Value::from), map(model, Value::from), map(edges, Value::from), + map(range, Value::from), map(thing, Value::from), map(table, Value::from), map(strand, Value::from), @@ -1289,6 +1300,7 @@ pub fn what(i: &str) -> IResult<&str, Value> { map(param, Value::from), map(model, Value::from), map(edges, Value::from), + map(range, Value::from), map(thing, Value::from), map(table, Value::from), ))(i) @@ -1472,6 +1484,7 @@ mod tests { assert_eq!(56, std::mem::size_of::()); assert_eq!(48, std::mem::size_of::()); assert_eq!(24, std::mem::size_of::()); + assert_eq!(8, std::mem::size_of::>()); assert_eq!(8, std::mem::size_of::>()); assert_eq!(8, std::mem::size_of::>()); assert_eq!(8, std::mem::size_of::>());