pub mod ast;
pub mod executor;
pub mod parse_cache;
pub mod parser;
pub mod planner;
pub mod result;
pub mod tokenizer;
mod window;
pub use ast::OutputFormat;
pub use executor::{execute_mutable, is_mutation_query, CypherExecutor};
pub use parse_cache::parse_cypher_cached as parse_cypher;
pub use planner::mark_lazy_eligibility;
pub use planner::optimize;
pub use planner::schema_check::{
collect_unknown_pattern_warnings, validate_schema, warn_unknown_pattern_refs,
};
pub use planner::simplification::rewrite_text_score;
pub use result::CypherResult;
use crate::datatypes::values::Value;
use crate::graph::schema::DirGraph;
use crate::graph::storage::GraphRead;
use ast::*;
fn estimate_match_rows(m: &MatchClause, graph: &DirGraph) -> Option<usize> {
let types = collect_node_types(m);
if types.is_empty() {
Some(graph.graph.node_count())
} else {
types
.iter()
.map(|t| graph.type_indices.get(t.as_str()).map_or(0, |v| v.len()))
.min()
}
}
fn collect_node_types(m: &MatchClause) -> Vec<String> {
use crate::graph::core::pattern_matching::PatternElement;
let mut types = Vec::new();
for pattern in &m.patterns {
for element in &pattern.elements {
if let PatternElement::Node(np) = element {
if let Some(ref t) = np.node_type {
types.push(t.clone());
}
}
}
}
types
}
pub fn parse_with_mutation_check(
query: &str,
) -> Result<(ast::CypherQuery, bool), crate::error::KgError> {
let parsed = parse_cypher(query)?;
let is_mutation = is_mutation_query(&parsed);
Ok((parsed, is_mutation))
}
pub fn generate_explain_result(query: &CypherQuery, graph: &DirGraph) -> result::CypherResult {
let mut rows = Vec::new();
for (i, clause) in query.clauses.iter().enumerate() {
let step = (i + 1) as i64;
let operation = executor::clause_display_name(clause);
let est = match clause {
Clause::Match(m) | Clause::OptionalMatch(m) => estimate_match_rows(m, graph)
.map(|e| Value::Int64(e as i64))
.unwrap_or(Value::Null),
Clause::FusedCountAll { .. }
| Clause::FusedMatchReturnAggregate { .. }
| Clause::FusedOptionalMatchAggregate { .. }
| Clause::FusedCountTypedEdge { .. }
| Clause::FusedCountAnchoredEdges { .. } => Value::Int64(1),
Clause::FusedCountTypedNode { node_type, .. } => {
let n = graph
.type_indices
.get(node_type.as_str())
.map_or(0, |v| v.len());
Value::Int64(n.min(1) as i64)
}
Clause::FusedCountByType { .. } => Value::Int64(graph.type_indices.len() as i64),
Clause::FusedVectorScoreTopK { limit, .. }
| Clause::FusedOrderByTopK { limit, .. }
| Clause::FusedNodeScanTopK { limit, .. } => Value::Int64(*limit as i64),
_ => Value::Null,
};
rows.push(vec![Value::Int64(step), Value::String(operation), est]);
}
result::CypherResult {
columns: vec!["step".into(), "operation".into(), "estimated_rows".into()],
rows,
stats: None,
profile: None,
diagnostics: None,
lazy: None,
}
}