use crate::jsondb::encoding::{encode_index_value, encode_primary_key};
use crate::record::ScanRange;
use serde_json::Value;
use std::ops::Bound;
#[derive(Debug, Clone)]
pub struct KeyRange {
pub lower: Option<Value>,
pub upper: Option<Value>,
pub lower_open: bool,
pub upper_open: bool,
}
impl Default for KeyRange {
fn default() -> Self {
Self {
lower: None,
upper: None,
lower_open: false,
upper_open: false,
}
}
}
impl KeyRange {
pub fn only(key: Value) -> Self {
Self {
lower: Some(key.clone()),
upper: Some(key),
lower_open: false,
upper_open: false,
}
}
pub fn bound(lower: Value, upper: Value, lower_open: bool, upper_open: bool) -> Self {
Self {
lower: Some(lower),
upper: Some(upper),
lower_open,
upper_open,
}
}
pub fn lower_bound(key: Value, open: bool) -> Self {
Self {
lower: Some(key),
upper: None,
lower_open: open,
upper_open: false,
}
}
pub fn upper_bound(key: Value, open: bool) -> Self {
Self {
lower: None,
upper: Some(key),
lower_open: false,
upper_open: open,
}
}
pub fn includes(&self, key: &Value) -> bool {
if let Some(ref lower) = self.lower {
let cmp = compare_values(key, lower);
if self.lower_open {
if cmp != std::cmp::Ordering::Greater {
return false;
}
} else if cmp == std::cmp::Ordering::Less {
return false;
}
}
if let Some(ref upper) = self.upper {
let cmp = compare_values(key, upper);
if self.upper_open {
if cmp != std::cmp::Ordering::Less {
return false;
}
} else if cmp == std::cmp::Ordering::Greater {
return false;
}
}
true
}
pub fn is_unbounded(&self) -> bool {
self.lower.is_none() && self.upper.is_none()
}
pub(crate) fn to_doc_scan_range(&self, store: &str) -> crate::error::Result<ScanRange> {
let pfx = crate::jsondb::encoding::doc_prefix(store);
let key_start = match &self.lower {
None => Bound::Included(pfx.clone()),
Some(val) => {
let key_bytes = encode_primary_key(val)?;
let full = [pfx.as_slice(), &key_bytes].concat();
if self.lower_open {
Bound::Excluded(full)
} else {
Bound::Included(full)
}
}
};
let key_end = match &self.upper {
None => {
let end = crate::record::increment_prefix_bytes(&pfx);
Bound::Excluded(end)
}
Some(val) => {
let key_bytes = encode_primary_key(val)?;
let full = [pfx.as_slice(), &key_bytes].concat();
if self.upper_open {
Bound::Excluded(full)
} else {
Bound::Included(full)
}
}
};
Ok(ScanRange {
key_start,
key_end,
ts_start: Bound::Unbounded,
ts_end: Bound::Unbounded,
})
}
pub(crate) fn to_index_scan_range(
&self,
store: &str,
index: &str,
) -> crate::error::Result<ScanRange> {
let pfx = crate::jsondb::encoding::idx_prefix(store, index);
let key_start = match &self.lower {
None => Bound::Included(pfx.clone()),
Some(val) => {
let encoded = encode_index_value(val);
let full = [pfx.as_slice(), &encoded].concat();
if self.lower_open {
Bound::Excluded(full)
} else {
Bound::Included(full)
}
}
};
let key_end = match &self.upper {
None => {
let end = crate::record::increment_prefix_bytes(&pfx);
Bound::Excluded(end)
}
Some(val) => {
let encoded = encode_index_value(val);
let full = [pfx.as_slice(), &encoded].concat();
if self.upper_open {
Bound::Excluded(full)
} else {
Bound::Included(full)
}
}
};
Ok(ScanRange {
key_start,
key_end,
ts_start: Bound::Unbounded,
ts_end: Bound::Unbounded,
})
}
}
fn compare_values(a: &Value, b: &Value) -> std::cmp::Ordering {
let enc_a = encode_index_value(a);
let enc_b = encode_index_value(b);
enc_a.cmp(&enc_b)
}