graphrecords-query 0.5.0

High-performance graph-based data records
Documentation
use graphrecords_core::{
    GraphRecord,
    graphrecord::{GraphRecordAttribute, Group},
};
use graphrecords_utils::aliases::{GrHashMap, GrHashSet};
use std::{
    any::{Any, TypeId},
    cell::RefCell,
    hash::Hash,
};

pub trait Statistic: 'static {
    type Key: Hash + Eq + Clone + 'static;
    type Value: Clone + 'static;

    fn compute(graphrecord: &GraphRecord, key: &Self::Key) -> Self::Value;
}

pub struct Stats<'a> {
    graphrecord: &'a GraphRecord,
    cache: RefCell<GrHashMap<TypeId, Box<dyn Any>>>,
}

impl<'a> Stats<'a> {
    #[must_use]
    pub fn new(graphrecord: &'a GraphRecord) -> Self {
        Self {
            graphrecord,
            cache: RefCell::new(GrHashMap::default()),
        }
    }

    pub fn get<S: Statistic>(&self, key: &S::Key) -> S::Value {
        let mut cache = self.cache.borrow_mut();

        #[allow(clippy::missing_panics_doc)]
        let map = cache
            .entry(TypeId::of::<S>())
            .or_insert_with(|| Box::new(GrHashMap::<S::Key, S::Value>::default()))
            .downcast_mut::<GrHashMap<S::Key, S::Value>>()
            .expect("Statistic cache type must match its TypeId key");

        map.entry(key.clone())
            .or_insert_with(|| S::compute(self.graphrecord, key))
            .clone()
    }
}

pub struct NodeGroupSize;

impl Statistic for NodeGroupSize {
    type Key = Group;
    type Value = usize;

    fn compute(graphrecord: &GraphRecord, key: &Self::Key) -> Self::Value {
        graphrecord.nodes_in_group(key).map_or(0, Iterator::count)
    }
}

pub struct EdgeGroupSize;

impl Statistic for EdgeGroupSize {
    type Key = Group;
    type Value = usize;

    fn compute(graphrecord: &GraphRecord, key: &Self::Key) -> Self::Value {
        graphrecord.edges_in_group(key).map_or(0, Iterator::count)
    }
}

pub struct NodeAttributeCardinality;

impl Statistic for NodeAttributeCardinality {
    type Key = GraphRecordAttribute;
    type Value = usize;

    fn compute(graphrecord: &GraphRecord, key: &Self::Key) -> Self::Value {
        graphrecord
            .node_indices()
            .filter_map(|node_index| {
                graphrecord
                    .node_attributes(node_index)
                    .expect("Node must exist")
                    .get(key)
                    .cloned()
            })
            .collect::<GrHashSet<_>>()
            .len()
    }
}

pub struct EdgeAttributeCardinality;

impl Statistic for EdgeAttributeCardinality {
    type Key = GraphRecordAttribute;
    type Value = usize;

    fn compute(graphrecord: &GraphRecord, key: &Self::Key) -> Self::Value {
        graphrecord
            .edge_indices()
            .filter_map(|edge_index| {
                graphrecord
                    .edge_attributes(edge_index)
                    .expect("Edge must exist")
                    .get(key)
                    .cloned()
            })
            .collect::<GrHashSet<_>>()
            .len()
    }
}

#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
pub enum CountKind {
    Nodes,
    Edges,
}

pub struct Count;

impl Statistic for Count {
    type Key = CountKind;
    type Value = usize;

    fn compute(graphrecord: &GraphRecord, key: &Self::Key) -> Self::Value {
        match key {
            CountKind::Nodes => graphrecord.node_indices().count(),
            CountKind::Edges => graphrecord.edge_indices().count(),
        }
    }
}