use crate::id::id_from_value;
use crate::id::id_into_value;
use crate::id::Id;
use crate::id::RawId;
use crate::id::ID_LEN;
use crate::query::Binding;
use crate::query::Constraint;
use crate::query::Variable;
use crate::query::VariableId;
use crate::query::VariableSet;
use crate::trible::TribleSet;
use crate::value::schemas::genid::GenId;
use crate::value::RawValue;
pub struct EntityRangeConstraint {
variable_e: VariableId,
min: RawId,
max: RawId,
set: TribleSet,
}
impl EntityRangeConstraint {
pub fn new(variable_e: Variable<GenId>, min: Id, max: Id, set: TribleSet) -> Self {
EntityRangeConstraint {
variable_e: variable_e.index,
min: min.into(),
max: max.into(),
set,
}
}
}
impl<'a> Constraint<'a> for EntityRangeConstraint {
fn variables(&self) -> VariableSet {
VariableSet::new_singleton(self.variable_e)
}
fn estimate(&self, variable: VariableId, _binding: &Binding) -> Option<usize> {
if variable != self.variable_e {
return None;
}
let count = self
.set
.eav
.count_range::<0, ID_LEN>(&[0u8; 0], &self.min, &self.max);
Some(count.min(usize::MAX as u64) as usize)
}
fn propose(&self, variable: VariableId, _binding: &Binding, proposals: &mut Vec<RawValue>) {
if variable != self.variable_e {
return;
}
self.set
.eav
.infixes_range::<0, ID_LEN, _>(&[0u8; 0], &self.min, &self.max, |e| {
proposals.push(id_into_value(e));
});
}
fn confirm(&self, variable: VariableId, _binding: &Binding, proposals: &mut Vec<RawValue>) {
if variable == self.variable_e {
proposals.retain(|v| {
let Some(id) = id_from_value(v) else {
return false;
};
id >= self.min && id <= self.max
});
}
}
fn satisfied(&self, binding: &Binding) -> bool {
match binding.get(self.variable_e) {
Some(v) => {
let Some(id) = id_from_value(v) else {
return false;
};
id >= self.min && id <= self.max
}
None => true,
}
}
}
pub struct AttributeRangeConstraint {
variable_a: VariableId,
min: RawId,
max: RawId,
set: TribleSet,
}
impl AttributeRangeConstraint {
pub fn new(variable_a: Variable<GenId>, min: Id, max: Id, set: TribleSet) -> Self {
AttributeRangeConstraint {
variable_a: variable_a.index,
min: min.into(),
max: max.into(),
set,
}
}
}
impl<'a> Constraint<'a> for AttributeRangeConstraint {
fn variables(&self) -> VariableSet {
VariableSet::new_singleton(self.variable_a)
}
fn estimate(&self, variable: VariableId, _binding: &Binding) -> Option<usize> {
if variable != self.variable_a {
return None;
}
let count = self
.set
.aev
.count_range::<0, ID_LEN>(&[0u8; 0], &self.min, &self.max);
Some(count.min(usize::MAX as u64) as usize)
}
fn propose(&self, variable: VariableId, _binding: &Binding, proposals: &mut Vec<RawValue>) {
if variable != self.variable_a {
return;
}
self.set
.aev
.infixes_range::<0, ID_LEN, _>(&[0u8; 0], &self.min, &self.max, |a| {
proposals.push(id_into_value(a));
});
}
fn confirm(&self, variable: VariableId, _binding: &Binding, proposals: &mut Vec<RawValue>) {
if variable == self.variable_a {
proposals.retain(|v| {
let Some(id) = id_from_value(v) else {
return false;
};
id >= self.min && id <= self.max
});
}
}
fn satisfied(&self, binding: &Binding) -> bool {
match binding.get(self.variable_a) {
Some(v) => {
let Some(id) = id_from_value(v) else {
return false;
};
id >= self.min && id <= self.max
}
None => true,
}
}
}
#[cfg(test)]
mod tests {
use crate::prelude::valueschemas::R256BE;
use crate::prelude::*;
attributes! {
"CC00000000000000CC00000000000000" as id_range_test_score: R256BE;
}
#[test]
fn entity_in_range_proposes_correctly() {
let e1 = ufoid();
let e2 = ufoid();
let e3 = ufoid();
let e4 = ufoid();
let v10: Value<R256BE> = 10i128.to_value();
let v50: Value<R256BE> = 50i128.to_value();
let v90: Value<R256BE> = 90i128.to_value();
let v100: Value<R256BE> = 100i128.to_value();
let mut data = TribleSet::new();
data += entity! { &e1 @ id_range_test_score: v10 };
data += entity! { &e2 @ id_range_test_score: v50 };
data += entity! { &e3 @ id_range_test_score: v90 };
data += entity! { &e4 @ id_range_test_score: v100 };
let mut sorted_ids: Vec<Id> = vec![*e1, *e2, *e3, *e4];
sorted_ids.sort_by_key(|id| -> RawId { (*id).into() });
let min_id = sorted_ids[1];
let max_id = sorted_ids[2];
let filtered: Vec<Id> = find!(
(id: Id, v: Value<R256BE>),
and!(
pattern!(&data, [{ ?id @ id_range_test_score: ?v }]),
data.entity_in_range(id, min_id, max_id),
)
)
.map(|(id, _v)| id)
.collect();
assert_eq!(filtered.len(), 2);
let all: Vec<Id> = find!(
(id: Id, v: Value<R256BE>),
pattern!(&data, [{ ?id @ id_range_test_score: ?v }])
)
.map(|(id, _v)| id)
.collect();
assert_eq!(all.len(), 4);
}
#[test]
fn entity_in_range_boundary_and_empty() {
let e1 = ufoid();
let e2 = ufoid();
let v1: Value<R256BE> = 1i128.to_value();
let v2: Value<R256BE> = 2i128.to_value();
let mut data = TribleSet::new();
data += entity! { &e1 @ id_range_test_score: v1 };
data += entity! { &e2 @ id_range_test_score: v2 };
let id1: Id = *e1;
let exact: Vec<Id> = find!(
(id: Id, v: Value<R256BE>),
and!(
pattern!(&data, [{ ?id @ id_range_test_score: ?v }]),
data.entity_in_range(id, id1, id1),
)
)
.map(|(id, _)| id)
.collect();
assert_eq!(exact.len(), 1);
assert_eq!(exact[0], id1);
let id2: Id = *e2;
let exact2: Vec<Id> = find!(
(id: Id, v: Value<R256BE>),
and!(
pattern!(&data, [{ ?id @ id_range_test_score: ?v }]),
data.entity_in_range(id, id2, id2),
)
)
.map(|(id, _)| id)
.collect();
assert_eq!(exact2.len(), 1);
assert_eq!(exact2[0], id2);
}
}