use std::{collections::BTreeMap, fmt::Debug, ops::Bound, sync::Arc};
use self::vertex_info::InternalVertexInfo;
use super::InterpretedQuery;
use crate::ir::{
EdgeKind, EdgeParameters, Eid, FieldValue, IREdge, IRFold, IRQueryComponent, IRVertex, Output,
Recursive, Vid,
};
mod candidates;
mod dynamic;
mod filters;
mod sealed;
mod vertex_info;
pub use candidates::{CandidateValue, Range};
pub use dynamic::DynamicallyResolvedValue;
pub use vertex_info::VertexInfo;
#[non_exhaustive]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct QueryInfo<'a> {
query: &'a InterpretedQuery,
}
impl<'a> QueryInfo<'a> {
fn new(query: &'a InterpretedQuery) -> Self {
Self { query }
}
#[allow(dead_code)] #[inline]
pub fn outputs(&self) -> &BTreeMap<Arc<str>, Output> {
&self.query.indexed_query.outputs
}
#[allow(dead_code)] #[inline]
pub fn variables(&self) -> &Arc<BTreeMap<Arc<str>, FieldValue>> {
&self.query.arguments
}
}
#[non_exhaustive]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ResolveInfo {
query: InterpretedQuery,
current_vid: Vid,
vertex_completed: bool,
}
#[non_exhaustive]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ResolveEdgeInfo {
query: InterpretedQuery,
current_vid: Vid,
target_vid: Vid,
crossing_eid: Eid,
}
impl ResolveInfo {
pub(crate) fn new(query: InterpretedQuery, current_vid: Vid, vertex_completed: bool) -> Self {
Self {
query,
current_vid,
vertex_completed,
}
}
pub(crate) fn into_inner(self) -> InterpretedQuery {
self.query
}
#[allow(dead_code)] #[inline]
pub fn query(&self) -> QueryInfo<'_> {
QueryInfo::new(&self.query)
}
}
impl sealed::__Sealed for ResolveInfo {}
impl sealed::__Sealed for NeighborInfo {}
impl InternalVertexInfo for ResolveInfo {
#[inline]
fn query(&self) -> &InterpretedQuery {
&self.query
}
#[inline]
fn non_binding_filters(&self) -> bool {
false }
#[inline]
fn execution_frontier(&self) -> Bound<Vid> {
if self.vertex_completed {
Bound::Included(self.current_vid)
} else {
Bound::Excluded(self.current_vid)
}
}
#[inline]
fn current_vertex(&self) -> &IRVertex {
&self.current_component().vertices[&self.current_vid]
}
#[inline]
fn current_component(&self) -> &IRQueryComponent {
self.starting_component()
}
#[inline]
fn starting_component(&self) -> &IRQueryComponent {
&self.query.indexed_query.vids[&self.current_vid]
}
#[inline]
fn query_variables(&self) -> &BTreeMap<Arc<str>, FieldValue> {
self.query.arguments.as_ref()
}
fn make_non_folded_edge_info(&self, edge: &IREdge) -> EdgeInfo {
let neighboring_info = NeighborInfo {
query: self.query.clone(),
execution_frontier: self.execution_frontier(),
starting_vertex: self.current_vid,
neighbor_vertex: edge.to_vid,
neighbor_path: vec![edge.eid],
within_optional_scope: edge.optional,
locally_non_binding_filters: check_locally_non_binding_filters_for_edge(edge),
};
EdgeInfo {
eid: edge.eid,
parameters: edge.parameters.clone(),
optional: edge.optional,
recursive: edge.recursive.clone(),
folded: false,
destination: neighboring_info,
}
}
fn make_folded_edge_info(&self, fold: &IRFold) -> EdgeInfo {
let at_least_one_element_required =
filters::fold_requires_at_least_one_element(&self.query.arguments, fold);
let neighboring_info = NeighborInfo {
query: self.query.clone(),
execution_frontier: self.execution_frontier(),
starting_vertex: self.current_vid,
neighbor_vertex: fold.to_vid,
neighbor_path: vec![fold.eid],
within_optional_scope: !at_least_one_element_required,
locally_non_binding_filters: false,
};
EdgeInfo {
eid: fold.eid,
parameters: fold.parameters.clone(),
optional: false,
recursive: None,
folded: true,
destination: neighboring_info,
}
}
}
impl ResolveEdgeInfo {
pub(crate) fn new(
query: InterpretedQuery,
current_vid: Vid,
target_vid: Vid,
crossing_eid: Eid,
) -> Self {
Self {
query,
current_vid,
target_vid,
crossing_eid,
}
}
pub(crate) fn into_inner(self) -> InterpretedQuery {
self.query
}
#[allow(dead_code)] #[inline]
pub fn query(&self) -> QueryInfo<'_> {
QueryInfo::new(&self.query)
}
#[inline]
pub fn eid(&self) -> Eid {
self.crossing_eid
}
#[inline]
pub fn origin_vid(&self) -> Vid {
self.current_vid
}
#[allow(dead_code)] #[inline]
pub fn destination_vid(&self) -> Vid {
self.target_vid
}
#[allow(dead_code)] #[inline]
pub fn destination(&self) -> NeighborInfo {
self.edge().destination
}
#[allow(dead_code)] #[inline]
pub fn edge(&self) -> EdgeInfo {
let eid = self.eid();
match &self.query.indexed_query.eids[&eid] {
EdgeKind::Regular(regular) => {
debug_assert_eq!(
self.target_vid, regular.to_vid,
"expected Vid {:?} but got {:?} in edge {regular:?}",
self.target_vid, regular.to_vid
);
EdgeInfo {
eid,
parameters: regular.parameters.clone(),
optional: regular.optional,
recursive: regular.recursive.clone(),
folded: false,
destination: NeighborInfo {
query: self.query.clone(),
execution_frontier: Bound::Excluded(self.target_vid),
starting_vertex: self.origin_vid(),
neighbor_vertex: regular.to_vid,
neighbor_path: vec![eid],
within_optional_scope: regular.optional,
locally_non_binding_filters: check_locally_non_binding_filters_for_edge(
regular,
),
},
}
}
EdgeKind::Fold(fold) => {
debug_assert_eq!(
self.target_vid, fold.to_vid,
"expected Vid {:?} but got {:?} in fold {fold:?}",
self.target_vid, fold.to_vid
);
let within_optional_scope = false;
EdgeInfo {
eid,
parameters: fold.parameters.clone(),
optional: false,
recursive: None,
folded: true,
destination: NeighborInfo {
query: self.query.clone(),
execution_frontier: Bound::Excluded(self.target_vid),
starting_vertex: self.origin_vid(),
neighbor_vertex: fold.to_vid,
neighbor_path: vec![eid],
within_optional_scope,
locally_non_binding_filters: false,
},
}
}
}
}
}
#[non_exhaustive]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct EdgeInfo {
eid: Eid,
parameters: EdgeParameters,
optional: bool,
recursive: Option<Recursive>,
folded: bool,
destination: NeighborInfo,
}
impl EdgeInfo {
#[allow(dead_code)] #[inline]
pub fn eid(&self) -> Eid {
self.eid
}
#[allow(dead_code)] #[inline]
pub fn parameters(&self) -> &EdgeParameters {
&self.parameters
}
#[allow(dead_code)] #[inline]
pub fn destination(&self) -> &NeighborInfo {
&self.destination
}
}
#[non_exhaustive]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct NeighborInfo {
query: InterpretedQuery,
execution_frontier: Bound<Vid>, starting_vertex: Vid,
neighbor_vertex: Vid,
neighbor_path: Vec<Eid>,
within_optional_scope: bool,
locally_non_binding_filters: bool,
}
impl InternalVertexInfo for NeighborInfo {
#[inline]
fn query(&self) -> &InterpretedQuery {
&self.query
}
#[inline]
fn non_binding_filters(&self) -> bool {
self.within_optional_scope || self.locally_non_binding_filters
}
#[inline]
fn execution_frontier(&self) -> Bound<Vid> {
self.execution_frontier
}
#[inline]
fn current_vertex(&self) -> &IRVertex {
&self.current_component().vertices[&self.neighbor_vertex]
}
#[inline]
fn current_component(&self) -> &IRQueryComponent {
&self.query.indexed_query.vids[&self.neighbor_vertex]
}
#[inline]
fn starting_component(&self) -> &IRQueryComponent {
&self.query.indexed_query.vids[&self.starting_vertex]
}
#[inline]
fn query_variables(&self) -> &BTreeMap<Arc<str>, FieldValue> {
self.query.arguments.as_ref()
}
fn make_non_folded_edge_info(&self, edge: &IREdge) -> EdgeInfo {
let mut neighbor_path = self.neighbor_path.clone();
neighbor_path.push(edge.eid);
let neighboring_info = NeighborInfo {
query: self.query.clone(),
execution_frontier: self.execution_frontier,
starting_vertex: self.starting_vertex,
neighbor_vertex: edge.to_vid,
neighbor_path,
within_optional_scope: self.within_optional_scope,
locally_non_binding_filters: check_locally_non_binding_filters_for_edge(edge),
};
EdgeInfo {
eid: edge.eid,
parameters: edge.parameters.clone(),
optional: edge.optional,
recursive: edge.recursive.clone(),
folded: false,
destination: neighboring_info,
}
}
fn make_folded_edge_info(&self, fold: &IRFold) -> EdgeInfo {
let at_least_one_element_required =
filters::fold_requires_at_least_one_element(&self.query.arguments, fold);
let mut neighbor_path = self.neighbor_path.clone();
neighbor_path.push(fold.eid);
let neighboring_info = NeighborInfo {
query: self.query.clone(),
execution_frontier: self.execution_frontier,
starting_vertex: self.starting_vertex,
neighbor_vertex: fold.to_vid,
neighbor_path,
within_optional_scope: self.within_optional_scope || !at_least_one_element_required,
locally_non_binding_filters: false,
};
EdgeInfo {
eid: fold.eid,
parameters: fold.parameters.clone(),
optional: false,
recursive: None,
folded: true,
destination: neighboring_info,
}
}
}
fn check_locally_non_binding_filters_for_edge(edge: &IREdge) -> bool {
edge.recursive
.as_ref()
.map(|r| r.depth.get() >= 2)
.unwrap_or(false)
}
#[cfg(test)]
mod tests;