use std::collections::HashMap;
use crate::graph::graph_classifier::{canonical_classifier, GraphValue};
use crate::graph::graph_query::GraphQuery;
use crate::graph::graph_view::GraphView;
use crate::pattern::Pattern;
use crate::pattern_graph::PatternGraph;
use crate::subject::{Subject, Symbol};
pub struct StandardGraph {
inner: PatternGraph<(), Subject>,
}
impl StandardGraph {
pub fn new() -> Self {
StandardGraph {
inner: PatternGraph::empty(),
}
}
pub fn add_node(&mut self, subject: Subject) -> &mut Self {
let id = subject.identity.clone();
let pattern = Pattern::point(subject);
self.inner.pg_nodes.insert(id, pattern);
self
}
pub fn add_relationship(
&mut self,
subject: Subject,
source: &Subject,
target: &Subject,
) -> &mut Self {
let source_pattern = self.get_or_create_placeholder_node(&source.identity);
let target_pattern = self.get_or_create_placeholder_node(&target.identity);
let id = subject.identity.clone();
let pattern = Pattern::pattern(subject, vec![source_pattern, target_pattern]);
self.inner.pg_relationships.insert(id, pattern);
self
}
pub fn add_walk(&mut self, subject: Subject, relationships: &[Subject]) -> &mut Self {
let rel_patterns: Vec<Pattern<Subject>> = relationships
.iter()
.map(|rel| self.get_or_create_placeholder_relationship(&rel.identity))
.collect();
let id = subject.identity.clone();
let pattern = Pattern::pattern(subject, rel_patterns);
self.inner.pg_walks.insert(id, pattern);
self
}
pub fn add_annotation(&mut self, subject: Subject, element: &Subject) -> &mut Self {
let element_id = &element.identity;
let element_pattern = if let Some(existing) = self.find_element(element_id) {
existing
} else {
let placeholder = Self::make_placeholder_node(element_id);
self.inner
.pg_nodes
.insert(element_id.clone(), placeholder.clone());
placeholder
};
let id = subject.identity.clone();
let pattern = Pattern::pattern(subject, vec![element_pattern]);
self.inner.pg_annotations.insert(id, pattern);
self
}
pub fn add_pattern(&mut self, pattern: Pattern<Subject>) -> &mut Self {
let classifier = canonical_classifier();
let policy = pattern_merge_policy();
self.inner = crate::pattern_graph::merge_with_policy(
&classifier,
&policy,
pattern,
std::mem::replace(&mut self.inner, PatternGraph::empty()),
);
self
}
pub fn add_patterns(
&mut self,
patterns: impl IntoIterator<Item = Pattern<Subject>>,
) -> &mut Self {
let classifier = canonical_classifier();
let policy = pattern_merge_policy();
let mut graph = std::mem::replace(&mut self.inner, PatternGraph::empty());
for pattern in patterns {
graph = crate::pattern_graph::merge_with_policy(&classifier, &policy, pattern, graph);
}
self.inner = graph;
self
}
pub fn from_patterns(patterns: impl IntoIterator<Item = Pattern<Subject>>) -> Self {
let classifier = canonical_classifier();
let policy = pattern_merge_policy();
let inner = crate::pattern_graph::from_patterns_with_policy(&classifier, &policy, patterns);
StandardGraph { inner }
}
pub fn from_pattern_graph(graph: PatternGraph<(), Subject>) -> Self {
StandardGraph { inner: graph }
}
pub fn node(&self, id: &Symbol) -> Option<&Pattern<Subject>> {
self.inner.pg_nodes.get(id)
}
pub fn relationship(&self, id: &Symbol) -> Option<&Pattern<Subject>> {
self.inner.pg_relationships.get(id)
}
pub fn walk(&self, id: &Symbol) -> Option<&Pattern<Subject>> {
self.inner.pg_walks.get(id)
}
pub fn annotation(&self, id: &Symbol) -> Option<&Pattern<Subject>> {
self.inner.pg_annotations.get(id)
}
pub fn node_count(&self) -> usize {
self.inner.pg_nodes.len()
}
pub fn relationship_count(&self) -> usize {
self.inner.pg_relationships.len()
}
pub fn walk_count(&self) -> usize {
self.inner.pg_walks.len()
}
pub fn annotation_count(&self) -> usize {
self.inner.pg_annotations.len()
}
pub fn is_empty(&self) -> bool {
self.inner.pg_nodes.is_empty()
&& self.inner.pg_relationships.is_empty()
&& self.inner.pg_walks.is_empty()
&& self.inner.pg_annotations.is_empty()
&& self.inner.pg_other.is_empty()
&& self.inner.pg_conflicts.is_empty()
}
pub fn has_conflicts(&self) -> bool {
!self.inner.pg_conflicts.is_empty()
}
pub fn conflicts(&self) -> &HashMap<Symbol, Vec<Pattern<Subject>>> {
&self.inner.pg_conflicts
}
pub fn other(&self) -> &HashMap<Symbol, ((), Pattern<Subject>)> {
&self.inner.pg_other
}
pub fn nodes(&self) -> impl Iterator<Item = (&Symbol, &Pattern<Subject>)> {
self.inner.pg_nodes.iter()
}
pub fn relationships(&self) -> impl Iterator<Item = (&Symbol, &Pattern<Subject>)> {
self.inner.pg_relationships.iter()
}
pub fn walks(&self) -> impl Iterator<Item = (&Symbol, &Pattern<Subject>)> {
self.inner.pg_walks.iter()
}
pub fn annotations(&self) -> impl Iterator<Item = (&Symbol, &Pattern<Subject>)> {
self.inner.pg_annotations.iter()
}
pub fn source(&self, rel_id: &Symbol) -> Option<&Pattern<Subject>> {
self.inner
.pg_relationships
.get(rel_id)
.and_then(|rel| rel.elements.first())
}
pub fn target(&self, rel_id: &Symbol) -> Option<&Pattern<Subject>> {
self.inner
.pg_relationships
.get(rel_id)
.and_then(|rel| rel.elements.get(1))
}
pub fn neighbors(&self, node_id: &Symbol) -> Vec<&Pattern<Subject>> {
let mut result = Vec::new();
for rel in self.inner.pg_relationships.values() {
if rel.elements.len() == 2 {
let src_id = rel.elements[0].value.identify();
let tgt_id = rel.elements[1].value.identify();
if src_id == node_id {
result.push(&rel.elements[1]);
} else if tgt_id == node_id {
result.push(&rel.elements[0]);
}
}
}
result
}
pub fn degree(&self, node_id: &Symbol) -> usize {
self.inner
.pg_relationships
.values()
.filter(|rel| {
rel.elements.len() == 2
&& (rel.elements[0].value.identify() == node_id
|| rel.elements[1].value.identify() == node_id)
})
.count()
}
pub fn as_pattern_graph(&self) -> &PatternGraph<(), Subject> {
&self.inner
}
pub fn into_pattern_graph(self) -> PatternGraph<(), Subject> {
self.inner
}
#[cfg(not(feature = "thread-safe"))]
pub fn as_query(&self) -> GraphQuery<Subject> {
use std::rc::Rc;
let graph = Rc::new(PatternGraph {
pg_nodes: self.inner.pg_nodes.clone(),
pg_relationships: self.inner.pg_relationships.clone(),
pg_walks: self.inner.pg_walks.clone(),
pg_annotations: self.inner.pg_annotations.clone(),
pg_other: self.inner.pg_other.clone(),
pg_conflicts: self.inner.pg_conflicts.clone(),
});
crate::pattern_graph::from_pattern_graph(graph)
}
#[cfg(feature = "thread-safe")]
pub fn as_query(&self) -> GraphQuery<Subject> {
use std::sync::Arc;
let graph = Arc::new(PatternGraph {
pg_nodes: self.inner.pg_nodes.clone(),
pg_relationships: self.inner.pg_relationships.clone(),
pg_walks: self.inner.pg_walks.clone(),
pg_annotations: self.inner.pg_annotations.clone(),
pg_other: self.inner.pg_other.clone(),
pg_conflicts: self.inner.pg_conflicts.clone(),
});
crate::pattern_graph::from_pattern_graph(graph)
}
pub fn as_snapshot(&self) -> GraphView<(), Subject> {
let classifier = canonical_classifier();
crate::graph::graph_view::from_pattern_graph(&classifier, &self.inner)
}
fn make_placeholder_node(id: &Symbol) -> Pattern<Subject> {
Pattern::point(Subject {
identity: id.clone(),
labels: std::collections::HashSet::new(),
properties: HashMap::new(),
})
}
fn get_or_create_placeholder_node(&mut self, id: &Symbol) -> Pattern<Subject> {
if let Some(node) = self.inner.pg_nodes.get(id) {
node.clone()
} else {
let placeholder = Self::make_placeholder_node(id);
self.inner.pg_nodes.insert(id.clone(), placeholder.clone());
placeholder
}
}
fn get_or_create_placeholder_relationship(&self, id: &Symbol) -> Pattern<Subject> {
if let Some(rel) = self.inner.pg_relationships.get(id) {
rel.clone()
} else {
Pattern::point(Subject {
identity: id.clone(),
labels: std::collections::HashSet::new(),
properties: HashMap::new(),
})
}
}
fn find_element(&self, id: &Symbol) -> Option<Pattern<Subject>> {
self.inner
.pg_nodes
.get(id)
.or_else(|| self.inner.pg_relationships.get(id))
.or_else(|| self.inner.pg_walks.get(id))
.or_else(|| self.inner.pg_annotations.get(id))
.cloned()
}
}
impl Default for StandardGraph {
fn default() -> Self {
Self::new()
}
}
fn pattern_merge_policy(
) -> crate::reconcile::ReconciliationPolicy<crate::reconcile::SubjectMergeStrategy> {
crate::reconcile::ReconciliationPolicy::Merge(
crate::reconcile::ElementMergeStrategy::UnionElements,
crate::reconcile::default_subject_merge_strategy(),
)
}