Skip to main content

oxirs_gql/
mapping.rs

1//! RDF to GraphQL mapping utilities
2
3use crate::ast::{Document, Field, OperationDefinition, Selection, SelectionSet, Value};
4use crate::optimizer::QueryOptimizer;
5use crate::schema::{PropertyType, RdfVocabulary};
6use anyhow::{anyhow, Result};
7use std::collections::HashMap;
8use std::fmt::Write;
9use std::sync::Arc;
10
11/// Configuration for query translation
12#[derive(Debug, Clone)]
13pub struct TranslationConfig {
14    pub default_limit: usize,
15    pub max_limit: usize,
16    pub enable_aggregation: bool,
17    pub enable_full_text_search: bool,
18    pub optimize_queries: bool,
19    pub enable_nested_filtering: bool,
20    pub enable_unions: bool,
21    pub enable_sorting: bool,
22    pub max_query_depth: usize,
23}
24
25impl Default for TranslationConfig {
26    fn default() -> Self {
27        Self {
28            default_limit: 100,
29            max_limit: 10000,
30            enable_aggregation: true,
31            enable_full_text_search: false,
32            optimize_queries: true,
33            enable_nested_filtering: true,
34            enable_unions: true,
35            enable_sorting: true,
36            max_query_depth: 10,
37        }
38    }
39}
40
41/// Context for query translation
42#[derive(Debug, Clone)]
43pub struct QueryContext {
44    pub variables: HashMap<String, Value>,
45    pub operation_name: Option<String>,
46    pub depth: usize,
47    pub fragments: HashMap<String, crate::ast::FragmentDefinition>,
48}
49
50impl QueryContext {
51    pub fn new() -> Self {
52        Self {
53            variables: HashMap::new(),
54            operation_name: None,
55            depth: 0,
56            fragments: HashMap::new(),
57        }
58    }
59}
60
61/// Maps RDF data to GraphQL types and translates GraphQL queries to SPARQL
62pub struct RdfGraphQLMapper {
63    namespace_prefixes: HashMap<String, String>,
64    vocabulary: Option<RdfVocabulary>,
65    config: TranslationConfig,
66    optimizer: Option<Arc<QueryOptimizer>>,
67    variable_counter: std::cell::RefCell<usize>,
68}
69
70impl RdfGraphQLMapper {
71    pub fn new() -> Self {
72        let mut namespace_prefixes = HashMap::new();
73
74        // Add common namespace prefixes
75        namespace_prefixes.insert(
76            "rdf".to_string(),
77            "http://www.w3.org/1999/02/22-rdf-syntax-ns#".to_string(),
78        );
79        namespace_prefixes.insert(
80            "rdfs".to_string(),
81            "http://www.w3.org/2000/01/rdf-schema#".to_string(),
82        );
83        namespace_prefixes.insert(
84            "owl".to_string(),
85            "http://www.w3.org/2002/07/owl#".to_string(),
86        );
87        namespace_prefixes.insert(
88            "xsd".to_string(),
89            "http://www.w3.org/2001/XMLSchema#".to_string(),
90        );
91        namespace_prefixes.insert("foaf".to_string(), "http://xmlns.com/foaf/0.1/".to_string());
92        namespace_prefixes.insert("schema".to_string(), "http://schema.org/".to_string());
93        namespace_prefixes.insert(
94            "dc".to_string(),
95            "http://purl.org/dc/elements/1.1/".to_string(),
96        );
97        namespace_prefixes.insert(
98            "dbo".to_string(),
99            "http://dbpedia.org/ontology/".to_string(),
100        );
101        namespace_prefixes.insert(
102            "geo".to_string(),
103            "http://www.w3.org/2003/01/geo/wgs84_pos#".to_string(),
104        );
105        namespace_prefixes.insert(
106            "skos".to_string(),
107            "http://www.w3.org/2004/02/skos/core#".to_string(),
108        );
109
110        Self {
111            namespace_prefixes,
112            vocabulary: None,
113            config: TranslationConfig::default(),
114            optimizer: None,
115            variable_counter: std::cell::RefCell::new(0),
116        }
117    }
118
119    pub fn with_vocabulary(mut self, vocabulary: RdfVocabulary) -> Self {
120        self.vocabulary = Some(vocabulary);
121        self
122    }
123
124    pub fn with_config(mut self, config: TranslationConfig) -> Self {
125        self.config = config;
126        self
127    }
128
129    pub fn with_optimizer(mut self, optimizer: Arc<QueryOptimizer>) -> Self {
130        self.optimizer = Some(optimizer);
131        self
132    }
133
134    pub fn add_namespace(&mut self, prefix: String, uri: String) {
135        self.namespace_prefixes.insert(prefix, uri);
136    }
137
138    /// Convert a GraphQL query to SPARQL based on RDF vocabulary mapping
139    pub async fn graphql_to_sparql(
140        &self,
141        document: &Document,
142        type_name: &str,
143        context: &QueryContext,
144    ) -> Result<String> {
145        // Use optimizer if available
146        if let Some(ref optimizer) = self.optimizer {
147            let query_plan = optimizer.get_query_plan(document).await?;
148            if self.config.optimize_queries {
149                return Ok(query_plan.sparql_query);
150            }
151        }
152
153        // Reset variable counter for this query
154        *self.variable_counter.borrow_mut() = 0;
155
156        let mut sparql_builder = SparqlQueryBuilder::new(&self.namespace_prefixes, &self.config);
157
158        // Extract the operation
159        let operation = self.extract_operation(document, &context.operation_name)?;
160
161        // Validate query depth
162        let query_depth = self.calculate_query_depth(&operation.selection_set);
163        if query_depth > self.config.max_query_depth {
164            return Err(anyhow!(
165                "Query depth {} exceeds maximum allowed depth {}",
166                query_depth,
167                self.config.max_query_depth
168            ));
169        }
170
171        // Handle different operation types
172        match operation.operation_type {
173            crate::ast::OperationType::Query => {
174                self.translate_query_operation(operation, &mut sparql_builder, context, type_name)
175            }
176            crate::ast::OperationType::Mutation => self.translate_mutation_operation(
177                operation,
178                &mut sparql_builder,
179                context,
180                type_name,
181            ),
182            crate::ast::OperationType::Subscription => {
183                Err(anyhow!("Subscription operations not yet supported"))
184            }
185        }
186    }
187
188    fn extract_operation<'a>(
189        &self,
190        document: &'a Document,
191        operation_name: &Option<String>,
192    ) -> Result<&'a OperationDefinition> {
193        let operations: Vec<_> = document
194            .definitions
195            .iter()
196            .filter_map(|def| match def {
197                crate::ast::Definition::Operation(op) => Some(op),
198                _ => None,
199            })
200            .collect();
201
202        match (operations.len(), operation_name) {
203            (0, _) => Err(anyhow!("No operations found in document")),
204            (1, _) => Ok(operations[0]),
205            (_, Some(name)) => operations
206                .iter()
207                .find(|op| op.name.as_ref() == Some(name))
208                .copied()
209                .ok_or_else(|| anyhow!("Operation '{}' not found", name)),
210            (_, None) => Err(anyhow!(
211                "Multiple operations found but no operation name specified"
212            )),
213        }
214    }
215
216    fn calculate_query_depth(&self, selection_set: &SelectionSet) -> usize {
217        fn calculate_depth_recursive(selection_set: &SelectionSet, current_depth: usize) -> usize {
218            let mut max_depth = current_depth;
219
220            for selection in &selection_set.selections {
221                let depth = match selection {
222                    Selection::Field(field) => {
223                        if let Some(ref nested_selection) = field.selection_set {
224                            calculate_depth_recursive(nested_selection, current_depth + 1)
225                        } else {
226                            current_depth + 1
227                        }
228                    }
229                    Selection::InlineFragment(fragment) => {
230                        calculate_depth_recursive(&fragment.selection_set, current_depth)
231                    }
232                    Selection::FragmentSpread(_) => current_depth + 1, // Simplified
233                };
234                max_depth = max_depth.max(depth);
235            }
236            max_depth
237        }
238
239        calculate_depth_recursive(selection_set, 0)
240    }
241
242    fn translate_query_operation(
243        &self,
244        operation: &OperationDefinition,
245        builder: &mut SparqlQueryBuilder,
246        context: &QueryContext,
247        type_name: &str,
248    ) -> Result<String> {
249        // For Query type, analyze the selection set
250        if type_name == "Query" {
251            self.translate_root_selection_set(&operation.selection_set, builder, context)?;
252        } else {
253            return Err(anyhow!("Only Query type supported for now"));
254        }
255
256        builder.build()
257    }
258
259    fn translate_mutation_operation(
260        &self,
261        operation: &OperationDefinition,
262        builder: &mut SparqlQueryBuilder,
263        context: &QueryContext,
264        type_name: &str,
265    ) -> Result<String> {
266        // For Mutation type, translate to SPARQL UPDATE operations
267        if type_name == "Mutation" {
268            self.translate_mutation_selection_set(&operation.selection_set, builder, context)?;
269        } else {
270            return Err(anyhow!("Only Mutation type supported for mutations"));
271        }
272
273        builder.build()
274    }
275
276    fn translate_mutation_selection_set(
277        &self,
278        selection_set: &SelectionSet,
279        builder: &mut SparqlQueryBuilder,
280        context: &QueryContext,
281    ) -> Result<()> {
282        for selection in &selection_set.selections {
283            match selection {
284                Selection::Field(field) => {
285                    self.translate_mutation_field(field, builder, context)?;
286                }
287                Selection::InlineFragment(_) | Selection::FragmentSpread(_) => {
288                    // Handle fragments if needed
289                }
290            }
291        }
292        Ok(())
293    }
294
295    fn translate_mutation_field(
296        &self,
297        field: &Field,
298        builder: &mut SparqlQueryBuilder,
299        _context: &QueryContext,
300    ) -> Result<()> {
301        match field.name.as_str() {
302            "insertTriple" => {
303                let args: Vec<(String, Value)> = field
304                    .arguments
305                    .iter()
306                    .map(|arg| (arg.name.clone(), arg.value.clone()))
307                    .collect();
308                let triple = self.extract_triple_from_arguments(&args)?;
309                builder.add_insert_data(&triple);
310            }
311            "insertTriples" => {
312                let args: Vec<(String, Value)> = field
313                    .arguments
314                    .iter()
315                    .map(|arg| (arg.name.clone(), arg.value.clone()))
316                    .collect();
317                let triples = self.extract_triples_from_arguments(&args)?;
318                for triple in triples {
319                    builder.add_insert_data(&triple);
320                }
321            }
322            "deleteTriple" => {
323                let args: Vec<(String, Value)> = field
324                    .arguments
325                    .iter()
326                    .map(|arg| (arg.name.clone(), arg.value.clone()))
327                    .collect();
328                let triple = self.extract_triple_from_arguments(&args)?;
329                builder.add_delete_data(&triple);
330            }
331            "deleteTriples" => {
332                let args: Vec<(String, Value)> = field
333                    .arguments
334                    .iter()
335                    .map(|arg| (arg.name.clone(), arg.value.clone()))
336                    .collect();
337                let triples = self.extract_triples_from_arguments(&args)?;
338                for triple in triples {
339                    builder.add_delete_data(&triple);
340                }
341            }
342            "updateTriple" => {
343                // Update = Delete old + Insert new
344                let args: Vec<(String, Value)> = field
345                    .arguments
346                    .iter()
347                    .map(|arg| (arg.name.clone(), arg.value.clone()))
348                    .collect();
349                if let Ok(old_triple) = self.extract_old_triple_from_arguments(&args) {
350                    builder.add_delete_data(&old_triple);
351                }
352                if let Ok(new_triple) = self.extract_new_triple_from_arguments(&args) {
353                    builder.add_insert_data(&new_triple);
354                }
355            }
356            _ => {
357                return Err(anyhow!("Unsupported mutation operation: {}", field.name));
358            }
359        }
360        Ok(())
361    }
362
363    fn extract_triple_from_arguments(&self, arguments: &[(String, Value)]) -> Result<String> {
364        let mut subject = None;
365        let mut predicate = None;
366        let mut object = None;
367
368        for (arg_name, arg_value) in arguments {
369            match arg_name.as_str() {
370                "subject" => {
371                    subject = Some(self.format_rdf_term(arg_value)?);
372                }
373                "predicate" => {
374                    predicate = Some(self.format_rdf_term(arg_value)?);
375                }
376                "object" => {
377                    object = Some(self.format_rdf_term(arg_value)?);
378                }
379                _ => {}
380            }
381        }
382
383        match (subject, predicate, object) {
384            (Some(s), Some(p), Some(o)) => Ok(format!("{s} {p} {o} .")),
385            _ => Err(anyhow!(
386                "Missing required arguments: subject, predicate, or object"
387            )),
388        }
389    }
390
391    fn extract_triples_from_arguments(&self, arguments: &[(String, Value)]) -> Result<Vec<String>> {
392        for (arg_name, arg_value) in arguments {
393            if arg_name == "triples" {
394                if let Value::ListValue(triple_list) = arg_value {
395                    let mut result = Vec::new();
396                    for triple_value in triple_list {
397                        if let Value::ObjectValue(triple_obj) = triple_value {
398                            let triple_args: Vec<(String, Value)> = triple_obj
399                                .iter()
400                                .map(|(k, v)| (k.clone(), v.clone()))
401                                .collect();
402                            result.push(self.extract_triple_from_arguments(&triple_args)?);
403                        }
404                    }
405                    return Ok(result);
406                }
407            }
408        }
409        Err(anyhow!("No triples argument found"))
410    }
411
412    fn extract_old_triple_from_arguments(&self, arguments: &[(String, Value)]) -> Result<String> {
413        for (arg_name, arg_value) in arguments {
414            if arg_name == "old" {
415                if let Value::ObjectValue(old_obj) = arg_value {
416                    let old_args: Vec<(String, Value)> = old_obj
417                        .iter()
418                        .map(|(k, v)| (k.clone(), v.clone()))
419                        .collect();
420                    return self.extract_triple_from_arguments(&old_args);
421                }
422            }
423        }
424        Err(anyhow!("No old triple argument found"))
425    }
426
427    fn extract_new_triple_from_arguments(&self, arguments: &[(String, Value)]) -> Result<String> {
428        for (arg_name, arg_value) in arguments {
429            if arg_name == "new" {
430                if let Value::ObjectValue(new_obj) = arg_value {
431                    let new_args: Vec<(String, Value)> = new_obj
432                        .iter()
433                        .map(|(k, v)| (k.clone(), v.clone()))
434                        .collect();
435                    return self.extract_triple_from_arguments(&new_args);
436                }
437            }
438        }
439        Err(anyhow!("No new triple argument found"))
440    }
441
442    fn format_rdf_term(&self, value: &Value) -> Result<String> {
443        match value {
444            Value::StringValue(s) => {
445                // Check if it's a URI (contains :// or starts with http)
446                if s.contains("://") || s.starts_with("http") {
447                    Ok(format!("<{s}>"))
448                } else if s.starts_with("_:") {
449                    // Blank node
450                    Ok(s.clone())
451                } else {
452                    // Literal string
453                    Ok(format!("\"{}\"", s.replace('\"', "\\\"")))
454                }
455            }
456            Value::IntValue(i) => Ok(format!(
457                "\"{i}\"^^<http://www.w3.org/2001/XMLSchema#integer>"
458            )),
459            Value::FloatValue(f) => Ok(format!(
460                "\"{f}\"^^<http://www.w3.org/2001/XMLSchema#decimal>"
461            )),
462            Value::BooleanValue(b) => Ok(format!(
463                "\"{b}\"^^<http://www.w3.org/2001/XMLSchema#boolean>"
464            )),
465            Value::ObjectValue(obj) => {
466                // Handle typed literals
467                if let (Some(Value::StringValue(value)), Some(Value::StringValue(datatype))) =
468                    (obj.get("value"), obj.get("datatype"))
469                {
470                    Ok(format!("\"{value}\"^^<{datatype}>"))
471                } else if let (Some(Value::StringValue(value)), Some(Value::StringValue(lang))) =
472                    (obj.get("value"), obj.get("language"))
473                {
474                    Ok(format!("\"{value}\"@{lang}"))
475                } else {
476                    Err(anyhow!("Invalid RDF term object format"))
477                }
478            }
479            _ => Err(anyhow!("Unsupported value type for RDF term")),
480        }
481    }
482
483    fn translate_root_selection_set(
484        &self,
485        selection_set: &SelectionSet,
486        builder: &mut SparqlQueryBuilder,
487        context: &QueryContext,
488    ) -> Result<()> {
489        for selection in &selection_set.selections {
490            match selection {
491                Selection::Field(field) => {
492                    self.translate_root_field(field, builder, context)?;
493                }
494                Selection::InlineFragment(fragment) => {
495                    // Handle inline fragments
496                    self.translate_root_selection_set(&fragment.selection_set, builder, context)?;
497                }
498                Selection::FragmentSpread(fragment_spread) => {
499                    // Handle fragment spreads
500                    if let Some(fragment_def) =
501                        context.fragments.get(&fragment_spread.fragment_name)
502                    {
503                        self.translate_root_selection_set(
504                            &fragment_def.selection_set,
505                            builder,
506                            context,
507                        )?;
508                    }
509                }
510            }
511        }
512        Ok(())
513    }
514
515    fn translate_root_field(
516        &self,
517        field: &Field,
518        builder: &mut SparqlQueryBuilder,
519        context: &QueryContext,
520    ) -> Result<()> {
521        match field.name.as_str() {
522            "hello" | "version" => {
523                // These are computed fields, no SPARQL needed
524            }
525            "triples" => {
526                builder.add_count_query("?s ?p ?o");
527            }
528            "subjects" => {
529                let limit = self
530                    .extract_limit_from_field(field)
531                    .unwrap_or(self.config.default_limit);
532                builder.add_distinct_select("?s");
533                builder.add_where_pattern("?s ?p ?o");
534                builder.set_limit(limit);
535            }
536            "predicates" => {
537                let limit = self
538                    .extract_limit_from_field(field)
539                    .unwrap_or(self.config.default_limit);
540                builder.add_distinct_select("?p");
541                builder.add_where_pattern("?s ?p ?o");
542                builder.set_limit(limit);
543            }
544            "objects" => {
545                let limit = self
546                    .extract_limit_from_field(field)
547                    .unwrap_or(self.config.default_limit);
548                builder.add_distinct_select("?o");
549                builder.add_where_pattern("?s ?p ?o");
550                builder.set_limit(limit);
551            }
552            "sparql" => {
553                // Raw SPARQL - handled separately in resolvers
554            }
555            _ => {
556                // Handle vocabulary-based fields
557                if let Some(ref vocabulary) = self.vocabulary {
558                    self.translate_vocabulary_field(field, builder, context, vocabulary)?;
559                } else {
560                    // Default pattern for unknown fields
561                    let var_name = format!("?{}", field.name);
562                    builder.add_select(&var_name);
563                    builder.add_where_pattern(&format!(
564                        "?s {} {}",
565                        self.field_to_predicate(&field.name),
566                        var_name
567                    ));
568                }
569            }
570        }
571        Ok(())
572    }
573
574    fn translate_vocabulary_field(
575        &self,
576        field: &Field,
577        builder: &mut SparqlQueryBuilder,
578        context: &QueryContext,
579        vocabulary: &RdfVocabulary,
580    ) -> Result<()> {
581        // Look for matching classes in the vocabulary
582        let field_name_variations = vec![
583            field.name.clone(),
584            self.pluralize(&field.name),
585            self.singularize(&field.name),
586        ];
587
588        for class_uri in vocabulary.classes.keys() {
589            let class_name = self.uri_to_graphql_name(class_uri);
590            let class_name_lower = class_name.to_lowercase();
591
592            for variation in &field_name_variations {
593                if variation.to_lowercase() == class_name_lower
594                    || variation.to_lowercase() == self.pluralize(&class_name_lower)
595                {
596                    // This field corresponds to a vocabulary class
597                    return self
598                        .translate_class_query(field, class_uri, builder, context, vocabulary);
599                }
600            }
601        }
602
603        // No matching class found, treat as property
604        self.translate_property_field(field, builder, context, vocabulary)
605    }
606
607    fn translate_class_query(
608        &self,
609        field: &Field,
610        class_uri: &str,
611        builder: &mut SparqlQueryBuilder,
612        context: &QueryContext,
613        vocabulary: &RdfVocabulary,
614    ) -> Result<()> {
615        let subject_var = "?subject";
616
617        // Add type constraint
618        builder.add_where_pattern(&format!("{subject_var} a <{class_uri}>"));
619
620        // Handle arguments
621        let limit = self
622            .extract_limit_from_field(field)
623            .unwrap_or(self.config.default_limit);
624        let offset = self.extract_offset_from_field(field).unwrap_or(0);
625
626        if let Some(where_filter) = self.extract_where_from_field(field) {
627            builder.add_filter(&where_filter);
628        }
629
630        // Handle nested selections
631        if let Some(ref selection_set) = field.selection_set {
632            self.translate_object_selection_set(
633                selection_set,
634                subject_var,
635                builder,
636                context,
637                vocabulary,
638            )?;
639        } else {
640            // If no selection set, just return the URI
641            builder.add_select(subject_var);
642        }
643
644        builder.set_limit(limit);
645        if offset > 0 {
646            builder.set_offset(offset);
647        }
648
649        Ok(())
650    }
651
652    fn translate_object_selection_set(
653        &self,
654        selection_set: &SelectionSet,
655        subject_var: &str,
656        builder: &mut SparqlQueryBuilder,
657        context: &QueryContext,
658        vocabulary: &RdfVocabulary,
659    ) -> Result<()> {
660        for selection in &selection_set.selections {
661            match selection {
662                Selection::Field(field) => {
663                    self.translate_object_field(field, subject_var, builder, context, vocabulary)?;
664                }
665                Selection::InlineFragment(fragment) => {
666                    self.translate_object_selection_set(
667                        &fragment.selection_set,
668                        subject_var,
669                        builder,
670                        context,
671                        vocabulary,
672                    )?;
673                }
674                Selection::FragmentSpread(fragment_spread) => {
675                    if let Some(fragment_def) =
676                        context.fragments.get(&fragment_spread.fragment_name)
677                    {
678                        self.translate_object_selection_set(
679                            &fragment_def.selection_set,
680                            subject_var,
681                            builder,
682                            context,
683                            vocabulary,
684                        )?;
685                    }
686                }
687            }
688        }
689        Ok(())
690    }
691
692    fn translate_object_field(
693        &self,
694        field: &Field,
695        subject_var: &str,
696        builder: &mut SparqlQueryBuilder,
697        context: &QueryContext,
698        vocabulary: &RdfVocabulary,
699    ) -> Result<()> {
700        match field.name.as_str() {
701            "id" | "uri" => {
702                builder.add_select(subject_var);
703            }
704            _ => {
705                // Look for matching property
706                let property_uri = self.find_property_uri(&field.name, vocabulary);
707                if let Some(prop_uri) = property_uri {
708                    let field_var = format!("?{}", field.name);
709                    builder.add_select(&field_var);
710
711                    if let Some(property) = vocabulary.properties.get(&prop_uri) {
712                        match property.property_type {
713                            PropertyType::ObjectProperty => {
714                                builder.add_where_pattern(&format!(
715                                    "{subject_var} <{prop_uri}> {field_var}"
716                                ));
717
718                                // Handle nested object selections
719                                if let Some(ref selection_set) = field.selection_set {
720                                    self.translate_object_selection_set(
721                                        selection_set,
722                                        &field_var,
723                                        builder,
724                                        context,
725                                        vocabulary,
726                                    )?;
727                                }
728                            }
729                            PropertyType::DataProperty | PropertyType::AnnotationProperty => {
730                                builder.add_where_pattern(&format!(
731                                    "{subject_var} <{prop_uri}> {field_var}"
732                                ));
733                            }
734                        }
735                    } else {
736                        // Fallback
737                        builder
738                            .add_where_pattern(&format!("{subject_var} <{prop_uri}> {field_var}"));
739                    }
740                } else {
741                    // Fallback to predicate mapping
742                    let predicate = self.field_to_predicate(&field.name);
743                    let field_var = format!("?{}", field.name);
744                    builder.add_select(&field_var);
745                    builder.add_where_pattern(&format!("{subject_var} {predicate} {field_var}"));
746                }
747            }
748        }
749        Ok(())
750    }
751
752    fn translate_property_field(
753        &self,
754        field: &Field,
755        builder: &mut SparqlQueryBuilder,
756        _context: &QueryContext,
757        _vocabulary: &RdfVocabulary,
758    ) -> Result<()> {
759        // Default property field translation
760        let var_name = format!("?{}", field.name);
761        let predicate = self.field_to_predicate(&field.name);
762
763        builder.add_select(&var_name);
764        builder.add_where_pattern(&format!("?s {predicate} {var_name}"));
765
766        Ok(())
767    }
768
769    fn find_property_uri(&self, field_name: &str, vocabulary: &RdfVocabulary) -> Option<String> {
770        // Try to find a property that matches the field name
771        for (prop_uri, property) in &vocabulary.properties {
772            if let Some(ref label) = property.label {
773                if label.to_lowercase() == field_name.to_lowercase() {
774                    return Some(prop_uri.clone());
775                }
776            }
777
778            // Also try matching the local name from the URI
779            let local_name = prop_uri.split(['#', '/']).next_back().unwrap_or("");
780            if local_name.to_lowercase() == field_name.to_lowercase() {
781                return Some(prop_uri.clone());
782            }
783        }
784        None
785    }
786
787    fn uri_to_graphql_name(&self, uri: &str) -> String {
788        if let Some(fragment) = uri.split('#').next_back() {
789            self.to_pascal_case(fragment)
790        } else if let Some(segment) = uri.split('/').next_back() {
791            self.to_pascal_case(segment)
792        } else {
793            "Resource".to_string()
794        }
795    }
796
797    fn to_pascal_case(&self, input: &str) -> String {
798        let mut result = String::new();
799        let mut capitalize_next = true;
800
801        for ch in input.chars() {
802            if ch.is_alphanumeric() {
803                if capitalize_next {
804                    result.push(ch.to_uppercase().next().unwrap_or(ch));
805                    capitalize_next = false;
806                } else {
807                    result.push(ch);
808                }
809            } else {
810                capitalize_next = true;
811            }
812        }
813
814        result
815    }
816
817    fn pluralize(&self, word: &str) -> String {
818        if word.ends_with('s') || word.ends_with("sh") || word.ends_with("ch") {
819            format!("{word}es")
820        } else if let Some(stripped) = word.strip_suffix('y') {
821            format!("{stripped}ies")
822        } else {
823            format!("{word}s")
824        }
825    }
826
827    fn singularize(&self, word: &str) -> String {
828        if let Some(stripped) = word.strip_suffix("ies") {
829            format!("{stripped}y")
830        } else if word.ends_with("es") && (word.ends_with("shes") || word.ends_with("ches")) {
831            word[..word.len() - 2].to_string()
832        } else if word.ends_with('s') && word.len() > 1 {
833            word[..word.len() - 1].to_string()
834        } else {
835            word.to_string()
836        }
837    }
838
839    #[allow(dead_code)]
840    fn build_sparql_from_selection_set(
841        &self,
842        selection_set: &SelectionSet,
843    ) -> Result<(Vec<String>, Vec<String>)> {
844        let mut select_vars = Vec::new();
845        let mut where_patterns = Vec::new();
846
847        for selection in &selection_set.selections {
848            match selection {
849                Selection::Field(field) => {
850                    match field.name.as_str() {
851                        "hello" | "version" => {
852                            // These are computed fields, no SPARQL needed
853                        }
854                        "triples" => {
855                            // Count query
856                            select_vars.push("(COUNT(*) as ?count)".to_string());
857                            where_patterns.push("?s ?p ?o".to_string());
858                        }
859                        "subjects" => {
860                            select_vars.push("DISTINCT ?s".to_string());
861                            where_patterns.push("?s ?p ?o".to_string());
862
863                            // Handle limit argument
864                            if let Some(_limit) = self.extract_limit_from_field(field) {
865                                // Limit will be added later
866                            }
867                        }
868                        "predicates" => {
869                            select_vars.push("DISTINCT ?p".to_string());
870                            where_patterns.push("?s ?p ?o".to_string());
871                        }
872                        "objects" => {
873                            select_vars.push("DISTINCT ?o".to_string());
874                            where_patterns.push("?s ?p ?o".to_string());
875                        }
876                        "sparql" => {
877                            // Raw SPARQL - handled separately
878                        }
879                        _ => {
880                            // Default pattern for other fields
881                            let var_name = format!("?{}", field.name);
882                            select_vars.push(var_name.clone());
883                            where_patterns.push(format!(
884                                "?s {} {}",
885                                self.field_to_predicate(&field.name),
886                                var_name
887                            ));
888                        }
889                    }
890                }
891                _ => {
892                    // Handle fragments if needed
893                }
894            }
895        }
896
897        Ok((select_vars, where_patterns))
898    }
899
900    fn extract_limit_from_field(&self, field: &Field) -> Option<usize> {
901        for arg in &field.arguments {
902            if arg.name == "limit" {
903                if let Value::IntValue(i) = &arg.value {
904                    let limit = (*i as usize).min(self.config.max_limit);
905                    return Some(limit);
906                }
907            }
908        }
909        None
910    }
911
912    fn extract_offset_from_field(&self, field: &Field) -> Option<usize> {
913        for arg in &field.arguments {
914            if arg.name == "offset" {
915                if let Value::IntValue(i) = &arg.value {
916                    return Some(*i as usize);
917                }
918            }
919        }
920        None
921    }
922
923    fn extract_where_from_field(&self, field: &Field) -> Option<String> {
924        for arg in &field.arguments {
925            if arg.name == "where" {
926                if let Value::StringValue(s) = &arg.value {
927                    return Some(s.clone());
928                }
929            }
930        }
931        None
932    }
933
934    fn field_to_predicate(&self, field_name: &str) -> String {
935        // Simple mapping - in a real implementation, this would use vocabulary mappings
936        match field_name {
937            "name" => "foaf:name".to_string(),
938            "email" => "foaf:mbox".to_string(),
939            "knows" => "foaf:knows".to_string(),
940            "label" => "rdfs:label".to_string(),
941            "comment" => "rdfs:comment".to_string(),
942            _ => format!(":{field_name}"), // Default to local namespace
943        }
944    }
945
946    /// Create a GraphQL schema from RDF vocabulary
947    pub fn rdf_to_graphql_schema(&self, vocabulary_uri: &str) -> Result<String> {
948        // Placeholder implementation
949        Ok(format!(
950            r#"
951type Query {{
952  hello: String
953  version: String
954  triples: Int
955  subjects(limit: Int = 10): [String!]!
956  predicates(limit: Int = 10): [String!]!
957  objects(limit: Int = 10): [String!]!
958  sparql(query: String!): String
959}}
960
961# Generated from vocabulary: {vocabulary_uri}
962"#
963        ))
964    }
965}
966
967/// SPARQL query builder for constructing complex queries
968struct SparqlQueryBuilder {
969    prefixes: HashMap<String, String>,
970    select_vars: Vec<String>,
971    where_patterns: Vec<String>,
972    filters: Vec<String>,
973    group_by: Vec<String>,
974    order_by: Vec<String>,
975    limit: Option<usize>,
976    offset: Option<usize>,
977    distinct: bool,
978    config: TranslationConfig,
979    // UPDATE operation fields
980    insert_data: Vec<String>,
981    delete_data: Vec<String>,
982    insert_where: Vec<String>,
983    delete_where: Vec<String>,
984    is_update_query: bool,
985}
986
987impl SparqlQueryBuilder {
988    fn new(prefixes: &HashMap<String, String>, config: &TranslationConfig) -> Self {
989        Self {
990            prefixes: prefixes.clone(),
991            select_vars: Vec::new(),
992            where_patterns: Vec::new(),
993            filters: Vec::new(),
994            group_by: Vec::new(),
995            order_by: Vec::new(),
996            limit: None,
997            offset: None,
998            distinct: false,
999            config: config.clone(),
1000            insert_data: Vec::new(),
1001            delete_data: Vec::new(),
1002            insert_where: Vec::new(),
1003            delete_where: Vec::new(),
1004            is_update_query: false,
1005        }
1006    }
1007
1008    fn add_select(&mut self, var: &str) {
1009        if !self.select_vars.contains(&var.to_string()) {
1010            self.select_vars.push(var.to_string());
1011        }
1012    }
1013
1014    fn add_distinct_select(&mut self, var: &str) {
1015        self.distinct = true;
1016        self.add_select(var);
1017    }
1018
1019    fn add_count_query(&mut self, pattern: &str) {
1020        self.select_vars.push("(COUNT(*) as ?count)".to_string());
1021        self.add_where_pattern(pattern);
1022    }
1023
1024    fn add_where_pattern(&mut self, pattern: &str) {
1025        self.where_patterns.push(pattern.to_string());
1026    }
1027
1028    fn add_filter(&mut self, filter: &str) {
1029        self.filters.push(filter.to_string());
1030    }
1031
1032    fn set_limit(&mut self, limit: usize) {
1033        self.limit = Some(limit.min(self.config.max_limit));
1034    }
1035
1036    fn set_offset(&mut self, offset: usize) {
1037        self.offset = Some(offset);
1038    }
1039
1040    fn add_insert_data(&mut self, triple: &str) {
1041        self.is_update_query = true;
1042        self.insert_data.push(triple.to_string());
1043    }
1044
1045    fn add_delete_data(&mut self, triple: &str) {
1046        self.is_update_query = true;
1047        self.delete_data.push(triple.to_string());
1048    }
1049
1050    #[allow(dead_code)]
1051    fn add_insert_where(&mut self, triple: &str, condition: &str) {
1052        self.is_update_query = true;
1053        self.insert_where
1054            .push(format!("{triple} WHERE {{ {condition} }}"));
1055    }
1056
1057    #[allow(dead_code)]
1058    fn add_delete_where(&mut self, triple: &str, condition: &str) {
1059        self.is_update_query = true;
1060        self.delete_where
1061            .push(format!("{triple} WHERE {{ {condition} }}"));
1062    }
1063
1064    fn build(&self) -> Result<String> {
1065        let mut query = String::new();
1066
1067        // Add prefixes
1068        for (prefix, uri) in &self.prefixes {
1069            writeln!(query, "PREFIX {prefix}: <{uri}>")?;
1070        }
1071
1072        if !query.is_empty() {
1073            writeln!(query)?;
1074        }
1075
1076        if self.is_update_query {
1077            // Build UPDATE query
1078            self.build_update_query(&mut query)?;
1079        } else {
1080            // Build SELECT query
1081            self.build_select_query(&mut query)?;
1082        }
1083
1084        Ok(query)
1085    }
1086
1087    fn build_select_query(&self, query: &mut String) -> Result<()> {
1088        // Add SELECT clause
1089        if self.select_vars.is_empty() {
1090            write!(query, "SELECT *")?;
1091        } else {
1092            let distinct_clause = if self.distinct { "DISTINCT " } else { "" };
1093            write!(
1094                query,
1095                "SELECT {}{}",
1096                distinct_clause,
1097                self.select_vars.join(" ")
1098            )?;
1099        }
1100
1101        // Add WHERE clause
1102        if !self.where_patterns.is_empty() {
1103            writeln!(query)?;
1104            writeln!(query, "WHERE {{")?;
1105            for pattern in &self.where_patterns {
1106                writeln!(query, "  {pattern}")?;
1107            }
1108
1109            // Add filters
1110            for filter in &self.filters {
1111                writeln!(query, "  FILTER({filter})")?;
1112            }
1113
1114            write!(query, "}}")?
1115        }
1116
1117        // Add GROUP BY
1118        if !self.group_by.is_empty() {
1119            writeln!(query)?;
1120            write!(query, "GROUP BY {}", self.group_by.join(" "))?;
1121        }
1122
1123        // Add ORDER BY
1124        if !self.order_by.is_empty() {
1125            writeln!(query)?;
1126            write!(query, "ORDER BY {}", self.order_by.join(" "))?;
1127        }
1128
1129        // Add LIMIT and OFFSET
1130        if let Some(limit) = self.limit {
1131            writeln!(query)?;
1132            write!(query, "LIMIT {limit}")?;
1133        }
1134
1135        if let Some(offset) = self.offset {
1136            writeln!(query)?;
1137            write!(query, "OFFSET {offset}")?;
1138        }
1139
1140        Ok(())
1141    }
1142
1143    fn build_update_query(&self, query: &mut String) -> Result<()> {
1144        let mut operations = Vec::new();
1145
1146        // Add DELETE DATA operations
1147        if !self.delete_data.is_empty() {
1148            let mut delete_block = String::new();
1149            writeln!(delete_block, "DELETE DATA {{")?;
1150            for triple in &self.delete_data {
1151                writeln!(delete_block, "  {triple}")?;
1152            }
1153            write!(delete_block, "}}")?;
1154            operations.push(delete_block);
1155        }
1156
1157        // Add INSERT DATA operations
1158        if !self.insert_data.is_empty() {
1159            let mut insert_block = String::new();
1160            writeln!(insert_block, "INSERT DATA {{")?;
1161            for triple in &self.insert_data {
1162                writeln!(insert_block, "  {triple}")?;
1163            }
1164            write!(insert_block, "}}")?;
1165            operations.push(insert_block);
1166        }
1167
1168        // Add DELETE WHERE operations
1169        for delete_where in &self.delete_where {
1170            operations.push(format!("DELETE {delete_where}"));
1171        }
1172
1173        // Add INSERT WHERE operations
1174        for insert_where in &self.insert_where {
1175            operations.push(format!("INSERT {insert_where}"));
1176        }
1177
1178        // Join operations with semicolons
1179        write!(query, "{}", operations.join(";\n"))?;
1180
1181        Ok(())
1182    }
1183
1184    /// Add group by clause
1185    #[allow(dead_code)]
1186    fn add_group_by(&mut self, group: &str) {
1187        self.group_by.push(group.to_string());
1188    }
1189
1190    /// Add order by clause
1191    #[allow(dead_code)]
1192    fn add_order_by(&mut self, order: &str) {
1193        self.order_by.push(order.to_string());
1194    }
1195}
1196
1197impl Default for RdfGraphQLMapper {
1198    fn default() -> Self {
1199        Self::new()
1200    }
1201}
1202
1203impl Default for QueryContext {
1204    fn default() -> Self {
1205        Self::new()
1206    }
1207}