Property path: faster eval for closed path

pull/355/head
Tpt 2 years ago committed by Thomas Tanon
parent 0a78eacfcd
commit 07e105e1be
  1. 288
      lib/src/sparql/eval.rs

@ -211,20 +211,16 @@ impl SimpleEvaluator {
};
match (input_subject, input_object, input_graph_name) {
(Some(input_subject), Some(input_object), Some(input_graph_name)) => {
Box::new(
path_eval
.eval_from_in_graph(&path, &input_subject, &input_graph_name)
.filter_map(move |o| match o {
Ok(o) => {
if o == input_object {
Some(Ok(from.clone()))
} else {
None
}
}
Err(error) => Some(Err(error)),
}),
)
match path_eval.eval_closed_in_graph(
&path,
&input_subject,
&input_object,
&input_graph_name,
) {
Ok(true) => Box::new(once(Ok(from))),
Ok(false) => Box::new(empty()),
Err(e) => Box::new(once(Err(e))),
}
}
(Some(input_subject), None, Some(input_graph_name)) => {
let object = object.clone();
@ -277,16 +273,16 @@ impl SimpleEvaluator {
let graph_name = graph_name.clone();
Box::new(
path_eval
.eval_to_in_unknown_graph(&path, &input_subject)
.eval_closed_in_unknown_graph(
&path,
&input_subject,
&input_object,
)
.filter_map(move |r| match r {
Ok((o, g)) => {
if o == input_object {
let mut new_tuple = from.clone();
put_pattern_value(&graph_name, g, &mut new_tuple)?;
Some(Ok(new_tuple))
} else {
None
}
Ok(g) => {
let mut new_tuple = from.clone();
put_pattern_value(&graph_name, g, &mut new_tuple)?;
Some(Ok(new_tuple))
}
Err(error) => Some(Err(error)),
}),
@ -3015,6 +3011,185 @@ struct PathEvaluator {
}
impl PathEvaluator {
fn eval_closed_in_graph(
&self,
path: &PlanPropertyPath,
start: &EncodedTerm,
end: &EncodedTerm,
graph_name: &EncodedTerm,
) -> Result<bool, EvaluationError> {
Ok(match path {
PlanPropertyPath::Path(p) => self
.dataset
.encoded_quads_for_pattern(Some(start), Some(p), Some(end), Some(graph_name))
.next()
.transpose()?
.is_some(),
PlanPropertyPath::Reverse(p) => self.eval_closed_in_graph(p, end, start, graph_name)?,
PlanPropertyPath::Sequence(a, b) => self
.eval_from_in_graph(a, start, graph_name)
.find_map(|middle| {
middle
.and_then(|middle| {
Ok(if self.eval_closed_in_graph(b, &middle, end, graph_name)? {
Some(())
} else {
None
})
})
.transpose()
})
.transpose()?
.is_some(),
PlanPropertyPath::Alternative(a, b) => {
self.eval_closed_in_graph(a, start, end, graph_name)?
|| self.eval_closed_in_graph(b, start, end, graph_name)?
}
PlanPropertyPath::ZeroOrMore(p) => {
if start == end {
self.is_subject_or_object_in_graph(start, graph_name)?
} else {
look_in_transitive_closure(
self.eval_from_in_graph(p, start, graph_name),
move |e| self.eval_from_in_graph(p, &e, graph_name),
end,
)?
}
}
PlanPropertyPath::OneOrMore(p) => look_in_transitive_closure(
self.eval_from_in_graph(p, start, graph_name),
move |e| self.eval_from_in_graph(p, &e, graph_name),
end,
)?,
PlanPropertyPath::ZeroOrOne(p) => {
if start == end {
self.is_subject_or_object_in_graph(start, graph_name)
} else {
self.eval_closed_in_graph(p, start, end, graph_name)
}?
}
PlanPropertyPath::NegatedPropertySet(ps) => self
.dataset
.encoded_quads_for_pattern(Some(start), None, Some(end), Some(graph_name))
.find_map(move |t| match t {
Ok(t) => {
if ps.contains(&t.predicate) {
None
} else {
Some(Ok(()))
}
}
Err(e) => Some(Err(e)),
})
.transpose()?
.is_some(),
})
}
fn eval_closed_in_unknown_graph(
&self,
path: &PlanPropertyPath,
start: &EncodedTerm,
end: &EncodedTerm,
) -> Box<dyn Iterator<Item = Result<EncodedTerm, EvaluationError>>> {
match path {
PlanPropertyPath::Path(p) => Box::new(
self.dataset
.encoded_quads_for_pattern(Some(start), Some(p), Some(end), None)
.map(|t| Ok(t?.graph_name)),
),
PlanPropertyPath::Reverse(p) => self.eval_closed_in_unknown_graph(p, end, start),
PlanPropertyPath::Sequence(a, b) => {
let eval = self.clone();
let b = b.clone();
let end = end.clone();
Box::new(self.eval_from_in_unknown_graph(a, start).flat_map_ok(
move |(middle, graph_name)| {
eval.eval_closed_in_graph(&b, &middle, &end, &graph_name)
.map(|is_found| if is_found { Some(graph_name) } else { None })
.transpose()
},
))
}
PlanPropertyPath::Alternative(a, b) => Box::new(hash_deduplicate(
self.eval_closed_in_unknown_graph(a, start, end)
.chain(self.eval_closed_in_unknown_graph(b, start, end)),
)),
PlanPropertyPath::ZeroOrMore(p) => {
let eval = self.clone();
let start2 = start.clone();
let end = end.clone();
let p = p.clone();
self.run_if_term_is_a_dataset_node(start, move |graph_name| {
look_in_transitive_closure(
Some(Ok(start2.clone())),
|e| eval.eval_from_in_graph(&p, &e, &graph_name),
&end,
)
.map(|is_found| if is_found { Some(graph_name) } else { None })
.transpose()
})
}
PlanPropertyPath::OneOrMore(p) => {
let eval = self.clone();
let end = end.clone();
let p = p.clone();
Box::new(
self.eval_from_in_unknown_graph(&p, start)
.filter_map(move |r| {
r.and_then(|(start, graph_name)| {
look_in_transitive_closure(
Some(Ok(start)),
|e| eval.eval_from_in_graph(&p, &e, &graph_name),
&end,
)
.map(|is_found| {
if is_found {
Some(graph_name)
} else {
None
}
})
})
.transpose()
}),
)
}
PlanPropertyPath::ZeroOrOne(p) => {
if start == end {
self.run_if_term_is_a_dataset_node(start, |graph_name| Some(Ok(graph_name)))
} else {
let eval = self.clone();
let start2 = start.clone();
let end = end.clone();
let p = p.clone();
self.run_if_term_is_a_dataset_node(start, move |graph_name| {
eval.eval_closed_in_graph(&p, &start2, &end, &graph_name)
.map(|is_found| if is_found { Some(graph_name) } else { None })
.transpose()
})
}
}
PlanPropertyPath::NegatedPropertySet(ps) => {
let ps = ps.clone();
Box::new(
self.dataset
.encoded_quads_for_pattern(Some(start), None, Some(end), None)
.filter_map(move |t| match t {
Ok(t) => {
if ps.contains(&t.predicate) {
None
} else {
Some(Ok(t.graph_name))
}
}
Err(e) => Some(Err(e)),
}),
)
}
}
}
fn eval_from_in_graph(
&self,
path: &PlanPropertyPath,
@ -3557,7 +3732,7 @@ impl PathEvaluator {
fn run_if_term_is_a_dataset_node<
T: 'static,
I: Iterator<Item = Result<T, EvaluationError>> + 'static,
I: IntoIterator<Item = Result<T, EvaluationError>> + 'static,
>(
&self,
term: &EncodedTerm,
@ -4013,46 +4188,57 @@ fn transitive_closure<T: Clone + Eq + Hash, NI: Iterator<Item = Result<T, Evalua
start: impl IntoIterator<Item = Result<T, EvaluationError>>,
mut next: impl FnMut(T) -> NI,
) -> impl Iterator<Item = Result<T, EvaluationError>> {
//TODO: optimize
let mut all = HashSet::<T>::default();
let mut errors = Vec::default();
let mut current = start
let mut errors = Vec::new();
let mut todo = start
.into_iter()
.filter_map(|e| match e {
Ok(e) => {
all.insert(e.clone());
Some(e)
}
Err(error) => {
errors.push(error);
Ok(e) => Some(e),
Err(e) => {
errors.push(e);
None
}
})
.collect::<Vec<_>>();
while !current.is_empty() {
current = current
.into_iter()
.flat_map(&mut next)
.filter_map(|e| match e {
let mut all = todo.iter().cloned().collect::<HashSet<_>>();
while let Some(e) = todo.pop() {
for e in next(e) {
match e {
Ok(e) => {
if all.contains(&e) {
None
} else {
all.insert(e.clone());
Some(e)
if all.insert(e.clone()) {
todo.push(e)
}
}
Err(error) => {
errors.push(error);
None
}
})
.collect();
Err(e) => errors.push(e),
}
}
}
errors.into_iter().map(Err).chain(all.into_iter().map(Ok))
}
fn look_in_transitive_closure<
T: Clone + Eq + Hash,
NI: Iterator<Item = Result<T, EvaluationError>>,
>(
start: impl IntoIterator<Item = Result<T, EvaluationError>>,
mut next: impl FnMut(T) -> NI,
target: &T,
) -> Result<bool, EvaluationError> {
let mut todo = start.into_iter().collect::<Result<Vec<_>, _>>()?;
let mut all = todo.iter().cloned().collect::<HashSet<_>>();
while let Some(e) = todo.pop() {
if e == *target {
return Ok(true);
}
for e in next(e) {
let e = e?;
if all.insert(e.clone()) {
todo.push(e);
}
}
}
Ok(false)
}
fn hash_deduplicate<T: Eq + Hash + Clone>(
iter: impl Iterator<Item = Result<T, EvaluationError>>,
) -> impl Iterator<Item = Result<T, EvaluationError>> {

Loading…
Cancel
Save