oxigraph/sparql/
model.rs

1use crate::io::{RdfFormat, RdfSerializer};
2use crate::model::*;
3use crate::sparql::error::EvaluationError;
4use crate::sparql::results::{
5    QueryResultsFormat, QueryResultsParseError, QueryResultsParser, QueryResultsSerializer,
6    ReaderQueryResultsParserOutput, ReaderSolutionsParser,
7};
8pub use sparesults::QuerySolution;
9use spareval::{
10    QueryEvaluationError, QueryResults as EvalQueryResults,
11    QuerySolutionIter as EvalQuerySolutionIter, QueryTripleIter as EvalQueryTripleIter,
12};
13use std::io::{Read, Write};
14use std::sync::Arc;
15
16/// Results of a [SPARQL query](https://www.w3.org/TR/sparql11-query/).
17pub enum QueryResults {
18    /// Results of a [SELECT](https://www.w3.org/TR/sparql11-query/#select) query.
19    Solutions(QuerySolutionIter),
20    /// Result of a [ASK](https://www.w3.org/TR/sparql11-query/#ask) query.
21    Boolean(bool),
22    /// Results of a [CONSTRUCT](https://www.w3.org/TR/sparql11-query/#construct) or [DESCRIBE](https://www.w3.org/TR/sparql11-query/#describe) query.
23    Graph(QueryTripleIter),
24}
25
26impl QueryResults {
27    /// Reads a SPARQL query results serialization.
28    pub fn read(
29        reader: impl Read + 'static,
30        format: QueryResultsFormat,
31    ) -> Result<Self, QueryResultsParseError> {
32        Ok(QueryResultsParser::from_format(format)
33            .for_reader(reader)?
34            .into())
35    }
36
37    /// Writes the query results (solutions or boolean).
38    ///
39    /// This method fails if it is called on the `Graph` results.
40    ///
41    /// ```
42    /// use oxigraph::store::Store;
43    /// use oxigraph::model::*;
44    /// use oxigraph::sparql::results::QueryResultsFormat;
45    ///
46    /// let store = Store::new()?;
47    /// let ex = NamedNodeRef::new("http://example.com")?;
48    /// store.insert(QuadRef::new(ex, ex, ex, GraphNameRef::DefaultGraph))?;
49    ///
50    /// let results = store.query("SELECT ?s WHERE { ?s ?p ?o }")?;
51    /// assert_eq!(
52    ///     results.write(Vec::new(), QueryResultsFormat::Json)?,
53    ///     r#"{"head":{"vars":["s"]},"results":{"bindings":[{"s":{"type":"uri","value":"http://example.com"}}]}}"#.as_bytes()
54    /// );
55    /// # Result::<_, Box<dyn std::error::Error>>::Ok(())
56    /// ```
57    pub fn write<W: Write>(
58        self,
59        writer: W,
60        format: QueryResultsFormat,
61    ) -> Result<W, EvaluationError> {
62        let serializer = QueryResultsSerializer::from_format(format);
63        match self {
64            Self::Boolean(value) => serializer.serialize_boolean_to_writer(writer, value),
65            Self::Solutions(solutions) => {
66                let mut serializer = serializer
67                    .serialize_solutions_to_writer(writer, solutions.variables().to_vec())
68                    .map_err(EvaluationError::ResultsSerialization)?;
69                for solution in solutions {
70                    serializer
71                        .serialize(&solution?)
72                        .map_err(EvaluationError::ResultsSerialization)?;
73                }
74                serializer.finish()
75            }
76            Self::Graph(triples) => {
77                let s = VariableRef::new_unchecked("subject");
78                let p = VariableRef::new_unchecked("predicate");
79                let o = VariableRef::new_unchecked("object");
80                let mut serializer = serializer
81                    .serialize_solutions_to_writer(
82                        writer,
83                        vec![s.into_owned(), p.into_owned(), o.into_owned()],
84                    )
85                    .map_err(EvaluationError::ResultsSerialization)?;
86                for triple in triples {
87                    let triple = triple?;
88                    serializer
89                        .serialize([
90                            (s, &triple.subject.into()),
91                            (p, &triple.predicate.into()),
92                            (o, &triple.object),
93                        ])
94                        .map_err(EvaluationError::ResultsSerialization)?;
95                }
96                serializer.finish()
97            }
98        }
99        .map_err(EvaluationError::ResultsSerialization)
100    }
101
102    /// Writes the graph query results.
103    ///
104    /// This method fails if it is called on the `Solution` or `Boolean` results.
105    ///
106    /// ```
107    /// use oxigraph::io::RdfFormat;
108    /// use oxigraph::model::*;
109    /// use oxigraph::store::Store;
110    ///
111    /// let graph = "<http://example.com> <http://example.com> <http://example.com> .\n";
112    ///
113    /// let store = Store::new()?;
114    /// store.load_graph(
115    ///     graph.as_bytes(),
116    ///     RdfFormat::NTriples,
117    ///     GraphName::DefaultGraph,
118    ///     None,
119    /// )?;
120    ///
121    /// let results = store.query("CONSTRUCT WHERE { ?s ?p ?o }")?;
122    /// assert_eq!(
123    ///     results.write_graph(Vec::new(), RdfFormat::NTriples)?,
124    ///     graph.as_bytes()
125    /// );
126    /// # Result::<_, Box<dyn std::error::Error>>::Ok(())
127    /// ```
128    pub fn write_graph<W: Write>(
129        self,
130        writer: W,
131        format: impl Into<RdfFormat>,
132    ) -> Result<W, EvaluationError> {
133        if let Self::Graph(triples) = self {
134            let mut serializer = RdfSerializer::from_format(format.into()).for_writer(writer);
135            for triple in triples {
136                serializer
137                    .serialize_triple(&triple?)
138                    .map_err(EvaluationError::ResultsSerialization)?;
139            }
140            serializer
141                .finish()
142                .map_err(EvaluationError::ResultsSerialization)
143        } else {
144            Err(EvaluationError::NotAGraph)
145        }
146    }
147}
148
149impl From<EvalQueryResults> for QueryResults {
150    #[inline]
151    fn from(results: EvalQueryResults) -> Self {
152        match results {
153            EvalQueryResults::Solutions(iter) => Self::Solutions(iter.into()),
154            EvalQueryResults::Boolean(value) => Self::Boolean(value),
155            EvalQueryResults::Graph(iter) => Self::Graph(iter.into()),
156        }
157    }
158}
159
160impl From<QuerySolutionIter> for QueryResults {
161    #[inline]
162    fn from(value: QuerySolutionIter) -> Self {
163        Self::Solutions(value)
164    }
165}
166
167impl<R: Read + 'static> From<ReaderQueryResultsParserOutput<R>> for QueryResults {
168    fn from(reader: ReaderQueryResultsParserOutput<R>) -> Self {
169        match reader {
170            ReaderQueryResultsParserOutput::Solutions(s) => Self::Solutions(s.into()),
171            ReaderQueryResultsParserOutput::Boolean(v) => Self::Boolean(v),
172        }
173    }
174}
175
176/// An iterator over [`QuerySolution`]s.
177///
178/// ```
179/// use oxigraph::sparql::QueryResults;
180/// use oxigraph::store::Store;
181///
182/// let store = Store::new()?;
183/// if let QueryResults::Solutions(solutions) = store.query("SELECT ?s WHERE { ?s ?p ?o }")? {
184///     for solution in solutions {
185///         println!("{:?}", solution?.get("s"));
186///     }
187/// }
188/// # Result::<_, Box<dyn std::error::Error>>::Ok(())
189/// ```
190pub struct QuerySolutionIter {
191    inner: EvalQuerySolutionIter,
192}
193
194impl QuerySolutionIter {
195    /// Construct a new iterator of solution from an ordered list of solution variables and an iterator of solution tuples
196    /// (each tuple using the same ordering as the variable list such that tuple element 0 is the value for the variable 0...)
197    pub fn new(
198        variables: Arc<[Variable]>,
199        iter: impl Iterator<Item = Result<Vec<Option<Term>>, EvaluationError>> + 'static,
200    ) -> Self {
201        Self {
202            inner: EvalQuerySolutionIter::new(
203                Arc::clone(&variables),
204                Box::new(iter.map(move |t| match t {
205                    Ok(values) => Ok((Arc::clone(&variables), values).into()),
206                    Err(e) => Err(QueryEvaluationError::Service(Box::new(e))),
207                })),
208            ),
209        }
210    }
211
212    /// The variables used in the solutions.
213    ///
214    /// ```
215    /// use oxigraph::sparql::{QueryResults, Variable};
216    /// use oxigraph::store::Store;
217    ///
218    /// let store = Store::new()?;
219    /// if let QueryResults::Solutions(solutions) = store.query("SELECT ?s ?o WHERE { ?s ?p ?o }")? {
220    ///     assert_eq!(
221    ///         solutions.variables(),
222    ///         &[Variable::new("s")?, Variable::new("o")?]
223    ///     );
224    /// }
225    /// # Result::<_, Box<dyn std::error::Error>>::Ok(())
226    /// ```
227    #[inline]
228    pub fn variables(&self) -> &[Variable] {
229        self.inner.variables()
230    }
231}
232
233impl From<EvalQuerySolutionIter> for QuerySolutionIter {
234    #[inline]
235    fn from(inner: EvalQuerySolutionIter) -> Self {
236        Self { inner }
237    }
238}
239
240impl From<QuerySolutionIter> for EvalQuerySolutionIter {
241    #[inline]
242    fn from(iter: QuerySolutionIter) -> Self {
243        iter.inner
244    }
245}
246
247impl<R: Read + 'static> From<ReaderSolutionsParser<R>> for QuerySolutionIter {
248    fn from(reader: ReaderSolutionsParser<R>) -> Self {
249        Self {
250            inner: EvalQuerySolutionIter::new(
251                reader.variables().into(),
252                Box::new(reader.map(|t| t.map_err(|e| QueryEvaluationError::Service(Box::new(e))))),
253            ),
254        }
255    }
256}
257
258impl Iterator for QuerySolutionIter {
259    type Item = Result<QuerySolution, EvaluationError>;
260
261    #[inline]
262    fn next(&mut self) -> Option<Self::Item> {
263        Some(self.inner.next()?.map_err(Into::into))
264    }
265
266    #[inline]
267    fn size_hint(&self) -> (usize, Option<usize>) {
268        self.inner.size_hint()
269    }
270}
271
272/// An iterator over the triples that compose a graph solution.
273///
274/// ```
275/// use oxigraph::sparql::QueryResults;
276/// use oxigraph::store::Store;
277///
278/// let store = Store::new()?;
279/// if let QueryResults::Graph(triples) = store.query("CONSTRUCT WHERE { ?s ?p ?o }")? {
280///     for triple in triples {
281///         println!("{}", triple?);
282///     }
283/// }
284/// # Result::<_, Box<dyn std::error::Error>>::Ok(())
285/// ```
286pub struct QueryTripleIter {
287    inner: EvalQueryTripleIter,
288}
289
290impl From<EvalQueryTripleIter> for QueryTripleIter {
291    #[inline]
292    fn from(inner: EvalQueryTripleIter) -> Self {
293        Self { inner }
294    }
295}
296
297impl From<QueryTripleIter> for EvalQueryTripleIter {
298    #[inline]
299    fn from(iter: QueryTripleIter) -> Self {
300        iter.inner
301    }
302}
303
304impl Iterator for QueryTripleIter {
305    type Item = Result<Triple, EvaluationError>;
306
307    #[inline]
308    fn next(&mut self) -> Option<Self::Item> {
309        Some(self.inner.next()?.map_err(Into::into))
310    }
311
312    #[inline]
313    fn size_hint(&self) -> (usize, Option<usize>) {
314        self.inner.size_hint()
315    }
316}
317
318#[cfg(test)]
319#[allow(clippy::panic_in_result_fn)]
320mod tests {
321    use super::*;
322    use std::io::Cursor;
323
324    #[test]
325    fn test_send_sync() {
326        fn is_send_sync<T: Send + Sync>() {}
327        is_send_sync::<QuerySolution>();
328    }
329
330    #[test]
331    fn test_serialization_roundtrip() -> Result<(), EvaluationError> {
332        use std::str;
333
334        for format in [
335            QueryResultsFormat::Json,
336            QueryResultsFormat::Xml,
337            QueryResultsFormat::Tsv,
338        ] {
339            let results = vec![
340                QueryResults::Boolean(true),
341                QueryResults::Boolean(false),
342                QueryResults::Solutions(QuerySolutionIter::new(
343                    [
344                        Variable::new_unchecked("foo"),
345                        Variable::new_unchecked("bar"),
346                    ]
347                    .as_ref()
348                    .into(),
349                    Box::new(
350                        vec![
351                            Ok(vec![None, None]),
352                            Ok(vec![
353                                Some(NamedNode::new_unchecked("http://example.com").into()),
354                                None,
355                            ]),
356                            Ok(vec![
357                                None,
358                                Some(NamedNode::new_unchecked("http://example.com").into()),
359                            ]),
360                            Ok(vec![
361                                Some(BlankNode::new_unchecked("foo").into()),
362                                Some(BlankNode::new_unchecked("bar").into()),
363                            ]),
364                            Ok(vec![Some(Literal::new_simple_literal("foo").into()), None]),
365                            Ok(vec![
366                                Some(
367                                    Literal::new_language_tagged_literal_unchecked("foo", "fr")
368                                        .into(),
369                                ),
370                                None,
371                            ]),
372                            Ok(vec![
373                                Some(Literal::from(1).into()),
374                                Some(Literal::from(true).into()),
375                            ]),
376                            Ok(vec![
377                                Some(Literal::from(1.33).into()),
378                                Some(Literal::from(false).into()),
379                            ]),
380                            Ok(vec![
381                                Some(
382                                    Triple::new(
383                                        NamedNode::new_unchecked("http://example.com/s"),
384                                        NamedNode::new_unchecked("http://example.com/p"),
385                                        Triple::new(
386                                            NamedNode::new_unchecked("http://example.com/os"),
387                                            NamedNode::new_unchecked("http://example.com/op"),
388                                            NamedNode::new_unchecked("http://example.com/oo"),
389                                        ),
390                                    )
391                                    .into(),
392                                ),
393                                None,
394                            ]),
395                        ]
396                        .into_iter(),
397                    ),
398                )),
399            ];
400
401            for ex in results {
402                let mut buffer = Vec::new();
403                ex.write(&mut buffer, format)?;
404                let ex2 = QueryResults::read(Cursor::new(buffer.clone()), format)?;
405                let mut buffer2 = Vec::new();
406                ex2.write(&mut buffer2, format)?;
407                assert_eq!(
408                    str::from_utf8(&buffer).unwrap(),
409                    str::from_utf8(&buffer2).unwrap()
410                );
411            }
412        }
413
414        Ok(())
415    }
416}