use serde_json::json;
use crate::catalog::manager::CatalogManager;
use crate::catalog::operations::QueryType;
use crate::exec::ExecutionError;
use crate::schema::types::GraphTypeDefinition;
pub struct IndexSchemaValidator<'a> {
catalog_manager: &'a CatalogManager,
}
impl<'a> IndexSchemaValidator<'a> {
pub fn new(catalog_manager: &'a CatalogManager) -> Self {
Self { catalog_manager }
}
}
impl<'a> IndexSchemaValidator<'a> {
pub fn validate_index_creation(
&self,
graph_name: &str,
label: &str,
properties: &[String],
) -> Result<(), ExecutionError> {
let graph_type = match self.get_graph_type(graph_name) {
Ok(Some(gt)) => gt,
Ok(None) => {
log::info!(
"No schema defined for graph '{}', allowing index creation",
graph_name
);
return Ok(());
}
Err(e) => return Err(e),
};
let node_type = graph_type
.node_types
.iter()
.find(|nt| nt.label == label)
.ok_or_else(|| {
ExecutionError::SchemaValidation(format!(
"Cannot create index: label '{}' not defined in graph type",
label
))
})?;
for property in properties {
if !node_type.properties.iter().any(|p| p.name == *property) {
return Err(ExecutionError::SchemaValidation(format!(
"Cannot create index: property '{}' not defined for label '{}'",
property, label
)));
}
}
for property in properties {
if let Some(prop_def) = node_type.properties.iter().find(|p| p.name == *property) {
match &prop_def.data_type {
crate::schema::types::DataType::Vector(_) => {
log::info!("Creating index on vector property '{}'", property);
}
crate::schema::types::DataType::List(_) => {
log::warn!(
"Creating index on list property '{}' - may have limited effectiveness",
property
);
}
_ => {
}
}
if prop_def
.constraints
.iter()
.any(|c| matches!(c, crate::schema::types::Constraint::Unique))
{
log::info!(
"Creating index on unique property '{}' - will enforce uniqueness",
property
);
}
}
}
Ok(())
}
pub fn validate_index_rebuild(
&self,
graph_name: &str,
index_name: &str,
) -> Result<(), ExecutionError> {
let _graph_type = match self.get_graph_type(graph_name) {
Ok(Some(gt)) => gt,
Ok(None) => {
log::info!(
"No schema defined for graph '{}', allowing index rebuild",
graph_name
);
return Ok(());
}
Err(e) => return Err(e),
};
log::info!(
"Validated index '{}' for rebuild in graph '{}'",
index_name,
graph_name
);
Ok(())
}
#[allow(dead_code)] pub fn suggest_indexes(
&self,
graph_name: &str,
) -> Result<Vec<IndexSuggestion>, ExecutionError> {
let mut suggestions = Vec::new();
let graph_type = match self.get_graph_type(graph_name) {
Ok(Some(gt)) => gt,
Ok(None) => {
return Ok(suggestions);
}
Err(e) => return Err(e),
};
for node_type in &graph_type.node_types {
for property in &node_type.properties {
if property
.constraints
.iter()
.any(|c| matches!(c, crate::schema::types::Constraint::Unique))
{
suggestions.push(IndexSuggestion {
label: node_type.label.clone(),
properties: vec![property.name.clone()],
reason: "Unique constraint would benefit from index".to_string(),
priority: IndexPriority::High,
});
}
if property.name.to_lowercase().contains("id") || property.name == "identifier" {
suggestions.push(IndexSuggestion {
label: node_type.label.clone(),
properties: vec![property.name.clone()],
reason: "ID field commonly used in lookups".to_string(),
priority: IndexPriority::Medium,
});
}
}
}
Ok(suggestions)
}
fn get_graph_type(
&self,
graph_name: &str,
) -> Result<Option<GraphTypeDefinition>, ExecutionError> {
match self.catalog_manager.query_read_only(
"graph",
QueryType::GetGraph,
json!({ "name": graph_name }),
) {
Ok(response) => {
if let Some(data) = response.data() {
if let Some(graph_type_name) = data.get("graph_type").and_then(|v| v.as_str()) {
match self.catalog_manager.query_read_only(
"graph_type",
QueryType::GetGraphType,
json!({ "name": graph_type_name }),
) {
Ok(type_response) => {
if let Some(type_data) = type_response.data() {
match serde_json::from_value::<GraphTypeDefinition>(
type_data.clone(),
) {
Ok(graph_type) => Ok(Some(graph_type)),
Err(_) => Ok(None),
}
} else {
Ok(None)
}
}
Err(_) => Ok(None),
}
} else {
Ok(None)
}
} else {
Ok(None)
}
}
Err(_) => {
Ok(None)
}
}
}
}
#[derive(Debug, Clone)]
#[allow(dead_code)] pub struct IndexSuggestion {
pub label: String,
pub properties: Vec<String>,
pub reason: String,
pub priority: IndexPriority,
}
#[derive(Debug, Clone, PartialEq)]
#[allow(dead_code)] pub enum IndexPriority {
High,
Medium,
Low,
}
#[cfg(test)]
mod tests {
#[test]
fn test_index_validator_creation() {
}
}