use redis::{Connection, Value};
use crate::{
assignments::FromTable,
result_set::{Column, FromRedisValueWithGraph, Scalar, Statistics, Take},
server_type_error, RedisGraphError, RedisGraphResult, RedisString, ResultSet,
};
pub struct Graph {
conn: Connection,
name: String,
labels: Vec<RedisString>,
relationship_types: Vec<RedisString>,
property_keys: Vec<RedisString>,
}
impl Graph {
pub fn open(conn: Connection, name: String) -> RedisGraphResult<Self> {
let mut graph = Self {
conn,
name,
labels: Vec::new(),
relationship_types: Vec::new(),
property_keys: Vec::new(),
};
graph.mutate("CREATE (dummy:__DUMMY_LABEL__)")?;
graph.mutate("MATCH (dummy:__DUMMY_LABEL__) DELETE dummy")?;
Ok(graph)
}
pub fn query<T: FromTable>(&mut self, query: &str) -> RedisGraphResult<T> {
self.query_with_statistics(query).map(|(value, _)| value)
}
pub fn query_with_statistics<T: FromTable>(
&mut self,
query: &str,
) -> RedisGraphResult<(T, Statistics)> {
let response: Value = self.request(query)?;
let result_set = self.get_result_set(response)?;
let value = T::from_table(&result_set)?;
Ok((value, result_set.statistics))
}
pub fn mutate(&mut self, query: &str) -> RedisGraphResult<()> {
self.mutate_with_statistics(query).map(|_| ())
}
pub fn mutate_with_statistics(&mut self, query: &str) -> RedisGraphResult<Statistics> {
let response: Value = self.request(query)?;
let result_set = self.get_result_set(response)?;
Ok(result_set.statistics)
}
pub fn delete(mut self) -> RedisGraphResult<()> {
redis::cmd("GRAPH.DELETE")
.arg(self.name())
.query::<()>(&mut self.conn)
.map_err(RedisGraphError::from)
}
pub fn update_labels(&mut self) -> RedisGraphResult<()> {
let refresh_response = self.request("CALL db.labels()")?;
self.labels = self.get_mapping(refresh_response)?;
Ok(())
}
pub fn update_relationship_types(&mut self) -> RedisGraphResult<()> {
let refresh_response = self.request("CALL db.relationshipTypes()")?;
self.relationship_types = self.get_mapping(refresh_response)?;
Ok(())
}
pub fn update_property_keys(&mut self) -> RedisGraphResult<()> {
let refresh_response = self.request("CALL db.propertyKeys()")?;
self.property_keys = self.get_mapping(refresh_response)?;
Ok(())
}
pub fn name(&self) -> &str {
&self.name
}
pub fn labels(&self) -> &[RedisString] {
&self.labels[..]
}
pub fn relationship_types(&self) -> &[RedisString] {
&self.relationship_types[..]
}
pub fn property_keys(&self) -> &[RedisString] {
&self.property_keys[..]
}
fn request(&mut self, query: &str) -> RedisGraphResult<Value> {
redis::cmd("GRAPH.QUERY")
.arg(self.name())
.arg(query)
.arg("--compact")
.query(&mut self.conn)
.map_err(RedisGraphError::from)
}
fn get_result_set(&mut self, response: Value) -> RedisGraphResult<ResultSet> {
match ResultSet::from_redis_value_with_graph(response.clone(), self) {
Ok(result_set) => Ok(result_set),
Err(RedisGraphError::LabelNotFound) => {
self.update_labels()?;
self.get_result_set(response)
}
Err(RedisGraphError::RelationshipTypeNotFound) => {
self.update_relationship_types()?;
self.get_result_set(response)
}
Err(RedisGraphError::PropertyKeyNotFound) => {
self.update_property_keys()?;
self.get_result_set(response)
}
any_err => any_err,
}
}
fn get_mapping(&self, response: Value) -> RedisGraphResult<Vec<RedisString>> {
let mut result_set = ResultSet::from_redis_value_with_graph(response, self)?;
match &mut result_set.columns[0] {
Column::Scalars(scalars) => scalars
.iter_mut()
.map(|scalar| match scalar.take() {
Scalar::String(string) => Ok(string),
_ => server_type_error!("expected strings in first column of result set"),
})
.collect::<RedisGraphResult<Vec<RedisString>>>(),
_ => server_type_error!("expected scalars as first column in result set"),
}
}
}