use super::Adapter;
use uuid::Uuid;
use crate::{
Object,
edge::{
Edge,
query::{EdgeQuery, ObjectEdge},
},
error::Error,
query::{
Comparison, Cursor, IndexField, Operator, QueryFilter, QueryMode, QuerySearch, QuerySort,
ToIndexValue,
},
system_owner,
};
#[derive(Debug, Clone)]
pub(crate) enum TraversalDirection {
Forward,
Reverse,
}
#[derive(Debug, Clone)]
pub struct Query {
pub owner: Uuid, pub filters: Vec<QueryFilter>,
pub limit: Option<u32>,
pub cursor: Option<Cursor>,
}
impl Default for Query {
fn default() -> Self {
Self {
owner: system_owner(),
filters: Vec::new(),
limit: None,
cursor: None,
}
}
}
impl Query {
pub fn new(owner: Uuid) -> Self {
Self {
owner,
filters: Vec::new(),
limit: None,
cursor: None,
}
}
pub fn wide() -> Self {
Self {
owner: Uuid::nil(),
filters: Vec::new(),
limit: None,
cursor: None,
}
}
pub fn filter(
self,
field: &'static IndexField,
value: impl ToIndexValue,
mode: QueryMode,
) -> Self {
let mut consumed_self = self;
consumed_self.filters.push(QueryFilter {
field,
value: value.to_index_value(),
mode,
});
consumed_self
}
pub fn where_eq(self, field: &'static IndexField, value: impl ToIndexValue) -> Self {
let mut consumed_self = self;
consumed_self.filters.push(QueryFilter {
field,
value: value.to_index_value(),
mode: QueryMode::Search(QuerySearch {
comparison: Comparison::Equal,
operator: Operator::default(),
}),
});
consumed_self
}
pub fn where_ne(self, field: &'static IndexField, value: impl ToIndexValue) -> Self {
let mut consumed_self = self;
consumed_self.filters.push(QueryFilter {
field,
value: value.to_index_value(),
mode: QueryMode::Search(QuerySearch {
comparison: Comparison::NotEqual,
operator: Operator::default(),
}),
});
consumed_self
}
pub fn where_gt(self, field: &'static IndexField, value: impl ToIndexValue) -> Self {
let mut consumed_self = self;
consumed_self.filters.push(QueryFilter {
field,
value: value.to_index_value(),
mode: QueryMode::Search(QuerySearch {
comparison: Comparison::GreaterThan,
operator: Operator::default(),
}),
});
consumed_self
}
pub fn where_gte(self, field: &'static IndexField, value: impl ToIndexValue) -> Self {
let mut consumed_self = self;
consumed_self.filters.push(QueryFilter {
field,
value: value.to_index_value(),
mode: QueryMode::Search(QuerySearch {
comparison: Comparison::GreaterThanOrEqual,
operator: Operator::default(),
}),
});
consumed_self
}
pub fn where_lt(self, field: &'static IndexField, value: impl ToIndexValue) -> Self {
let mut consumed_self = self;
consumed_self.filters.push(QueryFilter {
field,
value: value.to_index_value(),
mode: QueryMode::Search(QuerySearch {
comparison: Comparison::LessThan,
operator: Operator::default(),
}),
});
consumed_self
}
pub fn where_lte(self, field: &'static IndexField, value: impl ToIndexValue) -> Self {
let mut consumed_self = self;
consumed_self.filters.push(QueryFilter {
field,
value: value.to_index_value(),
mode: QueryMode::Search(QuerySearch {
comparison: Comparison::LessThanOrEqual,
operator: Operator::default(),
}),
});
consumed_self
}
pub fn where_contains(self, field: &'static IndexField, value: impl ToIndexValue) -> Self {
let mut consumed_self = self;
consumed_self.filters.push(QueryFilter {
field,
value: value.to_index_value(),
mode: QueryMode::Search(QuerySearch {
comparison: Comparison::Contains,
operator: Operator::default(),
}),
});
consumed_self
}
pub fn where_contains_all(self, field: &'static IndexField, value: impl ToIndexValue) -> Self {
let mut consumed_self = self;
consumed_self.filters.push(QueryFilter {
field,
value: value.to_index_value(),
mode: QueryMode::Search(QuerySearch {
comparison: Comparison::ContainsAll,
operator: Operator::default(),
}),
});
consumed_self
}
pub fn where_begins_with(self, field: &'static IndexField, value: impl ToIndexValue) -> Self {
let mut consumed_self = self;
consumed_self.filters.push(QueryFilter {
field,
value: value.to_index_value(),
mode: QueryMode::Search(QuerySearch {
comparison: Comparison::BeginsWith,
operator: Operator::default(),
}),
});
consumed_self
}
pub fn where_not_begins_with(
self,
field: &'static IndexField,
value: impl ToIndexValue,
) -> Self {
let mut consumed_self = self;
consumed_self.filters.push(QueryFilter {
field,
value: value.to_index_value(),
mode: QueryMode::Search(QuerySearch {
comparison: Comparison::NotBeginsWith,
operator: Operator::default(),
}),
});
consumed_self
}
pub fn where_not_contains(
self,
field: &'static IndexField,
value: impl ToIndexValue,
) -> Self {
let mut consumed_self = self;
consumed_self.filters.push(QueryFilter {
field,
value: value.to_index_value(),
mode: QueryMode::Search(QuerySearch {
comparison: Comparison::NotContains,
operator: Operator::default(),
}),
});
consumed_self
}
pub fn where_not_contains_all(
self,
field: &'static IndexField,
value: impl ToIndexValue,
) -> Self {
let mut consumed_self = self;
consumed_self.filters.push(QueryFilter {
field,
value: value.to_index_value(),
mode: QueryMode::Search(QuerySearch {
comparison: Comparison::NotContainsAll,
operator: Operator::default(),
}),
});
consumed_self
}
pub fn where_not_in(self, field: &'static IndexField, value: impl ToIndexValue) -> Self {
let mut consumed_self = self;
consumed_self.filters.push(QueryFilter {
field,
value: value.to_index_value(),
mode: QueryMode::Search(QuerySearch {
comparison: Comparison::NotIn,
operator: Operator::default(),
}),
});
consumed_self
}
pub fn sort_asc(self, field: &'static IndexField) -> Self {
let mut consumed_self = self;
consumed_self.filters.push(QueryFilter {
field,
value: field.name.to_index_value(), mode: QueryMode::Sort(QuerySort { ascending: true }),
});
consumed_self
}
pub fn sort_desc(self, field: &'static IndexField) -> Self {
let mut consumed_self = self;
consumed_self.filters.push(QueryFilter {
field,
value: field.name.to_index_value(), mode: QueryMode::Sort(QuerySort { ascending: false }),
});
consumed_self
}
pub fn sort_random(self) -> Self {
let mut consumed_self = self;
consumed_self.filters.push(QueryFilter::random_sort());
consumed_self
}
pub fn or_eq(self, field: &'static IndexField, value: impl ToIndexValue) -> Self {
let mut consumed_self = self;
consumed_self.filters.push(QueryFilter {
field,
value: value.to_index_value(),
mode: QueryMode::Search(QuerySearch {
comparison: Comparison::Equal,
operator: Operator::Or,
}),
});
consumed_self
}
pub fn or_ne(self, field: &'static IndexField, value: impl ToIndexValue) -> Self {
let mut consumed_self = self;
consumed_self.filters.push(QueryFilter {
field,
value: value.to_index_value(),
mode: QueryMode::Search(QuerySearch {
comparison: Comparison::NotEqual,
operator: Operator::Or,
}),
});
consumed_self
}
pub fn or_gt(self, field: &'static IndexField, value: impl ToIndexValue) -> Self {
let mut consumed_self = self;
consumed_self.filters.push(QueryFilter {
field,
value: value.to_index_value(),
mode: QueryMode::Search(QuerySearch {
comparison: Comparison::GreaterThan,
operator: Operator::Or,
}),
});
consumed_self
}
pub fn or_gte(self, field: &'static IndexField, value: impl ToIndexValue) -> Self {
let mut consumed_self = self;
consumed_self.filters.push(QueryFilter {
field,
value: value.to_index_value(),
mode: QueryMode::Search(QuerySearch {
comparison: Comparison::GreaterThanOrEqual,
operator: Operator::Or,
}),
});
consumed_self
}
pub fn or_lt(self, field: &'static IndexField, value: impl ToIndexValue) -> Self {
let mut consumed_self = self;
consumed_self.filters.push(QueryFilter {
field,
value: value.to_index_value(),
mode: QueryMode::Search(QuerySearch {
comparison: Comparison::LessThan,
operator: Operator::Or,
}),
});
consumed_self
}
pub fn or_lte(self, field: &'static IndexField, value: impl ToIndexValue) -> Self {
let mut consumed_self = self;
consumed_self.filters.push(QueryFilter {
field,
value: value.to_index_value(),
mode: QueryMode::Search(QuerySearch {
comparison: Comparison::LessThanOrEqual,
operator: Operator::Or,
}),
});
consumed_self
}
pub fn or_contains(self, field: &'static IndexField, value: impl ToIndexValue) -> Self {
let mut consumed_self = self;
consumed_self.filters.push(QueryFilter {
field,
value: value.to_index_value(),
mode: QueryMode::Search(QuerySearch {
comparison: Comparison::Contains,
operator: Operator::Or,
}),
});
consumed_self
}
pub fn or_contains_all(self, field: &'static IndexField, value: impl ToIndexValue) -> Self {
let mut consumed_self = self;
consumed_self.filters.push(QueryFilter {
field,
value: value.to_index_value(),
mode: QueryMode::Search(QuerySearch {
comparison: Comparison::ContainsAll,
operator: Operator::Or,
}),
});
consumed_self
}
pub fn or_begins_with(self, field: &'static IndexField, value: impl ToIndexValue) -> Self {
let mut consumed_self = self;
consumed_self.filters.push(QueryFilter {
field,
value: value.to_index_value(),
mode: QueryMode::Search(QuerySearch {
comparison: Comparison::BeginsWith,
operator: Operator::Or,
}),
});
consumed_self
}
pub fn or_not_begins_with(
self,
field: &'static IndexField,
value: impl ToIndexValue,
) -> Self {
let mut consumed_self = self;
consumed_self.filters.push(QueryFilter {
field,
value: value.to_index_value(),
mode: QueryMode::Search(QuerySearch {
comparison: Comparison::NotBeginsWith,
operator: Operator::Or,
}),
});
consumed_self
}
pub fn or_not_contains(self, field: &'static IndexField, value: impl ToIndexValue) -> Self {
let mut consumed_self = self;
consumed_self.filters.push(QueryFilter {
field,
value: value.to_index_value(),
mode: QueryMode::Search(QuerySearch {
comparison: Comparison::NotContains,
operator: Operator::Or,
}),
});
consumed_self
}
pub fn or_not_contains_all(
self,
field: &'static IndexField,
value: impl ToIndexValue,
) -> Self {
let mut consumed_self = self;
consumed_self.filters.push(QueryFilter {
field,
value: value.to_index_value(),
mode: QueryMode::Search(QuerySearch {
comparison: Comparison::NotContainsAll,
operator: Operator::Or,
}),
});
consumed_self
}
pub fn or_not_in(self, field: &'static IndexField, value: impl ToIndexValue) -> Self {
let mut consumed_self = self;
consumed_self.filters.push(QueryFilter {
field,
value: value.to_index_value(),
mode: QueryMode::Search(QuerySearch {
comparison: Comparison::NotIn,
operator: Operator::Or,
}),
});
consumed_self
}
pub fn with_limit(mut self, limit: u32) -> Self {
self.limit = Some(limit);
self
}
pub fn with_cursor(mut self, cursor: Uuid) -> Self {
self.cursor = Some(Cursor { last_id: cursor });
self
}
}
#[macro_export]
macro_rules! filter {
($field:expr, $value:expr) => {{
use $crate::query::ToIndexValue;
$crate::query::QueryFilter {
field: $field,
value: $value.to_index_value(),
mode: $crate::query::QueryMode::Search($crate::query::QuerySearch {
comparison: $crate::query::Comparison::Equal,
operator: $crate::query::Operator::default(),
}),
}
}};
}
pub struct QueryContext<'a, T> {
root: Uuid,
adapter: &'a dyn Adapter,
_marker: std::marker::PhantomData<T>,
}
impl<'a, T: Object> QueryContext<'a, T> {
pub(crate) fn new(adapter: &'a dyn Adapter, root: Uuid) -> Self {
Self {
root,
adapter,
_marker: std::marker::PhantomData,
}
}
pub async fn get(&self) -> Result<Option<T>, Error> {
let val = self.adapter.fetch_object(T::TYPE, self.root).await?;
match val {
Some(o) => o.to_object().map(|o| Some(o)),
None => Ok(None),
}
}
pub fn edge<E: Edge, O: Object>(self) -> EdgeQueryContext<'a, E, O> {
EdgeQueryContext::new(self.adapter, self.root)
}
pub fn preload<C: Object>(self) -> OwnedContext<'a, T, C> {
OwnedContext::new(self.adapter, self.root)
}
}
pub struct EdgeQueryContext<'a, E: Edge, O: crate::Object> {
owner: Uuid,
filters: Vec<QueryFilter>,
edge_filters: Vec<QueryFilter>,
limit: Option<u32>,
cursor: Option<Cursor>,
adapter: &'a dyn Adapter,
_marker: std::marker::PhantomData<(E, O)>,
}
impl<'a, E: Edge, O: Object> EdgeQueryContext<'a, E, O> {
pub(crate) fn new(adapter: &'a dyn Adapter, owner: Uuid) -> Self {
Self {
adapter,
owner,
filters: vec![],
edge_filters: vec![],
limit: None,
cursor: None,
_marker: std::marker::PhantomData,
}
}
pub fn filter(
mut self,
field: &'static IndexField,
value: impl ToIndexValue,
mode: QueryMode,
) -> Self {
self.filters.push(QueryFilter {
field,
value: value.to_index_value(),
mode,
});
self
}
pub fn where_eq(mut self, field: &'static IndexField, value: impl ToIndexValue) -> Self {
self.filters.push(QueryFilter {
field,
value: value.to_index_value(),
mode: QueryMode::Search(QuerySearch {
comparison: Comparison::Equal,
operator: Operator::default(),
}),
});
self
}
pub fn where_ne(mut self, field: &'static IndexField, value: impl ToIndexValue) -> Self {
self.filters.push(QueryFilter {
field,
value: value.to_index_value(),
mode: QueryMode::Search(QuerySearch {
comparison: Comparison::NotEqual,
operator: Operator::default(),
}),
});
self
}
pub fn where_gt(mut self, field: &'static IndexField, value: impl ToIndexValue) -> Self {
self.filters.push(QueryFilter {
field,
value: value.to_index_value(),
mode: QueryMode::Search(QuerySearch {
comparison: Comparison::GreaterThan,
operator: Operator::default(),
}),
});
self
}
pub fn where_gte(mut self, field: &'static IndexField, value: impl ToIndexValue) -> Self {
self.filters.push(QueryFilter {
field,
value: value.to_index_value(),
mode: QueryMode::Search(QuerySearch {
comparison: Comparison::GreaterThanOrEqual,
operator: Operator::default(),
}),
});
self
}
pub fn where_lt(mut self, field: &'static IndexField, value: impl ToIndexValue) -> Self {
self.filters.push(QueryFilter {
field,
value: value.to_index_value(),
mode: QueryMode::Search(QuerySearch {
comparison: Comparison::LessThan,
operator: Operator::default(),
}),
});
self
}
pub fn where_lte(mut self, field: &'static IndexField, value: impl ToIndexValue) -> Self {
self.filters.push(QueryFilter {
field,
value: value.to_index_value(),
mode: QueryMode::Search(QuerySearch {
comparison: Comparison::LessThanOrEqual,
operator: Operator::default(),
}),
});
self
}
pub fn where_contains(mut self, field: &'static IndexField, value: impl ToIndexValue) -> Self {
self.filters.push(QueryFilter {
field,
value: value.to_index_value(),
mode: QueryMode::Search(QuerySearch {
comparison: Comparison::Contains,
operator: Operator::default(),
}),
});
self
}
pub fn where_contains_all(
mut self,
field: &'static IndexField,
value: impl ToIndexValue,
) -> Self {
self.filters.push(QueryFilter {
field,
value: value.to_index_value(),
mode: QueryMode::Search(QuerySearch {
comparison: Comparison::ContainsAll,
operator: Operator::default(),
}),
});
self
}
pub fn where_begins_with(
mut self,
field: &'static IndexField,
value: impl ToIndexValue,
) -> Self {
self.filters.push(QueryFilter {
field,
value: value.to_index_value(),
mode: QueryMode::Search(QuerySearch {
comparison: Comparison::BeginsWith,
operator: Operator::default(),
}),
});
self
}
pub fn where_not_begins_with(
mut self,
field: &'static IndexField,
value: impl ToIndexValue,
) -> Self {
self.filters.push(QueryFilter {
field,
value: value.to_index_value(),
mode: QueryMode::Search(QuerySearch {
comparison: Comparison::NotBeginsWith,
operator: Operator::default(),
}),
});
self
}
pub fn where_not_contains(
mut self,
field: &'static IndexField,
value: impl ToIndexValue,
) -> Self {
self.filters.push(QueryFilter {
field,
value: value.to_index_value(),
mode: QueryMode::Search(QuerySearch {
comparison: Comparison::NotContains,
operator: Operator::default(),
}),
});
self
}
pub fn where_not_contains_all(
mut self,
field: &'static IndexField,
value: impl ToIndexValue,
) -> Self {
self.filters.push(QueryFilter {
field,
value: value.to_index_value(),
mode: QueryMode::Search(QuerySearch {
comparison: Comparison::NotContainsAll,
operator: Operator::default(),
}),
});
self
}
pub fn where_not_in(mut self, field: &'static IndexField, value: impl ToIndexValue) -> Self {
self.filters.push(QueryFilter {
field,
value: value.to_index_value(),
mode: QueryMode::Search(QuerySearch {
comparison: Comparison::NotIn,
operator: Operator::default(),
}),
});
self
}
pub fn or_eq(mut self, field: &'static IndexField, value: impl ToIndexValue) -> Self {
self.filters.push(QueryFilter {
field,
value: value.to_index_value(),
mode: QueryMode::Search(QuerySearch {
comparison: Comparison::Equal,
operator: Operator::Or,
}),
});
self
}
pub fn or_ne(mut self, field: &'static IndexField, value: impl ToIndexValue) -> Self {
self.filters.push(QueryFilter {
field,
value: value.to_index_value(),
mode: QueryMode::Search(QuerySearch {
comparison: Comparison::NotEqual,
operator: Operator::Or,
}),
});
self
}
pub fn or_gt(mut self, field: &'static IndexField, value: impl ToIndexValue) -> Self {
self.filters.push(QueryFilter {
field,
value: value.to_index_value(),
mode: QueryMode::Search(QuerySearch {
comparison: Comparison::GreaterThan,
operator: Operator::Or,
}),
});
self
}
pub fn or_gte(mut self, field: &'static IndexField, value: impl ToIndexValue) -> Self {
self.filters.push(QueryFilter {
field,
value: value.to_index_value(),
mode: QueryMode::Search(QuerySearch {
comparison: Comparison::GreaterThanOrEqual,
operator: Operator::Or,
}),
});
self
}
pub fn or_lt(mut self, field: &'static IndexField, value: impl ToIndexValue) -> Self {
self.filters.push(QueryFilter {
field,
value: value.to_index_value(),
mode: QueryMode::Search(QuerySearch {
comparison: Comparison::LessThan,
operator: Operator::Or,
}),
});
self
}
pub fn or_lte(mut self, field: &'static IndexField, value: impl ToIndexValue) -> Self {
self.filters.push(QueryFilter {
field,
value: value.to_index_value(),
mode: QueryMode::Search(QuerySearch {
comparison: Comparison::LessThanOrEqual,
operator: Operator::Or,
}),
});
self
}
pub fn or_contains(mut self, field: &'static IndexField, value: impl ToIndexValue) -> Self {
self.filters.push(QueryFilter {
field,
value: value.to_index_value(),
mode: QueryMode::Search(QuerySearch {
comparison: Comparison::Contains,
operator: Operator::Or,
}),
});
self
}
pub fn or_contains_all(mut self, field: &'static IndexField, value: impl ToIndexValue) -> Self {
self.filters.push(QueryFilter {
field,
value: value.to_index_value(),
mode: QueryMode::Search(QuerySearch {
comparison: Comparison::ContainsAll,
operator: Operator::Or,
}),
});
self
}
pub fn or_begins_with(mut self, field: &'static IndexField, value: impl ToIndexValue) -> Self {
self.filters.push(QueryFilter {
field,
value: value.to_index_value(),
mode: QueryMode::Search(QuerySearch {
comparison: Comparison::BeginsWith,
operator: Operator::Or,
}),
});
self
}
pub fn or_not_begins_with(
mut self,
field: &'static IndexField,
value: impl ToIndexValue,
) -> Self {
self.filters.push(QueryFilter {
field,
value: value.to_index_value(),
mode: QueryMode::Search(QuerySearch {
comparison: Comparison::NotBeginsWith,
operator: Operator::Or,
}),
});
self
}
pub fn or_not_contains(mut self, field: &'static IndexField, value: impl ToIndexValue) -> Self {
self.filters.push(QueryFilter {
field,
value: value.to_index_value(),
mode: QueryMode::Search(QuerySearch {
comparison: Comparison::NotContains,
operator: Operator::Or,
}),
});
self
}
pub fn or_not_contains_all(
mut self,
field: &'static IndexField,
value: impl ToIndexValue,
) -> Self {
self.filters.push(QueryFilter {
field,
value: value.to_index_value(),
mode: QueryMode::Search(QuerySearch {
comparison: Comparison::NotContainsAll,
operator: Operator::Or,
}),
});
self
}
pub fn or_not_in(mut self, field: &'static IndexField, value: impl ToIndexValue) -> Self {
self.filters.push(QueryFilter {
field,
value: value.to_index_value(),
mode: QueryMode::Search(QuerySearch {
comparison: Comparison::NotIn,
operator: Operator::Or,
}),
});
self
}
pub fn edge_filter(
mut self,
field: &'static IndexField,
value: impl ToIndexValue,
mode: QueryMode,
) -> Self {
self.edge_filters.push(QueryFilter {
field,
value: value.to_index_value(),
mode,
});
self
}
pub fn edge_eq(mut self, field: &'static IndexField, value: impl ToIndexValue) -> Self {
self.edge_filters.push(QueryFilter {
field,
value: value.to_index_value(),
mode: QueryMode::Search(QuerySearch {
comparison: Comparison::Equal,
operator: Operator::default(),
}),
});
self
}
pub fn edge_ne(mut self, field: &'static IndexField, value: impl ToIndexValue) -> Self {
self.edge_filters.push(QueryFilter {
field,
value: value.to_index_value(),
mode: QueryMode::Search(QuerySearch {
comparison: Comparison::NotEqual,
operator: Operator::default(),
}),
});
self
}
pub fn edge_gt(mut self, field: &'static IndexField, value: impl ToIndexValue) -> Self {
self.edge_filters.push(QueryFilter {
field,
value: value.to_index_value(),
mode: QueryMode::Search(QuerySearch {
comparison: Comparison::GreaterThan,
operator: Operator::default(),
}),
});
self
}
pub fn edge_gte(mut self, field: &'static IndexField, value: impl ToIndexValue) -> Self {
self.edge_filters.push(QueryFilter {
field,
value: value.to_index_value(),
mode: QueryMode::Search(QuerySearch {
comparison: Comparison::GreaterThanOrEqual,
operator: Operator::default(),
}),
});
self
}
pub fn edge_lt(mut self, field: &'static IndexField, value: impl ToIndexValue) -> Self {
self.edge_filters.push(QueryFilter {
field,
value: value.to_index_value(),
mode: QueryMode::Search(QuerySearch {
comparison: Comparison::LessThan,
operator: Operator::default(),
}),
});
self
}
pub fn edge_lte(mut self, field: &'static IndexField, value: impl ToIndexValue) -> Self {
self.edge_filters.push(QueryFilter {
field,
value: value.to_index_value(),
mode: QueryMode::Search(QuerySearch {
comparison: Comparison::LessThanOrEqual,
operator: Operator::default(),
}),
});
self
}
pub fn edge_contains(mut self, field: &'static IndexField, value: impl ToIndexValue) -> Self {
self.edge_filters.push(QueryFilter {
field,
value: value.to_index_value(),
mode: QueryMode::Search(QuerySearch {
comparison: Comparison::Contains,
operator: Operator::default(),
}),
});
self
}
pub fn edge_contains_all(
mut self,
field: &'static IndexField,
value: impl ToIndexValue,
) -> Self {
self.edge_filters.push(QueryFilter {
field,
value: value.to_index_value(),
mode: QueryMode::Search(QuerySearch {
comparison: Comparison::ContainsAll,
operator: Operator::default(),
}),
});
self
}
pub fn edge_begins_with(
mut self,
field: &'static IndexField,
value: impl ToIndexValue,
) -> Self {
self.edge_filters.push(QueryFilter {
field,
value: value.to_index_value(),
mode: QueryMode::Search(QuerySearch {
comparison: Comparison::BeginsWith,
operator: Operator::default(),
}),
});
self
}
pub fn edge_not_begins_with(
mut self,
field: &'static IndexField,
value: impl ToIndexValue,
) -> Self {
self.edge_filters.push(QueryFilter {
field,
value: value.to_index_value(),
mode: QueryMode::Search(QuerySearch {
comparison: Comparison::NotBeginsWith,
operator: Operator::default(),
}),
});
self
}
pub fn edge_not_contains(
mut self,
field: &'static IndexField,
value: impl ToIndexValue,
) -> Self {
self.edge_filters.push(QueryFilter {
field,
value: value.to_index_value(),
mode: QueryMode::Search(QuerySearch {
comparison: Comparison::NotContains,
operator: Operator::default(),
}),
});
self
}
pub fn edge_not_contains_all(
mut self,
field: &'static IndexField,
value: impl ToIndexValue,
) -> Self {
self.edge_filters.push(QueryFilter {
field,
value: value.to_index_value(),
mode: QueryMode::Search(QuerySearch {
comparison: Comparison::NotContainsAll,
operator: Operator::default(),
}),
});
self
}
pub fn edge_not_in(mut self, field: &'static IndexField, value: impl ToIndexValue) -> Self {
self.edge_filters.push(QueryFilter {
field,
value: value.to_index_value(),
mode: QueryMode::Search(QuerySearch {
comparison: Comparison::NotIn,
operator: Operator::default(),
}),
});
self
}
pub fn edge_or_eq(mut self, field: &'static IndexField, value: impl ToIndexValue) -> Self {
self.edge_filters.push(QueryFilter {
field,
value: value.to_index_value(),
mode: QueryMode::Search(QuerySearch {
comparison: Comparison::Equal,
operator: Operator::Or,
}),
});
self
}
pub fn edge_or_ne(mut self, field: &'static IndexField, value: impl ToIndexValue) -> Self {
self.edge_filters.push(QueryFilter {
field,
value: value.to_index_value(),
mode: QueryMode::Search(QuerySearch {
comparison: Comparison::NotEqual,
operator: Operator::Or,
}),
});
self
}
pub fn edge_or_gt(mut self, field: &'static IndexField, value: impl ToIndexValue) -> Self {
self.edge_filters.push(QueryFilter {
field,
value: value.to_index_value(),
mode: QueryMode::Search(QuerySearch {
comparison: Comparison::GreaterThan,
operator: Operator::Or,
}),
});
self
}
pub fn edge_or_gte(mut self, field: &'static IndexField, value: impl ToIndexValue) -> Self {
self.edge_filters.push(QueryFilter {
field,
value: value.to_index_value(),
mode: QueryMode::Search(QuerySearch {
comparison: Comparison::GreaterThanOrEqual,
operator: Operator::Or,
}),
});
self
}
pub fn edge_or_lt(mut self, field: &'static IndexField, value: impl ToIndexValue) -> Self {
self.edge_filters.push(QueryFilter {
field,
value: value.to_index_value(),
mode: QueryMode::Search(QuerySearch {
comparison: Comparison::LessThan,
operator: Operator::Or,
}),
});
self
}
pub fn edge_or_lte(mut self, field: &'static IndexField, value: impl ToIndexValue) -> Self {
self.edge_filters.push(QueryFilter {
field,
value: value.to_index_value(),
mode: QueryMode::Search(QuerySearch {
comparison: Comparison::LessThanOrEqual,
operator: Operator::Or,
}),
});
self
}
pub fn edge_or_contains(
mut self,
field: &'static IndexField,
value: impl ToIndexValue,
) -> Self {
self.edge_filters.push(QueryFilter {
field,
value: value.to_index_value(),
mode: QueryMode::Search(QuerySearch {
comparison: Comparison::Contains,
operator: Operator::Or,
}),
});
self
}
pub fn edge_or_contains_all(
mut self,
field: &'static IndexField,
value: impl ToIndexValue,
) -> Self {
self.edge_filters.push(QueryFilter {
field,
value: value.to_index_value(),
mode: QueryMode::Search(QuerySearch {
comparison: Comparison::ContainsAll,
operator: Operator::Or,
}),
});
self
}
pub fn edge_or_begins_with(
mut self,
field: &'static IndexField,
value: impl ToIndexValue,
) -> Self {
self.edge_filters.push(QueryFilter {
field,
value: value.to_index_value(),
mode: QueryMode::Search(QuerySearch {
comparison: Comparison::BeginsWith,
operator: Operator::Or,
}),
});
self
}
pub fn edge_or_not_begins_with(
mut self,
field: &'static IndexField,
value: impl ToIndexValue,
) -> Self {
self.edge_filters.push(QueryFilter {
field,
value: value.to_index_value(),
mode: QueryMode::Search(QuerySearch {
comparison: Comparison::NotBeginsWith,
operator: Operator::Or,
}),
});
self
}
pub fn edge_or_not_contains(
mut self,
field: &'static IndexField,
value: impl ToIndexValue,
) -> Self {
self.edge_filters.push(QueryFilter {
field,
value: value.to_index_value(),
mode: QueryMode::Search(QuerySearch {
comparison: Comparison::NotContains,
operator: Operator::Or,
}),
});
self
}
pub fn edge_or_not_contains_all(
mut self,
field: &'static IndexField,
value: impl ToIndexValue,
) -> Self {
self.edge_filters.push(QueryFilter {
field,
value: value.to_index_value(),
mode: QueryMode::Search(QuerySearch {
comparison: Comparison::NotContainsAll,
operator: Operator::Or,
}),
});
self
}
pub fn edge_or_not_in(mut self, field: &'static IndexField, value: impl ToIndexValue) -> Self {
self.edge_filters.push(QueryFilter {
field,
value: value.to_index_value(),
mode: QueryMode::Search(QuerySearch {
comparison: Comparison::NotIn,
operator: Operator::Or,
}),
});
self
}
pub fn sort_asc(mut self, field: &'static IndexField) -> Self {
self.filters.push(QueryFilter {
field,
value: field.name.to_index_value(),
mode: QueryMode::Sort(QuerySort { ascending: true }),
});
self
}
pub fn sort_desc(mut self, field: &'static IndexField) -> Self {
self.filters.push(QueryFilter {
field,
value: field.name.to_index_value(),
mode: QueryMode::Sort(QuerySort { ascending: false }),
});
self
}
pub fn edge_sort_asc(mut self, field: &'static IndexField) -> Self {
self.edge_filters.push(QueryFilter {
field,
value: field.name.to_index_value(),
mode: QueryMode::Sort(QuerySort { ascending: true }),
});
self
}
pub fn edge_sort_desc(mut self, field: &'static IndexField) -> Self {
self.edge_filters.push(QueryFilter {
field,
value: field.name.to_index_value(),
mode: QueryMode::Sort(QuerySort { ascending: false }),
});
self
}
pub fn sort_random(mut self) -> Self {
self.filters.push(QueryFilter::random_sort());
self
}
pub fn edge_sort_random(mut self) -> Self {
self.edge_filters.push(QueryFilter::random_sort());
self
}
pub fn with_limit(mut self, limit: u32) -> Self {
self.limit = Some(limit);
self
}
pub fn with_cursor(mut self, cursor: Uuid) -> Self {
self.cursor = Some(Cursor { last_id: cursor });
self
}
pub async fn collect(&self) -> Result<Vec<O>, Error> {
let mut edge_query = EdgeQuery::default();
edge_query.filters = self.edge_filters.clone();
if let Some(limit) = self.limit {
edge_query.limit = Some(limit);
}
if let Some(cusor) = self.cursor {
edge_query.cursor = Some(cusor);
}
let objects = self
.adapter
.fetch_object_from_edge_traversal_internal(
E::TYPE,
O::TYPE,
self.owner,
&self.filters,
edge_query,
)
.await?;
objects
.into_iter()
.map(|or| or.to_object())
.collect::<Result<Vec<O>, Error>>()
}
pub async fn collect_reverse(&self) -> Result<Vec<O>, Error> {
let mut edge_query = EdgeQuery::default();
edge_query.filters = self.edge_filters.clone();
if let Some(limit) = self.limit {
edge_query.limit = Some(limit);
}
if let Some(cusor) = self.cursor {
edge_query.cursor = Some(cusor);
}
let objects = self
.adapter
.fetch_object_from_edge_reverse_traversal_internal(
E::TYPE,
O::TYPE,
self.owner,
&self.filters,
edge_query,
)
.await?;
objects
.into_iter()
.map(|or| or.to_object())
.collect::<Result<Vec<O>, Error>>()
}
pub async fn collect_edges(&self) -> Result<Vec<E>, Error> {
let mut edge_query = EdgeQuery::default();
edge_query.filters = self.edge_filters.clone();
if let Some(limit) = self.limit {
edge_query.limit = Some(limit);
}
if let Some(offset) = self.cursor {
edge_query.cursor = Some(offset);
}
let edge_records = self
.adapter
.query_edges(E::TYPE, self.owner, edge_query)
.await?;
edge_records
.into_iter()
.map(|r| r.to_edge())
.collect::<Result<Vec<E>, Error>>()
}
pub async fn collect_reverse_edges(&self) -> Result<Vec<E>, Error> {
let mut edge_query = EdgeQuery::default();
edge_query.filters = self.edge_filters.clone();
if let Some(limit) = self.limit {
edge_query.limit = Some(limit);
}
if let Some(offset) = self.cursor {
edge_query.cursor = Some(offset);
}
let edge_records = self
.adapter
.query_reverse_edges(E::TYPE, self.owner, edge_query)
.await?;
edge_records
.into_iter()
.map(|r| r.to_edge())
.collect::<Result<Vec<E>, Error>>()
}
pub async fn collect_with_target(&self) -> Result<Vec<ObjectEdge<E, O>>, Error> {
let mut edge_query = EdgeQuery::default();
edge_query.filters = self.edge_filters.clone();
if let Some(limit) = self.limit {
edge_query.limit = Some(limit);
}
if let Some(offset) = self.cursor {
edge_query.cursor = Some(offset);
}
self.adapter
.query_edges_with_targets(E::TYPE, O::TYPE, self.owner, &self.filters, edge_query)
.await?
.into_iter()
.map(|(er, or)| Ok(ObjectEdge::new(er.to_edge::<E>()?, or.to_object::<O>()?)))
.collect()
}
pub async fn collect_reverse_with_target(&self) -> Result<Vec<ObjectEdge<E, O>>, Error> {
let mut edge_query = EdgeQuery::default();
edge_query.filters = self.edge_filters.clone();
if let Some(limit) = self.limit {
edge_query.limit = Some(limit);
}
if let Some(offset) = self.cursor {
edge_query.cursor = Some(offset);
}
self.adapter
.query_reverse_edges_with_sources(
E::TYPE,
O::TYPE,
self.owner,
&self.filters,
edge_query,
)
.await?
.into_iter()
.map(|(er, or)| Ok(ObjectEdge::new(er.to_edge::<E>()?, or.to_object::<O>()?)))
.collect()
}
pub fn paginate(mut self, cursor: Option<impl Into<Cursor>>) -> Self {
if let Some(cursor) = cursor {
let _cursor: Cursor = cursor.into();
self.cursor = Some(_cursor);
}
self
}
fn build_edge_query(&self) -> EdgeQuery {
let mut eq = EdgeQuery::default();
eq.filters = self.edge_filters.clone();
if let Some(limit) = self.limit {
eq.limit = Some(limit);
}
if let Some(cursor) = self.cursor {
eq.cursor = Some(cursor);
}
eq
}
pub async fn collect_both(&self) -> Result<(Vec<O>, Vec<O>), Error> {
let edge_query = self.build_edge_query();
let (fwd, rev) = self
.adapter
.query_edges_both_directions_with_objects(
E::TYPE,
O::TYPE,
self.owner,
&self.filters,
edge_query,
)
.await?;
let fwd = fwd
.into_iter()
.map(|(_, or)| or.to_object::<O>())
.collect::<Result<Vec<O>, Error>>()?;
let rev = rev
.into_iter()
.map(|(_, or)| or.to_object::<O>())
.collect::<Result<Vec<O>, Error>>()?;
Ok((fwd, rev))
}
pub async fn collect_both_with_target(
&self,
) -> Result<(Vec<ObjectEdge<E, O>>, Vec<ObjectEdge<E, O>>), Error> {
let edge_query = self.build_edge_query();
let (fwd, rev) = self
.adapter
.query_edges_both_directions_with_objects(
E::TYPE,
O::TYPE,
self.owner,
&self.filters,
edge_query,
)
.await?;
let fwd = fwd
.into_iter()
.map(|(er, or)| Ok(ObjectEdge::new(er.to_edge::<E>()?, or.to_object::<O>()?)))
.collect::<Result<Vec<_>, Error>>()?;
let rev = rev
.into_iter()
.map(|(er, or)| Ok(ObjectEdge::new(er.to_edge::<E>()?, or.to_object::<O>()?)))
.collect::<Result<Vec<_>, Error>>()?;
Ok((fwd, rev))
}
pub async fn collect_both_edges(&self) -> Result<(Vec<E>, Vec<E>), Error> {
let edge_query = self.build_edge_query();
let (fwd, rev) = self
.adapter
.query_edges_both_directions(E::TYPE, self.owner, edge_query)
.await?;
let fwd = fwd
.into_iter()
.map(|r| r.to_edge::<E>())
.collect::<Result<Vec<E>, Error>>()?;
let rev = rev
.into_iter()
.map(|r| r.to_edge::<E>())
.collect::<Result<Vec<E>, Error>>()?;
Ok((fwd, rev))
}
}
pub struct MultiPreloadContext<'a, P: Object> {
adapter: &'a dyn Adapter,
query: Query,
_marker: std::marker::PhantomData<P>,
}
impl<'a, P: Object> MultiPreloadContext<'a, P> {
pub(crate) fn new(adapter: &'a dyn Adapter, query: Query) -> Self {
Self {
adapter,
query,
_marker: std::marker::PhantomData,
}
}
pub fn edge<E: Edge, C: Object>(self) -> MultiEdgeContext<'a, E, P, C> {
MultiEdgeContext::new(self.adapter, self.query)
}
pub fn preload<C: Object>(self) -> MultiOwnedContext<'a, P, C> {
MultiOwnedContext::new(self.adapter, self.query)
}
}
pub struct MultiEdgeContext<'a, E: Edge, P: Object, C: Object> {
adapter: &'a dyn Adapter,
parent_query: Query,
edge_query: EdgeQuery,
obj_filters: Vec<QueryFilter>,
_marker: std::marker::PhantomData<(E, P, C)>,
}
impl<'a, E: Edge, P: Object, C: Object> MultiEdgeContext<'a, E, P, C> {
pub(crate) fn new(adapter: &'a dyn Adapter, parent_query: Query) -> Self {
Self {
adapter,
parent_query,
edge_query: EdgeQuery::default(),
obj_filters: Vec::new(),
_marker: std::marker::PhantomData,
}
}
pub fn with_edge_query(mut self, edge_query: EdgeQuery) -> Self {
self.edge_query = edge_query;
self
}
pub fn obj_eq(mut self, field: &'static IndexField, value: impl ToIndexValue) -> Self {
self.obj_filters.push(QueryFilter {
field,
value: value.to_index_value(),
mode: QueryMode::Search(QuerySearch {
comparison: Comparison::Equal,
operator: Operator::default(),
}),
});
self
}
pub async fn collect(self) -> Result<Vec<(P, Vec<C>)>, Error> {
let parents = self
.adapter
.query_objects(P::TYPE, self.parent_query)
.await?;
if parents.is_empty() {
return Ok(Vec::new());
}
let parent_ids: Vec<Uuid> = parents.iter().map(|p| p.id).collect();
let pairs = self
.adapter
.query_edges_with_targets_batch(
E::TYPE,
C::TYPE,
&parent_ids,
&self.obj_filters,
self.edge_query,
)
.await?;
let mut grouped: std::collections::HashMap<Uuid, Vec<C>> = std::collections::HashMap::new();
for (er, or) in pairs {
grouped
.entry(er.from)
.or_default()
.push(or.to_object::<C>()?);
}
parents
.into_iter()
.map(|pr| {
let p = pr.to_object::<P>()?;
let children = grouped.remove(&p.meta().id()).unwrap_or_default();
Ok((p, children))
})
.collect()
}
pub async fn collect_reverse(self) -> Result<Vec<(P, Vec<C>)>, Error> {
let parents = self
.adapter
.query_objects(P::TYPE, self.parent_query)
.await?;
if parents.is_empty() {
return Ok(Vec::new());
}
let parent_ids: Vec<Uuid> = parents.iter().map(|p| p.id).collect();
let pairs = self
.adapter
.query_reverse_edges_with_sources_batch(
E::TYPE,
C::TYPE,
&parent_ids,
&self.obj_filters,
self.edge_query,
)
.await?;
let mut grouped: std::collections::HashMap<Uuid, Vec<C>> = std::collections::HashMap::new();
for (er, or) in pairs {
grouped.entry(er.to).or_default().push(or.to_object::<C>()?);
}
parents
.into_iter()
.map(|pr| {
let p = pr.to_object::<P>()?;
let children = grouped.remove(&p.meta().id()).unwrap_or_default();
Ok((p, children))
})
.collect()
}
pub async fn collect_edges(self) -> Result<Vec<(P, Vec<E>)>, Error> {
let parents = self
.adapter
.query_objects(P::TYPE, self.parent_query)
.await?;
if parents.is_empty() {
return Ok(Vec::new());
}
let parent_ids: Vec<Uuid> = parents.iter().map(|p| p.id).collect();
let edge_records = self
.adapter
.query_edges_batch(E::TYPE, &parent_ids, self.edge_query)
.await?;
let mut grouped: std::collections::HashMap<Uuid, Vec<E>> = std::collections::HashMap::new();
for er in edge_records {
grouped.entry(er.from).or_default().push(er.to_edge::<E>()?);
}
parents
.into_iter()
.map(|pr| {
let p = pr.to_object::<P>()?;
let edges = grouped.remove(&p.meta().id()).unwrap_or_default();
Ok((p, edges))
})
.collect()
}
pub async fn collect_reverse_edges(self) -> Result<Vec<(P, Vec<E>)>, Error> {
let parents = self
.adapter
.query_objects(P::TYPE, self.parent_query)
.await?;
if parents.is_empty() {
return Ok(Vec::new());
}
let parent_ids: Vec<Uuid> = parents.iter().map(|p| p.id).collect();
let edge_records = self
.adapter
.query_reverse_edges_batch(E::TYPE, &parent_ids, self.edge_query)
.await?;
let mut grouped: std::collections::HashMap<Uuid, Vec<E>> = std::collections::HashMap::new();
for er in edge_records {
grouped.entry(er.to).or_default().push(er.to_edge::<E>()?);
}
parents
.into_iter()
.map(|pr| {
let p = pr.to_object::<P>()?;
let edges = grouped.remove(&p.meta().id()).unwrap_or_default();
Ok((p, edges))
})
.collect()
}
pub async fn collect_with_target(self) -> Result<Vec<(P, Vec<ObjectEdge<E, C>>)>, Error> {
let parents = self
.adapter
.query_objects(P::TYPE, self.parent_query)
.await?;
if parents.is_empty() {
return Ok(Vec::new());
}
let parent_ids: Vec<Uuid> = parents.iter().map(|p| p.id).collect();
let pairs = self
.adapter
.query_edges_with_targets_batch(
E::TYPE,
C::TYPE,
&parent_ids,
&self.obj_filters,
self.edge_query,
)
.await?;
let mut grouped: std::collections::HashMap<Uuid, Vec<ObjectEdge<E, C>>> =
std::collections::HashMap::new();
for (er, or) in pairs {
let from = er.from;
grouped
.entry(from)
.or_default()
.push(ObjectEdge::new(er.to_edge::<E>()?, or.to_object::<C>()?));
}
parents
.into_iter()
.map(|pr| {
let p = pr.to_object::<P>()?;
let items = grouped.remove(&p.meta().id()).unwrap_or_default();
Ok((p, items))
})
.collect()
}
pub async fn collect_reverse_with_target(
self,
) -> Result<Vec<(P, Vec<ObjectEdge<E, C>>)>, Error> {
let parents = self
.adapter
.query_objects(P::TYPE, self.parent_query)
.await?;
if parents.is_empty() {
return Ok(Vec::new());
}
let parent_ids: Vec<Uuid> = parents.iter().map(|p| p.id).collect();
let pairs = self
.adapter
.query_reverse_edges_with_sources_batch(
E::TYPE,
C::TYPE,
&parent_ids,
&self.obj_filters,
self.edge_query,
)
.await?;
let mut grouped: std::collections::HashMap<Uuid, Vec<ObjectEdge<E, C>>> =
std::collections::HashMap::new();
for (er, or) in pairs {
let to = er.to;
grouped
.entry(to)
.or_default()
.push(ObjectEdge::new(er.to_edge::<E>()?, or.to_object::<C>()?));
}
parents
.into_iter()
.map(|pr| {
let p = pr.to_object::<P>()?;
let items = grouped.remove(&p.meta().id()).unwrap_or_default();
Ok((p, items))
})
.collect()
}
pub async fn count(self) -> Result<Vec<(P, u64)>, Error> {
let parents = self
.adapter
.query_objects(P::TYPE, self.parent_query)
.await?;
if parents.is_empty() {
return Ok(Vec::new());
}
let parent_ids: Vec<Uuid> = parents.iter().map(|p| p.id).collect();
let counts = self
.adapter
.count_edges_batch(E::TYPE, &parent_ids, self.edge_query)
.await?;
let count_map: std::collections::HashMap<Uuid, u64> = counts.into_iter().collect();
parents
.into_iter()
.map(|pr| {
let p = pr.to_object::<P>()?;
let n = count_map.get(&p.meta().id()).copied().unwrap_or(0);
Ok((p, n))
})
.collect()
}
pub async fn count_reverse(self) -> Result<Vec<(P, u64)>, Error> {
let parents = self
.adapter
.query_objects(P::TYPE, self.parent_query)
.await?;
if parents.is_empty() {
return Ok(Vec::new());
}
let parent_ids: Vec<Uuid> = parents.iter().map(|p| p.id).collect();
let counts = self
.adapter
.count_reverse_edges_batch(E::TYPE, &parent_ids, self.edge_query)
.await?;
let count_map: std::collections::HashMap<Uuid, u64> = counts.into_iter().collect();
parents
.into_iter()
.map(|pr| {
let p = pr.to_object::<P>()?;
let n = count_map.get(&p.meta().id()).copied().unwrap_or(0);
Ok((p, n))
})
.collect()
}
}
pub struct OwnedContext<'a, P: Object, C: Object> {
adapter: &'a dyn Adapter,
owner: Uuid,
_parent_marker: std::marker::PhantomData<P>,
_children_marker: std::marker::PhantomData<C>,
}
impl<'a, P: Object, C: Object> OwnedContext<'a, P, C> {
pub(crate) fn new(adapter: &'a dyn Adapter, owner: Uuid) -> Self {
Self {
adapter,
owner,
_parent_marker: std::marker::PhantomData,
_children_marker: std::marker::PhantomData,
}
}
pub async fn collect(self) -> Result<Option<(P, Vec<C>)>, Error> {
let parent = self.adapter.fetch_object(P::TYPE, self.owner).await?;
let Some(parent) = parent else {
return Ok(None);
};
let parent = parent.to_object()?;
let records = self
.adapter
.fetch_owned_objects(C::TYPE, self.owner)
.await?;
let chilren = records
.into_iter()
.map(|or| or.to_object())
.collect::<Result<Vec<C>, Error>>()?;
Ok(Some((parent, chilren)))
}
}
pub struct MultiOwnedContext<'a, P: Object, C: Object> {
adapter: &'a dyn Adapter,
parent_query: Query,
_marker: std::marker::PhantomData<(P, C)>,
}
impl<'a, P: Object, C: Object> MultiOwnedContext<'a, P, C> {
pub(crate) fn new(adapter: &'a dyn Adapter, parent_query: Query) -> Self {
Self {
adapter,
parent_query,
_marker: std::marker::PhantomData,
}
}
pub async fn collect(self) -> Result<Vec<(P, Vec<C>)>, Error> {
let parents = self
.adapter
.query_objects(P::TYPE, self.parent_query)
.await?;
if parents.is_empty() {
return Ok(Vec::new());
}
let owner_ids: Vec<Uuid> = parents.iter().map(|p| p.id).collect();
let children = self
.adapter
.fetch_owned_objects_batch(C::TYPE, &owner_ids)
.await?;
let mut grouped: std::collections::HashMap<Uuid, Vec<C>> = std::collections::HashMap::new();
for cr in children {
let owner = cr.owner;
grouped.entry(owner).or_default().push(cr.to_object::<C>()?);
}
parents
.into_iter()
.map(|pr| {
let p = pr.to_object::<P>()?;
let children = grouped.remove(&p.meta().id()).unwrap_or_default();
Ok((p, children))
})
.collect()
}
}