use super::ProjectedRelationshipPredicate;
use crate::background::CheckpointWorkContext;
use hawdb_core::{HawDBError, Result, Value};
use std::cmp::Ordering;
use std::collections::BTreeMap;
fn compare(left: &[u8], right: &[u8], work: &CheckpointWorkContext) -> Result<Ordering> {
for (left, right) in left.chunks(64 * 1024).zip(right.chunks(64 * 1024)) {
let unit = work.start_unit().map_err(HawDBError::from_storage_error)?;
let ordering = left.cmp(right);
unit.finish();
if !ordering.is_eq() {
work.checkpoint().map_err(HawDBError::from_storage_error)?;
return Ok(ordering);
}
}
let unit = work.start_unit().map_err(HawDBError::from_storage_error)?;
let ordering = left.len().cmp(&right.len());
unit.finish();
work.checkpoint().map_err(HawDBError::from_storage_error)?;
Ok(ordering)
}
fn property<'a>(
properties: &'a BTreeMap<String, Value>,
name: &str,
work: &CheckpointWorkContext,
) -> Result<Option<&'a Value>> {
for (key, value) in properties {
match compare(key.as_bytes(), name.as_bytes(), work)? {
Ordering::Less => {}
Ordering::Equal => return Ok(Some(value)),
Ordering::Greater => break,
}
}
work.checkpoint().map_err(HawDBError::from_storage_error)?;
Ok(None)
}
fn equal(left: &Value, right: &Value, work: &CheckpointWorkContext) -> Result<bool> {
let unit = work.start_unit().map_err(HawDBError::from_storage_error)?;
let scalar = match (left, right) {
(Value::Null, Value::Null) => Some(true),
(Value::Bool(left), Value::Bool(right)) => Some(left == right),
(Value::Int(left), Value::Int(right)) => Some(left == right),
(Value::Float(left), Value::Float(right)) => Some(left.total_cmp(right).is_eq()),
(Value::Uuid(left), Value::Uuid(right)) => Some(left == right),
(Value::String(_), Value::String(_))
| (Value::Binary(_), Value::Binary(_))
| (Value::Vector(_), Value::Vector(_))
| (Value::List(_), Value::List(_))
| (Value::Map(_), Value::Map(_)) => None,
_ => Some(false),
};
unit.finish();
if let Some(equal) = scalar {
work.checkpoint().map_err(HawDBError::from_storage_error)?;
return Ok(equal);
}
let equal = match (left, right) {
(Value::String(left), Value::String(right)) => {
compare(left.as_bytes(), right.as_bytes(), work)?.is_eq()
}
(Value::Binary(left), Value::Binary(right)) => compare(left, right, work)?.is_eq(),
(Value::Vector(left), Value::Vector(right)) => {
compare(left.encoded(), right.encoded(), work)?.is_eq()
}
(Value::List(left), Value::List(right)) => {
if left.len() != right.len() {
return Ok(false);
}
for (left, right) in left.iter().zip(right) {
if !self::equal(left, right, work)? {
return Ok(false);
}
}
true
}
(Value::Map(left), Value::Map(right)) => {
if left.len() != right.len() {
return Ok(false);
}
for ((left_key, left), (right_key, right)) in left.iter().zip(right) {
if !compare(left_key.as_bytes(), right_key.as_bytes(), work)?.is_eq()
|| !self::equal(left, right, work)?
{
return Ok(false);
}
}
true
}
_ => unreachable!("scalar and mismatched variants were compared above"),
};
work.checkpoint().map_err(HawDBError::from_storage_error)?;
Ok(equal)
}
pub(in crate::projection) fn matches(
predicate: &ProjectedRelationshipPredicate,
properties: &BTreeMap<String, Value>,
work: &CheckpointWorkContext,
) -> Result<bool> {
work.start_unit()
.map_err(HawDBError::from_storage_error)?
.finish();
let matched = match predicate {
ProjectedRelationshipPredicate::And(predicates) => {
for predicate in predicates {
if !matches(predicate, properties, work)? {
return Ok(false);
}
}
true
}
ProjectedRelationshipPredicate::Eq {
property: name,
value,
} => match property(properties, name, work)? {
Some(actual) => equal(actual, value, work)?,
None => false,
},
ProjectedRelationshipPredicate::Gte {
property: name,
value,
} => match property(properties, name, work)? {
Some(Value::String(actual)) if matches!(value, Value::String(_)) => {
let Value::String(value) = value else {
unreachable!();
};
!compare(actual.as_bytes(), value.as_bytes(), work)?.is_lt()
}
Some(actual) => {
let unit = work.start_unit().map_err(HawDBError::from_storage_error)?;
let matched = crate::predicate::comparable_value_ordering(actual, value)
.is_some_and(|ordering| !ordering.is_lt());
unit.finish();
matched
}
None => false,
},
};
work.checkpoint().map_err(HawDBError::from_storage_error)?;
Ok(matched)
}
#[cfg(test)]
mod tests;