Files
AI/참고/ontocast-main/ontocast/tool/structured_sparql.py
2026-05-12 19:40:31 +09:00

371 lines
12 KiB
Python

"""Structured SPARQL parser and executor.
This module provides tools for parsing and executing structured SPARQL queries
with separate ADD, UPDATE, and REMOVE sections.
"""
import logging
from ontocast.onto.enum import SPARQLOperationType
from ontocast.onto.rdfgraph import RDFGraph
from ontocast.onto.sparql_models import (
SPARQLOperationModel,
StructuredSPARQLQueryModel,
)
logger = logging.getLogger(__name__)
class StructuredSPARQLParser:
"""Parser for structured SPARQL queries with ADD, UPDATE, REMOVE sections."""
def __init__(self):
self.logger = logging.getLogger(__name__)
def parse_structured_query(self, query_text: str) -> StructuredSPARQLQueryModel:
"""Parse a structured SPARQL query from text.
Args:
query_text: The structured SPARQL query text
Returns:
StructuredSPARQLQueryModel: Parsed structured query
"""
self.logger.info("Parsing structured SPARQL query")
# Split into sections
sections = self._split_into_sections(query_text)
# Parse each section and combine all operations
all_operations = []
all_operations.extend(
self._parse_section_operations(
sections.get("ADD", ""), SPARQLOperationType.INSERT
)
)
all_operations.extend(
self._parse_section_operations(
sections.get("UPDATE", ""), SPARQLOperationType.UPDATE
)
)
all_operations.extend(
self._parse_section_operations(
sections.get("REMOVE", ""), SPARQLOperationType.DELETE
)
)
# Extract namespaces
namespaces = self._extract_namespaces(query_text)
structured_query = StructuredSPARQLQueryModel(
operations=all_operations,
namespaces=namespaces,
)
self.logger.info(f"Parsed structured query: {structured_query.get_summary()}")
return structured_query
def _split_into_sections(self, query_text: str) -> dict[str, str]:
"""Split query text into ADD, UPDATE, REMOVE sections.
Args:
query_text: The query text to split
Returns:
dict[str, str]: Dictionary mapping section names to content
"""
sections = {}
current_section = None
current_content = []
lines = query_text.split("\n")
for line in lines:
line = line.strip()
# Check for section headers
if line.upper().startswith("-- ADD SECTION:") or line.upper().startswith(
"ADD SECTION:"
):
if current_section:
sections[current_section] = "\n".join(current_content)
current_section = "ADD"
current_content = []
elif line.upper().startswith(
"-- UPDATE SECTION:"
) or line.upper().startswith("UPDATE SECTION:"):
if current_section:
sections[current_section] = "\n".join(current_content)
current_section = "UPDATE"
current_content = []
elif line.upper().startswith(
"-- REMOVE SECTION:"
) or line.upper().startswith("REMOVE SECTION:"):
if current_section:
sections[current_section] = "\n".join(current_content)
current_section = "REMOVE"
current_content = []
else:
current_content.append(line)
# Add the last section
if current_section:
sections[current_section] = "\n".join(current_content)
return sections
def _parse_section_operations(
self, section_content: str, default_type: SPARQLOperationType
) -> list[SPARQLOperationModel]:
"""Parse operations from a section.
Args:
section_content: The section content
default_type: Default operation type for the section
Returns:
list[SPARQLOperationModel]: List of parsed operations
"""
operations = []
if not section_content.strip():
return operations
# Split by PREFIX blocks and INSERT/UPDATE/DELETE blocks
blocks = self._split_into_blocks(section_content)
for block in blocks:
if block.strip():
operation = self._create_operation_from_block(block, default_type)
if operation:
operations.append(operation)
return operations
def _split_into_blocks(self, content: str) -> list[str]:
"""Split content into SPARQL operation blocks.
Args:
content: The content to split
Returns:
list[str]: List of operation blocks
"""
blocks = []
current_block = []
in_operation = False
lines = content.split("\n")
for line in lines:
line = line.strip()
if not line:
continue
# Check if this is the start of a new operation
if (
line.upper().startswith("INSERT")
or line.upper().startswith("UPDATE")
or line.upper().startswith("DELETE")
):
# Save previous block if exists
if current_block:
blocks.append("\n".join(current_block))
# Start new block
current_block = [line]
in_operation = True
elif in_operation:
current_block.append(line)
# Check if operation is complete
if line == "}" and current_block:
blocks.append("\n".join(current_block))
current_block = []
in_operation = False
# Add any remaining block
if current_block:
blocks.append("\n".join(current_block))
return blocks
def _create_operation_from_block(
self, block: str, default_type: SPARQLOperationType
) -> SPARQLOperationModel | None:
"""Create a SPARQL operation from a block.
Args:
block: The operation block
default_type: Default operation type
Returns:
SPARQLOperationModel | None: Created operation or None if invalid
"""
if not block.strip():
return None
# Determine operation type
if block.upper().startswith("INSERT"):
operation_type = SPARQLOperationType.INSERT
elif block.upper().startswith("UPDATE"):
operation_type = SPARQLOperationType.UPDATE
elif block.upper().startswith("DELETE"):
operation_type = SPARQLOperationType.DELETE
else:
operation_type = default_type
return SPARQLOperationModel(operation_type=operation_type, query=block.strip())
def _extract_namespaces(self, query_text: str) -> dict[str, str]:
"""Extract namespace declarations from query text.
Args:
query_text: The query text
Returns:
dict[str, str]: Dictionary mapping prefixes to URIs
"""
namespaces = {}
lines = query_text.split("\n")
for line in lines:
line = line.strip()
if line.upper().startswith("PREFIX "):
# Parse PREFIX declaration
parts = line.split()
if len(parts) >= 3:
prefix = parts[1].rstrip(":")
uri = parts[2].strip("<>")
namespaces[prefix] = uri
return namespaces
class StructuredSPARQLExecutor:
"""Executor for structured SPARQL queries."""
def __init__(self, sparql_tool):
"""Initialize the executor.
Args:
sparql_tool: The SPARQL tool for executing operations
"""
self.sparql_tool = sparql_tool
self.logger = logging.getLogger(__name__)
def execute_structured_query(
self, structured_query: StructuredSPARQLQueryModel, graph: RDFGraph
) -> bool:
"""Execute a structured SPARQL query.
Args:
structured_query: The structured query to execute
graph: The RDF graph to execute against
Returns:
bool: True if execution was successful
"""
self.logger.info(
f"Executing structured query: {structured_query.get_summary()}"
)
try:
# Execute operations in order: ADD, UPDATE, REMOVE
all_operations = structured_query.get_all_operations()
for operation in all_operations:
# Execute SPARQLOperationModel directly
self.sparql_tool.execute_operation(operation)
self.logger.debug(
f"Executed {operation.operation_type.value} operation"
)
self.logger.info("Structured query execution completed successfully")
return True
except Exception as e:
self.logger.error(f"Failed to execute structured query: {e}")
return False
def execute_add_operations(
self, structured_query: StructuredSPARQLQueryModel, graph: RDFGraph
) -> bool:
"""Execute only ADD operations from a structured query.
Args:
structured_query: The structured query
graph: The RDF graph to execute against
Returns:
bool: True if execution was successful
"""
return self._execute_operations(structured_query.get_add_operations(), "ADD")
def execute_update_operations(
self, structured_query: StructuredSPARQLQueryModel, graph: RDFGraph
) -> bool:
"""Execute only UPDATE operations from a structured query.
Args:
structured_query: The structured query
graph: The RDF graph to execute against
Returns:
bool: True if execution was successful
"""
return self._execute_operations(
structured_query.get_update_operations(), "UPDATE"
)
def execute_remove_operations(
self, structured_query: StructuredSPARQLQueryModel, graph: RDFGraph
) -> bool:
"""Execute only REMOVE operations from a structured query.
Args:
structured_query: The structured query
graph: The RDF graph to execute against
Returns:
bool: True if execution was successful
"""
return self._execute_operations(
structured_query.get_remove_operations(), "REMOVE"
)
def _execute_operations(
self, operations: list[SPARQLOperationModel], section_name: str
) -> bool:
"""Execute a list of operations.
Args:
operations: List of operations to execute
section_name: Name of the section for logging
Returns:
bool: True if execution was successful
"""
if not operations:
self.logger.info(f"No {section_name} operations to execute")
return True
try:
for operation in operations:
# Execute SPARQLOperationModel directly
self.sparql_tool.execute_operation(operation)
self.logger.debug(
f"Executed {section_name} {operation.operation_type.value} operation"
)
self.logger.info(
f"Executed {len(operations)} {section_name} operations successfully"
)
return True
except Exception as e:
self.logger.error(f"Failed to execute {section_name} operations: {e}")
return False