Add support for Record ID ranges

Closes #66
This commit is contained in:
Tobie Morgan Hitchcock 2022-08-28 23:19:59 +01:00
parent e26d86b412
commit c1a1eba8b5
9 changed files with 206 additions and 4 deletions

View file

@ -135,6 +135,66 @@ impl Iterable {
break; 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<Vec<u8>> = 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) => { Iterable::Edges(e) => {
// Pull out options // Pull out options
let ns = opt.ns(); let ns = opt.ns();

View file

@ -135,6 +135,66 @@ impl Iterable {
break; 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<Vec<u8>> = 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) => { Iterable::Edges(e) => {
// Pull out options // Pull out options
let ns = opt.ns(); let ns = opt.ns();

View file

@ -10,6 +10,7 @@ use crate::sql::array::Array;
use crate::sql::edges::Edges; use crate::sql::edges::Edges;
use crate::sql::field::Field; use crate::sql::field::Field;
use crate::sql::part::Part; use crate::sql::part::Part;
use crate::sql::range::Range;
use crate::sql::table::Table; use crate::sql::table::Table;
use crate::sql::thing::Thing; use crate::sql::thing::Thing;
use crate::sql::value::Value; use crate::sql::value::Value;
@ -22,6 +23,7 @@ pub enum Iterable {
Value(Value), Value(Value),
Table(Table), Table(Table),
Thing(Thing), Thing(Thing),
Range(Range),
Edges(Edges), Edges(Edges),
Mergeable(Thing, Value), Mergeable(Thing, Value),
Relatable(Thing, Thing, Thing), Relatable(Thing, Thing, Thing),

View file

@ -37,6 +37,7 @@ pub(crate) mod part;
pub(crate) mod paths; pub(crate) mod paths;
pub(crate) mod permission; pub(crate) mod permission;
pub(crate) mod query; pub(crate) mod query;
pub(crate) mod range;
pub(crate) mod regex; pub(crate) mod regex;
pub(crate) mod script; pub(crate) mod script;
pub(crate) mod serde; pub(crate) mod serde;
@ -100,6 +101,7 @@ pub use self::part::Part;
pub use self::permission::Permission; pub use self::permission::Permission;
pub use self::permission::Permissions; pub use self::permission::Permissions;
pub use self::query::Query; pub use self::query::Query;
pub use self::range::Range;
pub use self::regex::Regex; pub use self::regex::Regex;
pub use self::script::Script; pub use self::script::Script;
pub use self::split::Split; pub use self::split::Split;

62
lib/src/sql/range.rs Normal file
View file

@ -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)
);
}
}

View file

@ -55,6 +55,7 @@ impl DeleteStatement {
match v { match v {
Value::Table(v) => i.ingest(Iterable::Table(v)), Value::Table(v) => i.ingest(Iterable::Table(v)),
Value::Thing(v) => i.ingest(Iterable::Thing(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::Edges(v) => i.ingest(Iterable::Edges(*v)),
Value::Model(v) => { Value::Model(v) => {
for v in v { for v in v {

View file

@ -94,6 +94,7 @@ impl SelectStatement {
match v { match v {
Value::Table(v) => i.ingest(Iterable::Table(v)), Value::Table(v) => i.ingest(Iterable::Table(v)),
Value::Thing(v) => i.ingest(Iterable::Thing(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::Edges(v) => i.ingest(Iterable::Edges(*v)),
Value::Model(v) => { Value::Model(v) => {
for v in v { for v in v {

View file

@ -56,6 +56,7 @@ impl UpdateStatement {
match v { match v {
Value::Table(v) => i.ingest(Iterable::Table(v)), Value::Table(v) => i.ingest(Iterable::Table(v)),
Value::Thing(v) => i.ingest(Iterable::Thing(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::Edges(v) => i.ingest(Iterable::Edges(*v)),
Value::Model(v) => { Value::Model(v) => {
for v in v { for v in v {

View file

@ -23,6 +23,7 @@ use crate::sql::object::{object, Object};
use crate::sql::operation::Operation; use crate::sql::operation::Operation;
use crate::sql::param::{param, Param}; use crate::sql::param::{param, Param};
use crate::sql::part::Part; use crate::sql::part::Part;
use crate::sql::range::{range, Range};
use crate::sql::regex::{regex, Regex}; use crate::sql::regex::{regex, Regex};
use crate::sql::serde::is_internal_serialization; use crate::sql::serde::is_internal_serialization;
use crate::sql::strand::{strand, Strand}; use crate::sql::strand::{strand, Strand};
@ -114,6 +115,7 @@ pub enum Value {
Thing(Thing), Thing(Thing),
Model(Model), Model(Model),
Regex(Regex), Regex(Regex),
Range(Box<Range>),
Edges(Box<Edges>), Edges(Box<Edges>),
Function(Box<Function>), Function(Box<Function>),
Subquery(Box<Subquery>), Subquery(Box<Subquery>),
@ -227,6 +229,12 @@ impl From<Duration> for Value {
} }
} }
impl From<Range> for Value {
fn from(v: Range) -> Self {
Value::Range(Box::new(v))
}
}
impl From<Edges> for Value { impl From<Edges> for Value {
fn from(v: Edges) -> Self { fn from(v: Edges) -> Self {
Value::Edges(Box::new(v)) Value::Edges(Box::new(v))
@ -1071,6 +1079,7 @@ impl fmt::Display for Value {
Value::Thing(v) => write!(f, "{}", v), Value::Thing(v) => write!(f, "{}", v),
Value::Model(v) => write!(f, "{}", v), Value::Model(v) => write!(f, "{}", v),
Value::Regex(v) => write!(f, "{}", v), Value::Regex(v) => write!(f, "{}", v),
Value::Range(v) => write!(f, "{}", v),
Value::Edges(v) => write!(f, "{}", v), Value::Edges(v) => write!(f, "{}", v),
Value::Function(v) => write!(f, "{}", v), Value::Function(v) => write!(f, "{}", v),
Value::Subquery(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::Thing(v) => s.serialize_newtype_variant("Value", 15, "Thing", v),
Value::Model(v) => s.serialize_newtype_variant("Value", 16, "Model", v), Value::Model(v) => s.serialize_newtype_variant("Value", 16, "Model", v),
Value::Regex(v) => s.serialize_newtype_variant("Value", 17, "Regex", v), Value::Regex(v) => s.serialize_newtype_variant("Value", 17, "Regex", v),
Value::Edges(v) => s.serialize_newtype_variant("Value", 18, "Edges", v), Value::Range(v) => s.serialize_newtype_variant("Value", 18, "Range", v),
Value::Function(v) => s.serialize_newtype_variant("Value", 19, "Function", v), Value::Edges(v) => s.serialize_newtype_variant("Value", 19, "Edges", v),
Value::Subquery(v) => s.serialize_newtype_variant("Value", 20, "Subquery", v), Value::Function(v) => s.serialize_newtype_variant("Value", 20, "Function", v),
Value::Expression(v) => s.serialize_newtype_variant("Value", 21, "Expression", v), Value::Subquery(v) => s.serialize_newtype_variant("Value", 21, "Subquery", v),
Value::Expression(v) => s.serialize_newtype_variant("Value", 22, "Expression", v),
} }
} else { } else {
match self { match self {
@ -1275,6 +1285,7 @@ pub fn select(i: &str) -> IResult<&str, Value> {
map(regex, Value::from), map(regex, Value::from),
map(model, Value::from), map(model, Value::from),
map(edges, Value::from), map(edges, Value::from),
map(range, Value::from),
map(thing, Value::from), map(thing, Value::from),
map(table, Value::from), map(table, Value::from),
map(strand, Value::from), map(strand, Value::from),
@ -1289,6 +1300,7 @@ pub fn what(i: &str) -> IResult<&str, Value> {
map(param, Value::from), map(param, Value::from),
map(model, Value::from), map(model, Value::from),
map(edges, Value::from), map(edges, Value::from),
map(range, Value::from),
map(thing, Value::from), map(thing, Value::from),
map(table, Value::from), map(table, Value::from),
))(i) ))(i)
@ -1472,6 +1484,7 @@ mod tests {
assert_eq!(56, std::mem::size_of::<crate::sql::thing::Thing>()); assert_eq!(56, std::mem::size_of::<crate::sql::thing::Thing>());
assert_eq!(48, std::mem::size_of::<crate::sql::model::Model>()); assert_eq!(48, std::mem::size_of::<crate::sql::model::Model>());
assert_eq!(24, std::mem::size_of::<crate::sql::regex::Regex>()); assert_eq!(24, std::mem::size_of::<crate::sql::regex::Regex>());
assert_eq!(8, std::mem::size_of::<Box<crate::sql::range::Range>>());
assert_eq!(8, std::mem::size_of::<Box<crate::sql::edges::Edges>>()); assert_eq!(8, std::mem::size_of::<Box<crate::sql::edges::Edges>>());
assert_eq!(8, std::mem::size_of::<Box<crate::sql::function::Function>>()); assert_eq!(8, std::mem::size_of::<Box<crate::sql::function::Function>>());
assert_eq!(8, std::mem::size_of::<Box<crate::sql::subquery::Subquery>>()); assert_eq!(8, std::mem::size_of::<Box<crate::sql::subquery::Subquery>>());