use crate::{
parser::{parse_header, redis_value_as_untyped_string_vec},
FalkorResult,
};
use std::str::FromStr;
pub(crate) mod constraint;
pub(crate) mod execution_plan;
pub(crate) mod index;
pub(crate) mod lazy_result_set;
pub(crate) mod slowlog_entry;
#[derive(Copy, Clone, Debug, Eq, PartialEq, strum::IntoStaticStr)]
enum StatisticType {
#[strum(serialize = "Labels added")]
LabelsAdded,
#[strum(serialize = "Labels removed")]
LabelsRemoved,
#[strum(serialize = "Nodes created")]
NodesCreated,
#[strum(serialize = "Nodes deleted")]
NodesDeleted,
#[strum(serialize = "Properties set")]
PropertiesSet,
#[strum(serialize = "Properties removed")]
PropertiesRemoved,
#[strum(serialize = "Indices created")]
IndicesCreated,
#[strum(serialize = "Indices deleted")]
IndicesDeleted,
#[strum(serialize = "Relationships created")]
RelationshipsCreated,
#[strum(serialize = "Relationships deleted")]
RelationshipsDeleted,
#[strum(serialize = "Cached execution")]
CachedExecution,
#[strum(serialize = "internal execution time")]
InternalExecutionTime,
}
#[derive(Clone, Debug, Default)]
pub struct QueryResult<T> {
pub header: Vec<String>,
pub data: T,
pub stats: Vec<String>,
}
impl<T> QueryResult<T> {
#[cfg_attr(
feature = "tracing",
tracing::instrument(name = "New Falkor Response", skip_all, level = "trace")
)]
pub fn from_response(
headers: Option<redis::Value>,
data: T,
stats: redis::Value,
) -> FalkorResult<Self> {
Ok(Self {
header: match headers {
Some(headers) => parse_header(headers)?,
None => vec![],
},
data,
stats: redis_value_as_untyped_string_vec(stats)?,
})
}
fn get_statistics<S>(
&self,
stat_type: StatisticType,
) -> Option<S>
where
S: FromStr,
{
for stat in self.stats.iter() {
if stat.contains(Into::<&'static str>::into(stat_type)) {
return stat
.split(": ")
.nth(1)
.and_then(|stat_value| stat_value.split(' ').next())
.and_then(|res| res.parse().ok());
}
}
None
}
pub fn get_labels_added(&self) -> Option<i64> {
self.get_statistics(StatisticType::LabelsAdded)
}
pub fn get_labels_removed(&self) -> Option<i64> {
self.get_statistics(StatisticType::LabelsRemoved)
}
pub fn get_nodes_created(&self) -> Option<i64> {
self.get_statistics(StatisticType::NodesCreated)
}
pub fn get_nodes_deleted(&self) -> Option<i64> {
self.get_statistics(StatisticType::NodesDeleted)
}
pub fn get_properties_set(&self) -> Option<i64> {
self.get_statistics(StatisticType::PropertiesSet)
}
pub fn get_properties_removed(&self) -> Option<i64> {
self.get_statistics(StatisticType::PropertiesRemoved)
}
pub fn get_indices_created(&self) -> Option<i64> {
self.get_statistics(StatisticType::IndicesCreated)
}
pub fn get_indices_deleted(&self) -> Option<i64> {
self.get_statistics(StatisticType::IndicesDeleted)
}
pub fn get_relationship_created(&self) -> Option<i64> {
self.get_statistics(StatisticType::RelationshipsCreated)
}
pub fn get_relationship_deleted(&self) -> Option<i64> {
self.get_statistics(StatisticType::RelationshipsDeleted)
}
pub fn get_cached_execution(&self) -> Option<bool> {
self.get_statistics(StatisticType::CachedExecution)
.map(|res: i64| res != 0)
}
pub fn get_internal_execution_time(&self) -> Option<f64> {
self.get_statistics(StatisticType::InternalExecutionTime)
}
}
#[cfg(test)]
mod tests {
use crate::test_utils::open_empty_test_graph;
#[test]
fn test_get_statistics() {
let mut graph = open_empty_test_graph("imdb_stats_test");
{
let query_result = graph
.inner
.query("CREATE (a:new_node { new_property: 1})-[b:new_relationship]->(a)")
.execute()
.expect("Could not run query");
assert!(query_result.get_internal_execution_time().is_some());
assert_eq!(query_result.get_nodes_created(), Some(1));
assert_eq!(query_result.get_relationship_created(), Some(1));
assert_eq!(query_result.get_properties_set(), Some(1));
}
{
let query_result = graph
.inner
.query(
"MATCH (a:new_node { new_property: 1})-[b:new_relationship]->(a) DELETE b, a",
)
.execute()
.expect("Could not run query");
assert_eq!(query_result.get_nodes_deleted(), Some(1));
assert_eq!(query_result.get_relationship_deleted(), Some(1));
}
{
let query_result = graph
.inner
.query("UNWIND range(0, 1000) AS x RETURN x")
.execute()
.expect("Could not run query");
assert_eq!(query_result.get_cached_execution(), Some(false));
}
{
let query_result = graph
.inner
.query("UNWIND range(0, 1000) AS x RETURN x")
.execute()
.expect("Could not run query");
assert_eq!(query_result.get_cached_execution(), Some(true));
}
}
}