Renames some sparql module element and improves documentation

QueryResult -> QueryResults
QueryResultFormat -> QueryResultsFormat
QuerySolutionsIterator -> QuerySolutionIter
QueryTriplesIterator -> QueryTripleIter
pull/46/head
Tpt 4 years ago
parent e787eb69f5
commit 6b3062f496
  1. 5
      README.md
  2. 6
      js/README.md
  3. 8
      js/src/store.rs
  4. 20
      lib/src/io/read.rs
  5. 26
      lib/src/io/write.rs
  6. 6
      lib/src/lib.rs
  7. 22
      lib/src/model/blank_node.rs
  8. 6
      lib/src/model/literal.rs
  9. 4
      lib/src/model/named_node.rs
  10. 2
      lib/src/model/vocab.rs
  11. 26
      lib/src/sparql/eval.rs
  12. 8
      lib/src/sparql/json_results.rs
  13. 24
      lib/src/sparql/mod.rs
  14. 104
      lib/src/sparql/model.rs
  15. 57
      lib/src/sparql/xml_results.rs
  16. 68
      lib/src/store/memory.rs
  17. 5
      lib/src/store/mod.rs
  18. 14
      lib/src/store/rocksdb.rs
  19. 14
      lib/src/store/sled.rs
  20. 2
      python/src/memory_store.rs
  21. 2
      python/src/sled_store.rs
  22. 26
      python/src/store_utils.rs
  23. 18
      server/src/main.rs
  24. 16
      testsuite/src/sparql_evaluator.rs
  25. 18
      wikibase/src/main.rs

@ -4,10 +4,9 @@ Oxigraph
[![actions status](https://github.com/oxigraph/oxigraph/workflows/build/badge.svg)](https://github.com/oxigraph/oxigraph/actions) [![actions status](https://github.com/oxigraph/oxigraph/workflows/build/badge.svg)](https://github.com/oxigraph/oxigraph/actions)
[![Gitter](https://badges.gitter.im/oxigraph/community.svg)](https://gitter.im/oxigraph/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) [![Gitter](https://badges.gitter.im/oxigraph/community.svg)](https://gitter.im/oxigraph/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
Oxigraph is a work in progress graph database implementing the [SPARQL](https://www.w3.org/TR/sparql11-overview/) standard. Oxigraph is a graph database implementing the [SPARQL](https://www.w3.org/TR/sparql11-overview/) standard.
There is no released version yet. There is no released version yet.
The storage format is not stable yet and may be at any time.
Its goal is to provide a compliant, safe and fast graph database based on the [RocksDB](https://rocksdb.org/) and [Sled](https://sled.rs/) key-value stores. Its goal is to provide a compliant, safe and fast graph database based on the [RocksDB](https://rocksdb.org/) and [Sled](https://sled.rs/) key-value stores.
It is written in Rust. It is written in Rust.
@ -20,7 +19,7 @@ It is split into multiple parts:
* The `server` directory contains a stand-alone binary of a web server implementing the [SPARQL 1.1 Protocol](https://www.w3.org/TR/sparql11-protocol/). It uses the [RocksDB](https://rocksdb.org/) key-value store. * The `server` directory contains a stand-alone binary of a web server implementing the [SPARQL 1.1 Protocol](https://www.w3.org/TR/sparql11-protocol/). It uses the [RocksDB](https://rocksdb.org/) key-value store.
* The `wikibase` directory contains a stand-alone binary of a web server able to synchronize with a [Wikibase instance](https://wikiba.se/). * The `wikibase` directory contains a stand-alone binary of a web server able to synchronize with a [Wikibase instance](https://wikiba.se/).
Are currently implemented: Oxigraph implements the following specifications:
* [SPARQL 1.1 Query](https://www.w3.org/TR/sparql11-query/). * [SPARQL 1.1 Query](https://www.w3.org/TR/sparql11-query/).
* [SPARQL 1.1 Federated Query](https://www.w3.org/TR/sparql11-federated-query/). * [SPARQL 1.1 Federated Query](https://www.w3.org/TR/sparql11-federated-query/).
* [Turtle](https://www.w3.org/TR/turtle/), [TriG](https://www.w3.org/TR/trig/), [N-Triples](https://www.w3.org/TR/n-triples/), [N-Quads](https://www.w3.org/TR/n-quads/) and [RDF XML](https://www.w3.org/TR/rdf-syntax-grammar/) RDF serialization formats for both data ingestion and retrieval using the [Rio library](https://github.com/oxigraph/rio). * [Turtle](https://www.w3.org/TR/turtle/), [TriG](https://www.w3.org/TR/trig/), [N-Triples](https://www.w3.org/TR/n-triples/), [N-Quads](https://www.w3.org/TR/n-quads/) and [RDF XML](https://www.w3.org/TR/rdf-syntax-grammar/) RDF serialization formats for both data ingestion and retrieval using the [Rio library](https://github.com/oxigraph/rio).

@ -7,9 +7,9 @@ Oxigraph for JavaScript
This package provides a JavaScript API on top of Oxigraph compiled with WebAssembly. This package provides a JavaScript API on top of Oxigraph compiled with WebAssembly.
Oxigraph is a work in progress graph database written in Rust implementing the [SPARQL](https://www.w3.org/TR/sparql11-overview/) standard. Oxigraph is a graph database written in Rust implementing the [SPARQL](https://www.w3.org/TR/sparql11-overview/) standard.
It is a work in progress and currently offers a simple in-memory store with [SPARQL 1.1 Query](https://www.w3.org/TR/sparql11-query/) capabilities. Oxigraph or JavaScript is a work in progress and currently offers a simple in-memory store with [SPARQL 1.1 Query](https://www.w3.org/TR/sparql11-query/) capabilities.
The store is also able to load RDF serialized in [Turtle](https://www.w3.org/TR/turtle/), [TriG](https://www.w3.org/TR/trig/), [N-Triples](https://www.w3.org/TR/n-triples/), [N-Quads](https://www.w3.org/TR/n-quads/) and [RDF/XML](https://www.w3.org/TR/rdf-syntax-grammar/). The store is also able to load RDF serialized in [Turtle](https://www.w3.org/TR/turtle/), [TriG](https://www.w3.org/TR/trig/), [N-Triples](https://www.w3.org/TR/n-triples/), [N-Quads](https://www.w3.org/TR/n-quads/) and [RDF/XML](https://www.w3.org/TR/rdf-syntax-grammar/).
@ -163,4 +163,4 @@ The Oxigraph bindings are written in Rust using [the Rust WASM toolkit](https://
The [The Rust Wasm Book](https://rustwasm.github.io/docs/book/) is a great tutorial to get started. The [The Rust Wasm Book](https://rustwasm.github.io/docs/book/) is a great tutorial to get started.
To build the JavaScript bindings, just run `wasm-pack build`, to run the tests of the JS bindings written in JS just do a usual `npm test`. To build the JavaScript bindings, run `wasm-pack build`, to run the tests of the JS bindings written in JS run `npm test`.

@ -4,7 +4,7 @@ use crate::utils::to_err;
use js_sys::{Array, Map}; use js_sys::{Array, Map};
use oxigraph::io::{DatasetFormat, GraphFormat}; use oxigraph::io::{DatasetFormat, GraphFormat};
use oxigraph::model::*; use oxigraph::model::*;
use oxigraph::sparql::{QueryOptions, QueryResult}; use oxigraph::sparql::{QueryOptions, QueryResults};
use oxigraph::MemoryStore; use oxigraph::MemoryStore;
use std::convert::{TryFrom, TryInto}; use std::convert::{TryFrom, TryInto};
use std::io::Cursor; use std::io::Cursor;
@ -110,7 +110,7 @@ impl JsMemoryStore {
.query(query, QueryOptions::default()) .query(query, QueryOptions::default())
.map_err(to_err)?; .map_err(to_err)?;
let output = match results { let output = match results {
QueryResult::Solutions(solutions) => { QueryResults::Solutions(solutions) => {
let results = Array::new(); let results = Array::new();
for solution in solutions { for solution in solutions {
let solution = solution.map_err(to_err)?; let solution = solution.map_err(to_err)?;
@ -125,14 +125,14 @@ impl JsMemoryStore {
} }
results.into() results.into()
} }
QueryResult::Graph(quads) => { QueryResults::Graph(quads) => {
let results = Array::new(); let results = Array::new();
for quad in quads { for quad in quads {
results.push(&JsQuad::from(quad.map_err(to_err)?.in_graph(None)).into()); results.push(&JsQuad::from(quad.map_err(to_err)?.in_graph(None)).into());
} }
results.into() results.into()
} }
QueryResult::Boolean(b) => b.into(), QueryResults::Boolean(b) => b.into(),
}; };
Ok(output) Ok(output)
} }

@ -14,9 +14,9 @@ use std::io::BufRead;
/// Parsers for RDF graph serialization formats. /// Parsers for RDF graph serialization formats.
/// ///
/// It currently supports the following formats: /// It currently supports the following formats:
/// * [N-Triples](https://www.w3.org/TR/n-triples/) (`GraphFormat::NTriples`) /// * [N-Triples](https://www.w3.org/TR/n-triples/) ([`GraphFormat::NTriples`](../enum.GraphFormat.html#variant.NTriples))
/// * [Turtle](https://www.w3.org/TR/turtle/) (`GraphFormat::Turtle`) /// * [Turtle](https://www.w3.org/TR/turtle/) ([`GraphFormat::Turtle`](../enum.GraphFormat.html#variant.Turtle))
/// * [RDF/XML](https://www.w3.org/TR/rdf-syntax-grammar/) (`GraphFormat::RdfXml`) /// * [RDF/XML](https://www.w3.org/TR/rdf-syntax-grammar/) ([`GraphFormat::RdfXml`](../enum.GraphFormat.html#variant.RdfXml))
/// ///
/// ``` /// ```
/// use oxigraph::io::{GraphFormat, GraphParser}; /// use oxigraph::io::{GraphFormat, GraphParser};
@ -37,6 +37,7 @@ pub struct GraphParser {
} }
impl GraphParser { impl GraphParser {
/// Builds a parser for the given format
pub fn from_format(format: GraphFormat) -> Self { pub fn from_format(format: GraphFormat) -> Self {
Self { Self {
format, format,
@ -64,7 +65,7 @@ impl GraphParser {
Ok(self) Ok(self)
} }
/// Executes the parsing itself /// Executes the parsing itself on a [`BufRead`](https://doc.rust-lang.org/std/io/trait.BufRead.html) implementation and returns an iterator of triples
pub fn read_triples<R: BufRead>(&self, reader: R) -> Result<TripleReader<R>, io::Error> { pub fn read_triples<R: BufRead>(&self, reader: R) -> Result<TripleReader<R>, io::Error> {
Ok(TripleReader { Ok(TripleReader {
mapper: RioMapper::default(), mapper: RioMapper::default(),
@ -82,7 +83,7 @@ impl GraphParser {
} }
} }
/// Allows reading triples. /// An iterator yielding read triples.
/// Could be built using a [`GraphParser`](struct.GraphParser.html). /// Could be built using a [`GraphParser`](struct.GraphParser.html).
/// ///
/// ``` /// ```
@ -162,8 +163,8 @@ impl<R: BufRead> TripleReader<R> {
/// A parser for RDF dataset serialization formats. /// A parser for RDF dataset serialization formats.
/// ///
/// It currently supports the following formats: /// It currently supports the following formats:
/// * [N-Quads](https://www.w3.org/TR/n-quads/) (`DatasetFormat::NQuads`) /// * [N-Quads](https://www.w3.org/TR/n-quads/) ([`DatasetFormat::NQuads`](../enum.DatasetFormat.html#variant.NQuads))
/// * [TriG](https://www.w3.org/TR/trig/) (`DatasetFormat::TriG`) /// * [TriG](https://www.w3.org/TR/trig/) ([`DatasetFormat::TriG`](../enum.DatasetFormat.html#variant.TriG))
/// ///
/// ``` /// ```
/// use oxigraph::io::{DatasetFormat, DatasetParser}; /// use oxigraph::io::{DatasetFormat, DatasetParser};
@ -184,6 +185,7 @@ pub struct DatasetParser {
} }
impl DatasetParser { impl DatasetParser {
/// Builds a parser for the given format
pub fn from_format(format: DatasetFormat) -> Self { pub fn from_format(format: DatasetFormat) -> Self {
Self { Self {
format, format,
@ -211,7 +213,7 @@ impl DatasetParser {
Ok(self) Ok(self)
} }
/// Executes the parsing itself /// Executes the parsing itself on a [`BufRead`](https://doc.rust-lang.org/std/io/trait.BufRead.html) implementation and returns an iterator of quads
pub fn read_quads<R: BufRead>(&self, reader: R) -> Result<QuadReader<R>, io::Error> { pub fn read_quads<R: BufRead>(&self, reader: R) -> Result<QuadReader<R>, io::Error> {
Ok(QuadReader { Ok(QuadReader {
mapper: RioMapper::default(), mapper: RioMapper::default(),
@ -226,7 +228,7 @@ impl DatasetParser {
} }
} }
/// Allows reading quads. /// An iterator yielding read quads.
/// Could be built using a [`DatasetParser`](struct.DatasetParser.html). /// Could be built using a [`DatasetParser`](struct.DatasetParser.html).
/// ///
/// ``` /// ```

@ -11,9 +11,9 @@ use std::io::Write;
/// A serializer for RDF graph serialization formats. /// A serializer for RDF graph serialization formats.
/// ///
/// It currently supports the following formats: /// It currently supports the following formats:
/// * [N-Triples](https://www.w3.org/TR/n-triples/) (`GraphFormat::NTriples`) /// * [N-Triples](https://www.w3.org/TR/n-triples/) ([`GraphFormat::NTriples`](../enum.GraphFormat.html#variant.NTriples))
/// * [Turtle](https://www.w3.org/TR/turtle/) (`GraphFormat::Turtle`) /// * [Turtle](https://www.w3.org/TR/turtle/) ([`GraphFormat::Turtle`](../enum.GraphFormat.html#variant.Turtle))
/// * [RDF/XML](https://www.w3.org/TR/rdf-syntax-grammar/) (`GraphFormat::RdfXml`) /// * [RDF/XML](https://www.w3.org/TR/rdf-syntax-grammar/) ([`GraphFormat::RdfXml`](../enum.GraphFormat.html#variant.RdfXml))
/// ///
/// ``` /// ```
/// use oxigraph::io::{GraphFormat, GraphSerializer}; /// use oxigraph::io::{GraphFormat, GraphSerializer};
@ -37,11 +37,12 @@ pub struct GraphSerializer {
} }
impl GraphSerializer { impl GraphSerializer {
/// Builds a serializer for the given format
pub fn from_format(format: GraphFormat) -> Self { pub fn from_format(format: GraphFormat) -> Self {
Self { format } Self { format }
} }
/// Returns a `TripleWriter` allowing writing triples into the given `Write` implementation /// Returns a `TripleWriter` allowing writing triples into the given [`Write`](https://doc.rust-lang.org/std/io/trait.Write.html) implementation
pub fn triple_writer<W: Write>(&self, writer: W) -> Result<TripleWriter<W>, io::Error> { pub fn triple_writer<W: Write>(&self, writer: W) -> Result<TripleWriter<W>, io::Error> {
Ok(TripleWriter { Ok(TripleWriter {
formatter: match self.format { formatter: match self.format {
@ -54,9 +55,9 @@ impl GraphSerializer {
} }
/// Allows writing triples. /// Allows writing triples.
/// Could be built using a `GraphSerializer`. /// Could be built using a [`GraphSerializer`](struct.GraphSerializer.html).
/// ///
/// Warning: Do not forget to run the `finish` method to properly write the last bytes of the file. /// Warning: Do not forget to run the [`finish`](#method.finish) method to properly write the last bytes of the file.
/// ///
/// ``` /// ```
/// use oxigraph::io::{GraphFormat, GraphSerializer}; /// use oxigraph::io::{GraphFormat, GraphSerializer};
@ -86,6 +87,7 @@ enum TripleWriterKind<W: Write> {
} }
impl<W: Write> TripleWriter<W> { impl<W: Write> TripleWriter<W> {
/// Writes a triple
pub fn write<'a>(&mut self, triple: impl Into<TripleRef<'a>>) -> Result<(), io::Error> { pub fn write<'a>(&mut self, triple: impl Into<TripleRef<'a>>) -> Result<(), io::Error> {
let triple = triple.into(); let triple = triple.into();
match &mut self.formatter { match &mut self.formatter {
@ -110,8 +112,8 @@ impl<W: Write> TripleWriter<W> {
/// A serializer for RDF graph serialization formats. /// A serializer for RDF graph serialization formats.
/// ///
/// It currently supports the following formats: /// It currently supports the following formats:
/// * [N-Quads](https://www.w3.org/TR/n-quads/) (`DatasetFormat::NQuads`) /// * [N-Quads](https://www.w3.org/TR/n-quads/) ([`DatasetFormat::NQuads`](../enum.DatasetFormat.html#variant.NQuads))
/// * [TriG](https://www.w3.org/TR/trig/) (`DatasetFormat::TriG`) /// * [TriG](https://www.w3.org/TR/trig/) ([`DatasetFormat::TriG`](../enum.DatasetFormat.html#variant.TriG))
/// ///
/// ``` /// ```
/// use oxigraph::io::{DatasetFormat, DatasetSerializer}; /// use oxigraph::io::{DatasetFormat, DatasetSerializer};
@ -136,11 +138,12 @@ pub struct DatasetSerializer {
} }
impl DatasetSerializer { impl DatasetSerializer {
/// Builds a serializer for the given format
pub fn from_format(format: DatasetFormat) -> Self { pub fn from_format(format: DatasetFormat) -> Self {
Self { format } Self { format }
} }
/// Returns a `QuadWriter` allowing writing triples into the given `Write` implementation /// Returns a `QuadWriter` allowing writing triples into the given [`Write`](https://doc.rust-lang.org/std/io/trait.Write.html) implementation
pub fn quad_writer<W: Write>(&self, writer: W) -> Result<QuadWriter<W>, io::Error> { pub fn quad_writer<W: Write>(&self, writer: W) -> Result<QuadWriter<W>, io::Error> {
Ok(QuadWriter { Ok(QuadWriter {
formatter: match self.format { formatter: match self.format {
@ -152,9 +155,9 @@ impl DatasetSerializer {
} }
/// Allows writing triples. /// Allows writing triples.
/// Could be built using a `DatasetSerializer`. /// Could be built using a [`DatasetSerializer`](struct.DatasetSerializer.html).
/// ///
/// Warning: Do not forget to run the `finish` method to properly write the last bytes of the file. /// Warning: Do not forget to run the [`finish`](#method.finish) method to properly write the last bytes of the file.
/// ///
/// ``` /// ```
/// use oxigraph::io::{DatasetFormat, DatasetSerializer}; /// use oxigraph::io::{DatasetFormat, DatasetSerializer};
@ -184,6 +187,7 @@ enum QuadWriterKind<W: Write> {
} }
impl<W: Write> QuadWriter<W> { impl<W: Write> QuadWriter<W> {
/// Writes a quad
pub fn write<'a>(&mut self, quad: impl Into<QuadRef<'a>>) -> Result<(), io::Error> { pub fn write<'a>(&mut self, quad: impl Into<QuadRef<'a>>) -> Result<(), io::Error> {
let quad = quad.into(); let quad = quad.into();
match &mut self.formatter { match &mut self.formatter {

@ -1,4 +1,4 @@
//! Oxigraph is a work in progress graph database implementing the [SPARQL](https://www.w3.org/TR/sparql11-overview/) standard. //! Oxigraph is a graph database implementing the [SPARQL](https://www.w3.org/TR/sparql11-overview/) standard.
//! //!
//! Its goal is to provide a compliant, safe and fast graph database. //! Its goal is to provide a compliant, safe and fast graph database.
//! //!
@ -19,7 +19,7 @@
//! ``` //! ```
//! use oxigraph::MemoryStore; //! use oxigraph::MemoryStore;
//! use oxigraph::model::*; //! use oxigraph::model::*;
//! use oxigraph::sparql::{QueryOptions, QueryResult}; //! use oxigraph::sparql::{QueryOptions, QueryResults};
//! //!
//! let store = MemoryStore::new(); //! let store = MemoryStore::new();
//! //!
@ -33,7 +33,7 @@
//! assert_eq!(vec![quad], results); //! assert_eq!(vec![quad], results);
//! //!
//! // SPARQL query //! // SPARQL query
//! if let QueryResult::Solutions(mut solutions) = store.query("SELECT ?s WHERE { ?s ?p ?o }", QueryOptions::default())? { //! if let QueryResults::Solutions(mut solutions) = store.query("SELECT ?s WHERE { ?s ?p ?o }", QueryOptions::default())? {
//! assert_eq!(solutions.next().unwrap()?.get("s"), Some(&ex.into())); //! assert_eq!(solutions.next().unwrap()?.get("s"), Some(&ex.into()));
//! } //! }
//! # Result::<_,Box<dyn std::error::Error>>::Ok(()) //! # Result::<_,Box<dyn std::error::Error>>::Ok(())

@ -7,9 +7,9 @@ use std::str;
/// An owned RDF [blank node](https://www.w3.org/TR/rdf11-concepts/#dfn-blank-node). /// An owned RDF [blank node](https://www.w3.org/TR/rdf11-concepts/#dfn-blank-node).
/// ///
/// The common way to create a new blank node is to use the `BlankNode::default` trait method. /// The common way to create a new blank node is to use the [`BlankNode::default`](#impl-Default) function.
/// ///
/// It is also possible to create a blank node from a blank node identifier using the `BlankNode::new` method. /// It is also possible to create a blank node from a blank node identifier using the [`BlankNode::new`](#method.new) function.
/// The blank node identifier must be valid according to N-Triples, Turtle and SPARQL grammars. /// The blank node identifier must be valid according to N-Triples, Turtle and SPARQL grammars.
/// ///
/// The default string formatter is returning a N-Triples, Turtle and SPARQL compatible representation: /// The default string formatter is returning a N-Triples, Turtle and SPARQL compatible representation:
@ -36,8 +36,8 @@ impl BlankNode {
/// ///
/// The blank node identifier must be valid according to N-Triples, Turtle and SPARQL grammars. /// The blank node identifier must be valid according to N-Triples, Turtle and SPARQL grammars.
/// ///
/// In most cases, it is much more convenient to create a blank node using `BlankNode::default()`. /// In most cases, it is much more convenient to create a blank node using [`BlankNode::default()`](#impl-Default)
/// `BlankNode::default()` creates a random ID that could be easily inlined by Oxigraph stores. ///that creates a random ID that could be easily inlined by Oxigraph stores.
pub fn new(id: impl Into<String>) -> Result<Self, BlankNodeIdParseError> { pub fn new(id: impl Into<String>) -> Result<Self, BlankNodeIdParseError> {
let id = id.into(); let id = id.into();
validate_blank_node_identifier(&id)?; validate_blank_node_identifier(&id)?;
@ -49,7 +49,7 @@ impl BlankNode {
/// It is the caller's responsibility to ensure that `id` is a valid blank node identifier /// It is the caller's responsibility to ensure that `id` is a valid blank node identifier
/// according to N-Triples, Turtle and SPARQL grammars. /// according to N-Triples, Turtle and SPARQL grammars.
/// ///
/// Except if you really know what you do, you should use [`new`](#method.new). /// [`new`](#method.new) is a safe version of this constructor and should be used for untrusted data.
pub fn new_unchecked(id: impl Into<String>) -> Self { pub fn new_unchecked(id: impl Into<String>) -> Self {
let id = id.into(); let id = id.into();
if let Some(numerical_id) = to_integer_id(&id) { if let Some(numerical_id) = to_integer_id(&id) {
@ -61,7 +61,7 @@ impl BlankNode {
/// Creates a blank node from a unique numerical id /// Creates a blank node from a unique numerical id
/// ///
/// In most cases, it is much more convenient to create a blank node using `BlankNode::default()`. /// In most cases, it is much more convenient to create a blank node using [`BlankNode::default()`](#impl-Default).
pub fn new_from_unique_id(id: impl Into<u128>) -> Self { pub fn new_from_unique_id(id: impl Into<u128>) -> Self {
let id = id.into(); let id = id.into();
Self(BlankNodeContent::Anonymous { Self(BlankNodeContent::Anonymous {
@ -117,9 +117,9 @@ impl Default for BlankNode {
/// A borrowed RDF [blank node](https://www.w3.org/TR/rdf11-concepts/#dfn-blank-node). /// A borrowed RDF [blank node](https://www.w3.org/TR/rdf11-concepts/#dfn-blank-node).
/// ///
/// The common way to create a new blank node is to use the `BlankNode::default` trait method. /// The common way to create a new blank node is to use the [`BlankNode::default`](#impl-Default) trait method.
/// ///
/// It is also possible to create a blank node from a blank node identifier using the `BlankNodeRef::new` method. /// It is also possible to create a blank node from a blank node identifier using the [`BlankNodeRef::new`](#method.new) function.
/// The blank node identifier must be valid according to N-Triples, Turtle and SPARQL grammars. /// The blank node identifier must be valid according to N-Triples, Turtle and SPARQL grammars.
/// ///
/// The default string formatter is returning a N-Triples, Turtle and SPARQL compatible representation: /// The default string formatter is returning a N-Triples, Turtle and SPARQL compatible representation:
@ -146,8 +146,8 @@ impl<'a> BlankNodeRef<'a> {
/// ///
/// The blank node identifier must be valid according to N-Triples, Turtle and SPARQL grammars. /// The blank node identifier must be valid according to N-Triples, Turtle and SPARQL grammars.
/// ///
/// In most cases, it is much more convenient to create a blank node using `BlankNode::default()`. /// In most cases, it is much more convenient to create a blank node using [`BlankNode::default()`](#impl-Default)
/// `BlankNode::default()` creates a random ID that could be easily inlined by Oxigraph stores. /// that creates a random ID that could be easily inlined by Oxigraph stores.
pub fn new(id: &'a str) -> Result<Self, BlankNodeIdParseError> { pub fn new(id: &'a str) -> Result<Self, BlankNodeIdParseError> {
validate_blank_node_identifier(id)?; validate_blank_node_identifier(id)?;
Ok(Self::new_unchecked(id)) Ok(Self::new_unchecked(id))
@ -158,7 +158,7 @@ impl<'a> BlankNodeRef<'a> {
/// It is the caller's responsibility to ensure that `id` is a valid blank node identifier /// It is the caller's responsibility to ensure that `id` is a valid blank node identifier
/// according to N-Triples, Turtle and SPARQL grammars. /// according to N-Triples, Turtle and SPARQL grammars.
/// ///
/// Except if you really know what you do, you should use [`new`](#method.new). /// [`new`](#method.new) is a safe version of this constructor and should be used for untrusted data.
pub fn new_unchecked(id: &'a str) -> Self { pub fn new_unchecked(id: &'a str) -> Self {
if let Some(numerical_id) = to_integer_id(id) { if let Some(numerical_id) = to_integer_id(id) {
Self(BlankNodeRefContent::Anonymous { Self(BlankNodeRefContent::Anonymous {

@ -82,8 +82,7 @@ impl Literal {
/// is valid [BCP47](https://tools.ietf.org/html/bcp47) language tag, /// is valid [BCP47](https://tools.ietf.org/html/bcp47) language tag,
/// and is lowercase. /// and is lowercase.
/// ///
/// Except if you really know what you do, /// [`new_language_tagged_literal`](#method.new_language_tagged_literal) is a safe version of this constructor and should be used for untrusted data.
/// you should use [`new_language_tagged_literal`](#method.new_language_tagged_literal).
#[inline] #[inline]
pub fn new_language_tagged_literal_unchecked( pub fn new_language_tagged_literal_unchecked(
value: impl Into<String>, value: impl Into<String>,
@ -462,8 +461,7 @@ impl<'a> LiteralRef<'a> {
/// is valid [BCP47](https://tools.ietf.org/html/bcp47) language tag, /// is valid [BCP47](https://tools.ietf.org/html/bcp47) language tag,
/// and is lowercase. /// and is lowercase.
/// ///
/// Except if you really know what you do, /// [`new_language_tagged_literal`](#method.new_language_tagged_literal) is a safe version of this constructor and should be used for untrusted data.
/// you should use [`new_language_tagged_literal`](#method.new_language_tagged_literal).
#[inline] #[inline]
pub fn new_language_tagged_literal_unchecked(value: &'a str, language: &'a str) -> Self { pub fn new_language_tagged_literal_unchecked(value: &'a str, language: &'a str) -> Self {
LiteralRef(LiteralRefContent::LanguageTaggedString { value, language }) LiteralRef(LiteralRefContent::LanguageTaggedString { value, language })

@ -34,7 +34,7 @@ impl NamedNode {
/// ///
/// It is the caller's responsibility to ensure that `iri` is a valid IRI. /// It is the caller's responsibility to ensure that `iri` is a valid IRI.
/// ///
/// Except if you really know what you do, you should use [`parse`](#method.parse). /// [`parse`](#method.parse) is a safe version of this constructor and should be used for untrusted data.
#[inline] #[inline]
pub fn new_unchecked(iri: impl Into<String>) -> Self { pub fn new_unchecked(iri: impl Into<String>) -> Self {
Self { iri: iri.into() } Self { iri: iri.into() }
@ -123,7 +123,7 @@ impl<'a> NamedNodeRef<'a> {
/// ///
/// It is the caller's responsibility to ensure that `iri` is a valid IRI. /// It is the caller's responsibility to ensure that `iri` is a valid IRI.
/// ///
/// Except if you really know what you do, you should use [`parse`](#method.parse). /// [`parse`](#method.parse) is a safe version of this constructor and should be used for untrusted data.
#[inline] #[inline]
pub const fn new_unchecked(iri: &'a str) -> Self { pub const fn new_unchecked(iri: &'a str) -> Self {
Self { iri } Self { iri }

@ -1,7 +1,7 @@
//! Provides ready to use [`NamedNodeRef`s](struct.NamedNodeRef.html) for basic RDF vocabularies //! Provides ready to use [`NamedNodeRef`s](struct.NamedNodeRef.html) for basic RDF vocabularies
pub mod rdf { pub mod rdf {
//! [RDF 1.1](https://www.w3.org/TR/rdf11-concepts/) vocabulary //! [RDF](https://www.w3.org/TR/rdf11-concepts/) vocabulary
use crate::model::named_node::NamedNodeRef; use crate::model::named_node::NamedNodeRef;
/// The class of containers of alternatives. /// The class of containers of alternatives.

@ -70,9 +70,9 @@ where
&self, &self,
plan: &PlanNode<S::StrId>, plan: &PlanNode<S::StrId>,
variables: Rc<Vec<Variable>>, variables: Rc<Vec<Variable>>,
) -> Result<QueryResult, EvaluationError> { ) -> Result<QueryResults, EvaluationError> {
let iter = self.eval_plan(plan, EncodedTuple::with_capacity(variables.len())); let iter = self.eval_plan(plan, EncodedTuple::with_capacity(variables.len()));
Ok(QueryResult::Solutions( Ok(QueryResults::Solutions(
self.decode_bindings(iter, variables), self.decode_bindings(iter, variables),
)) ))
} }
@ -80,12 +80,12 @@ where
pub fn evaluate_ask_plan( pub fn evaluate_ask_plan(
&self, &self,
plan: &PlanNode<S::StrId>, plan: &PlanNode<S::StrId>,
) -> Result<QueryResult, EvaluationError> { ) -> Result<QueryResults, EvaluationError> {
let from = EncodedTuple::with_capacity(plan.maybe_bound_variables().len()); let from = EncodedTuple::with_capacity(plan.maybe_bound_variables().len());
match self.eval_plan(plan, from).next() { match self.eval_plan(plan, from).next() {
Some(Ok(_)) => Ok(QueryResult::Boolean(true)), Some(Ok(_)) => Ok(QueryResults::Boolean(true)),
Some(Err(error)) => Err(error), Some(Err(error)) => Err(error),
None => Ok(QueryResult::Boolean(false)), None => Ok(QueryResults::Boolean(false)),
} }
} }
@ -93,9 +93,9 @@ where
&self, &self,
plan: &PlanNode<S::StrId>, plan: &PlanNode<S::StrId>,
construct: Rc<Vec<TripleTemplate<S::StrId>>>, construct: Rc<Vec<TripleTemplate<S::StrId>>>,
) -> Result<QueryResult, EvaluationError> { ) -> Result<QueryResults, EvaluationError> {
let from = EncodedTuple::with_capacity(plan.maybe_bound_variables().len()); let from = EncodedTuple::with_capacity(plan.maybe_bound_variables().len());
Ok(QueryResult::Graph(QueryTriplesIterator { Ok(QueryResults::Graph(QueryTripleIter {
iter: Box::new(ConstructIterator { iter: Box::new(ConstructIterator {
eval: self.clone(), eval: self.clone(),
iter: self.eval_plan(plan, from), iter: self.eval_plan(plan, from),
@ -109,9 +109,9 @@ where
pub fn evaluate_describe_plan( pub fn evaluate_describe_plan(
&self, &self,
plan: &PlanNode<S::StrId>, plan: &PlanNode<S::StrId>,
) -> Result<QueryResult, EvaluationError> { ) -> Result<QueryResults, EvaluationError> {
let from = EncodedTuple::with_capacity(plan.maybe_bound_variables().len()); let from = EncodedTuple::with_capacity(plan.maybe_bound_variables().len());
Ok(QueryResult::Graph(QueryTriplesIterator { Ok(QueryResults::Graph(QueryTripleIter {
iter: Box::new(DescribeIterator { iter: Box::new(DescribeIterator {
eval: self.clone(), eval: self.clone(),
iter: self.eval_plan(plan, from), iter: self.eval_plan(plan, from),
@ -520,7 +520,7 @@ where
variables: Rc<Vec<Variable>>, variables: Rc<Vec<Variable>>,
from: &EncodedTuple<S::StrId>, from: &EncodedTuple<S::StrId>,
) -> Result<EncodedTuplesIterator<S::StrId>, EvaluationError> { ) -> Result<EncodedTuplesIterator<S::StrId>, EvaluationError> {
if let QueryResult::Solutions(iter) = self.service_handler.handle( if let QueryResults::Solutions(iter) = self.service_handler.handle(
self.dataset.decode_named_node( self.dataset.decode_named_node(
get_pattern_value(service_name, from) get_pattern_value(service_name, from)
.ok_or_else(|| EvaluationError::msg("The SERVICE name is not bound"))?, .ok_or_else(|| EvaluationError::msg("The SERVICE name is not bound"))?,
@ -1816,10 +1816,10 @@ where
&self, &self,
iter: EncodedTuplesIterator<S::StrId>, iter: EncodedTuplesIterator<S::StrId>,
variables: Rc<Vec<Variable>>, variables: Rc<Vec<Variable>>,
) -> QuerySolutionsIterator { ) -> QuerySolutionIter {
let eval = self.clone(); let eval = self.clone();
let tuple_size = variables.len(); let tuple_size = variables.len();
QuerySolutionsIterator::new( QuerySolutionIter::new(
variables, variables,
Box::new(iter.map(move |values| { Box::new(iter.map(move |values| {
let mut result = vec![None; tuple_size]; let mut result = vec![None; tuple_size];
@ -1837,7 +1837,7 @@ where
fn encode_bindings( fn encode_bindings(
&self, &self,
variables: Rc<Vec<Variable>>, variables: Rc<Vec<Variable>>,
iter: QuerySolutionsIterator, iter: QuerySolutionIter,
) -> EncodedTuplesIterator<S::StrId> { ) -> EncodedTuplesIterator<S::StrId> {
let eval = self.clone(); let eval = self.clone();
Box::new(iter.map(move |solution| { Box::new(iter.map(move |solution| {

@ -7,16 +7,16 @@ use crate::sparql::model::*;
use std::io::Write; use std::io::Write;
pub fn write_json_results( pub fn write_json_results(
results: QueryResult, results: QueryResults,
mut sink: impl Write, mut sink: impl Write,
) -> Result<(), EvaluationError> { ) -> Result<(), EvaluationError> {
match results { match results {
QueryResult::Boolean(value) => { QueryResults::Boolean(value) => {
sink.write_all(b"{\"head\":{},\"boolean\":")?; sink.write_all(b"{\"head\":{},\"boolean\":")?;
sink.write_all(if value { b"true" } else { b"false" })?; sink.write_all(if value { b"true" } else { b"false" })?;
sink.write_all(b"}")?; sink.write_all(b"}")?;
} }
QueryResult::Solutions(solutions) => { QueryResults::Solutions(solutions) => {
sink.write_all(b"{\"head\":{\"vars\":[")?; sink.write_all(b"{\"head\":{\"vars\":[")?;
let mut start_vars = true; let mut start_vars = true;
for variable in solutions.variables() { for variable in solutions.variables() {
@ -75,7 +75,7 @@ pub fn write_json_results(
} }
sink.write_all(b"]}}")?; sink.write_all(b"]}}")?;
} }
QueryResult::Graph(_) => { QueryResults::Graph(_) => {
return Err(invalid_input_error( return Err(invalid_input_error(
"Graphs could not be formatted to SPARQL query results XML format", "Graphs could not be formatted to SPARQL query results XML format",
) )

@ -1,4 +1,6 @@
//! [SPARQL](https://www.w3.org/TR/sparql11-overview/) implementation. //! [SPARQL](https://www.w3.org/TR/sparql11-overview/) implementation.
//!
//! SPARQL evaluation is done from a store. See [`MemoryStore`](../store/memory/struct.MemoryStore.html#method.query) for an example.
mod algebra; mod algebra;
mod dataset; mod dataset;
@ -16,11 +18,11 @@ use crate::sparql::algebra::QueryVariants;
use crate::sparql::dataset::DatasetView; use crate::sparql::dataset::DatasetView;
pub use crate::sparql::error::EvaluationError; pub use crate::sparql::error::EvaluationError;
use crate::sparql::eval::SimpleEvaluator; use crate::sparql::eval::SimpleEvaluator;
pub use crate::sparql::model::QueryResult; pub use crate::sparql::model::QueryResults;
pub use crate::sparql::model::QueryResultFormat; pub use crate::sparql::model::QueryResultsFormat;
pub use crate::sparql::model::QuerySolution; pub use crate::sparql::model::QuerySolution;
pub use crate::sparql::model::QuerySolutionsIterator; pub use crate::sparql::model::QuerySolutionIter;
pub use crate::sparql::model::QueryTriplesIterator; pub use crate::sparql::model::QueryTripleIter;
pub use crate::sparql::model::Variable; pub use crate::sparql::model::Variable;
pub use crate::sparql::parser::ParseError; pub use crate::sparql::parser::ParseError;
pub use crate::sparql::parser::Query; pub use crate::sparql::parser::Query;
@ -141,7 +143,7 @@ impl<S: ReadableEncodedStore + 'static> SimplePreparedQuery<S> {
} }
/// Evaluates the query and returns its results /// Evaluates the query and returns its results
pub fn exec(&self) -> Result<QueryResult, EvaluationError> { pub fn exec(&self) -> Result<QueryResults, EvaluationError> {
match &self.0 { match &self.0 {
SimplePreparedQueryAction::Select { SimplePreparedQueryAction::Select {
plan, plan,
@ -201,7 +203,7 @@ impl QueryOptions {
/// ``` /// ```
/// use oxigraph::MemoryStore; /// use oxigraph::MemoryStore;
/// use oxigraph::model::*; /// use oxigraph::model::*;
/// use oxigraph::sparql::{QueryOptions, QueryResult, ServiceHandler, Query, EvaluationError}; /// use oxigraph::sparql::{QueryOptions, QueryResults, ServiceHandler, Query, EvaluationError};
/// ///
/// #[derive(Default)] /// #[derive(Default)]
/// struct TestServiceHandler { /// struct TestServiceHandler {
@ -211,7 +213,7 @@ impl QueryOptions {
/// impl ServiceHandler for TestServiceHandler { /// impl ServiceHandler for TestServiceHandler {
/// type Error = EvaluationError; /// type Error = EvaluationError;
/// ///
/// fn handle(&self,service_name: NamedNode, query: Query) -> Result<QueryResult,EvaluationError> { /// fn handle(&self,service_name: NamedNode, query: Query) -> Result<QueryResults,EvaluationError> {
/// if service_name == "http://example.com/service" { /// if service_name == "http://example.com/service" {
/// self.store.query(query, QueryOptions::default()) /// self.store.query(query, QueryOptions::default())
/// } else { /// } else {
@ -225,7 +227,7 @@ impl QueryOptions {
/// let ex = NamedNode::new("http://example.com")?; /// let ex = NamedNode::new("http://example.com")?;
/// service.store.insert(Quad::new(ex.clone(), ex.clone(), ex.clone(), None)); /// service.store.insert(Quad::new(ex.clone(), ex.clone(), ex.clone(), None));
/// ///
/// if let QueryResult::Solutions(mut solutions) = store.query( /// if let QueryResults::Solutions(mut solutions) = store.query(
/// "SELECT ?s WHERE { SERVICE <http://example.com/service> { ?s ?p ?o } }", /// "SELECT ?s WHERE { SERVICE <http://example.com/service> { ?s ?p ?o } }",
/// QueryOptions::default().with_service_handler(service) /// QueryOptions::default().with_service_handler(service)
/// )? { /// )? {
@ -237,7 +239,7 @@ pub trait ServiceHandler {
type Error: Error + Send + Sync + 'static; type Error: Error + Send + Sync + 'static;
/// Evaluates a [`Query`](struct.Query.html) against a given service identified by a [`NamedNode`](../model/struct.NamedNode.html). /// Evaluates a [`Query`](struct.Query.html) against a given service identified by a [`NamedNode`](../model/struct.NamedNode.html).
fn handle(&self, service_name: NamedNode, query: Query) -> Result<QueryResult, Self::Error>; fn handle(&self, service_name: NamedNode, query: Query) -> Result<QueryResults, Self::Error>;
} }
struct EmptyServiceHandler; struct EmptyServiceHandler;
@ -245,7 +247,7 @@ struct EmptyServiceHandler;
impl ServiceHandler for EmptyServiceHandler { impl ServiceHandler for EmptyServiceHandler {
type Error = EvaluationError; type Error = EvaluationError;
fn handle(&self, _: NamedNode, _: Query) -> Result<QueryResult, EvaluationError> { fn handle(&self, _: NamedNode, _: Query) -> Result<QueryResults, EvaluationError> {
Err(EvaluationError::msg( Err(EvaluationError::msg(
"The SERVICE feature is not implemented", "The SERVICE feature is not implemented",
)) ))
@ -263,7 +265,7 @@ impl<S: ServiceHandler> ServiceHandler for ErrorConversionServiceHandler<S> {
&self, &self,
service_name: NamedNode, service_name: NamedNode,
query: Query, query: Query,
) -> Result<QueryResult, EvaluationError> { ) -> Result<QueryResults, EvaluationError> {
self.handler self.handler
.handle(service_name, query) .handle(service_name, query)
.map_err(EvaluationError::wrap) .map_err(EvaluationError::wrap)

@ -6,31 +6,31 @@ use crate::sparql::error::EvaluationError;
use crate::sparql::json_results::write_json_results; use crate::sparql::json_results::write_json_results;
use crate::sparql::xml_results::{read_xml_results, write_xml_results}; use crate::sparql::xml_results::{read_xml_results, write_xml_results};
use rand::random; use rand::random;
use std::fmt;
use std::io::{BufRead, Write}; use std::io::{BufRead, Write};
use std::rc::Rc; use std::rc::Rc;
use std::{fmt, io};
/// Results of a [SPARQL query](https://www.w3.org/TR/sparql11-query/) /// Results of a [SPARQL query](https://www.w3.org/TR/sparql11-query/)
pub enum QueryResult { pub enum QueryResults {
/// Results of a [SELECT](https://www.w3.org/TR/sparql11-query/#select) query /// Results of a [SELECT](https://www.w3.org/TR/sparql11-query/#select) query
Solutions(QuerySolutionsIterator), Solutions(QuerySolutionIter),
/// Result of a [ASK](https://www.w3.org/TR/sparql11-query/#ask) query /// Result of a [ASK](https://www.w3.org/TR/sparql11-query/#ask) query
Boolean(bool), Boolean(bool),
/// Results of a [CONSTRUCT](https://www.w3.org/TR/sparql11-query/#construct) or [DESCRIBE](https://www.w3.org/TR/sparql11-query/#describe) query /// Results of a [CONSTRUCT](https://www.w3.org/TR/sparql11-query/#construct) or [DESCRIBE](https://www.w3.org/TR/sparql11-query/#describe) query
Graph(QueryTriplesIterator), Graph(QueryTripleIter),
} }
impl QueryResult { impl QueryResults {
/// Reads a SPARQL query results serialization
pub fn read( pub fn read(
reader: impl BufRead + 'static, reader: impl BufRead + 'static,
format: QueryResultFormat, format: QueryResultsFormat,
) -> Result<Self, EvaluationError> { ) -> Result<Self, io::Error> {
match format { match format {
QueryResultFormat::Xml => read_xml_results(reader), QueryResultsFormat::Xml => read_xml_results(reader),
QueryResultFormat::Json => Err(invalid_input_error( QueryResultsFormat::Json => Err(invalid_input_error(
"JSON SPARQL results format parsing has not been implemented yet", "JSON SPARQL results format parsing has not been implemented yet",
) )), //TODO: implement
.into()), //TODO: implement
} }
} }
@ -41,25 +41,25 @@ impl QueryResult {
/// ``` /// ```
/// use oxigraph::MemoryStore; /// use oxigraph::MemoryStore;
/// use oxigraph::model::*; /// use oxigraph::model::*;
/// use oxigraph::sparql::{QueryOptions, QueryResultFormat}; /// use oxigraph::sparql::{QueryOptions, QueryResultsFormat};
/// ///
/// let store = MemoryStore::new(); /// let store = MemoryStore::new();
/// let ex = NamedNode::new("http://example.com")?; /// let ex = NamedNode::new("http://example.com")?;
/// store.insert(Quad::new(ex.clone(), ex.clone(), ex.clone(), None)); /// store.insert(Quad::new(ex.clone(), ex.clone(), ex.clone(), None));
/// ///
/// let mut results = Vec::new(); /// let mut results = Vec::new();
/// store.query("SELECT ?s WHERE { ?s ?p ?o }", QueryOptions::default())?.write(&mut results, QueryResultFormat::Json)?; /// store.query("SELECT ?s WHERE { ?s ?p ?o }", QueryOptions::default())?.write(&mut results, QueryResultsFormat::Json)?;
/// assert_eq!(results, "{\"head\":{\"vars\":[\"s\"]},\"results\":{\"bindings\":[{\"s\":{\"type\":\"uri\",\"value\":\"http://example.com\"}}]}}".as_bytes()); /// assert_eq!(results, "{\"head\":{\"vars\":[\"s\"]},\"results\":{\"bindings\":[{\"s\":{\"type\":\"uri\",\"value\":\"http://example.com\"}}]}}".as_bytes());
/// # Result::<_,Box<dyn std::error::Error>>::Ok(()) /// # Result::<_,Box<dyn std::error::Error>>::Ok(())
/// ``` /// ```
pub fn write( pub fn write(
self, self,
writer: impl Write, writer: impl Write,
format: QueryResultFormat, format: QueryResultsFormat,
) -> Result<(), EvaluationError> { ) -> Result<(), EvaluationError> {
match format { match format {
QueryResultFormat::Xml => write_xml_results(self, writer), QueryResultsFormat::Xml => write_xml_results(self, writer),
QueryResultFormat::Json => write_json_results(self, writer), QueryResultsFormat::Json => write_json_results(self, writer),
} }
} }
@ -89,7 +89,7 @@ impl QueryResult {
write: impl Write, write: impl Write,
format: GraphFormat, format: GraphFormat,
) -> Result<(), EvaluationError> { ) -> Result<(), EvaluationError> {
if let QueryResult::Graph(triples) = self { if let QueryResults::Graph(triples) = self {
let mut writer = GraphSerializer::from_format(format).triple_writer(write)?; let mut writer = GraphSerializer::from_format(format).triple_writer(write)?;
for triple in triples { for triple in triples {
writer.write(&triple?)?; writer.write(&triple?)?;
@ -105,89 +105,89 @@ impl QueryResult {
} }
} }
impl From<QuerySolutionsIterator> for QueryResult { impl From<QuerySolutionIter> for QueryResults {
#[inline] #[inline]
fn from(value: QuerySolutionsIterator) -> Self { fn from(value: QuerySolutionIter) -> Self {
QueryResult::Solutions(value) QueryResults::Solutions(value)
} }
} }
/// [SPARQL query](https://www.w3.org/TR/sparql11-query/) serialization formats /// [SPARQL query](https://www.w3.org/TR/sparql11-query/) results serialization formats
/// ///
/// This enumeration is non exhaustive. New formats like CSV will be added in the future. /// This enumeration is non exhaustive. New formats like CSV will be added in the future.
#[derive(Eq, PartialEq, Debug, Clone, Copy, Hash)] #[derive(Eq, PartialEq, Debug, Clone, Copy, Hash)]
#[non_exhaustive] #[non_exhaustive]
pub enum QueryResultFormat { pub enum QueryResultsFormat {
/// [SPARQL Query Results XML Format](http://www.w3.org/TR/rdf-sparql-XMLres/) /// [SPARQL Query Results XML Format](http://www.w3.org/TR/rdf-sparql-XMLres/)
Xml, Xml,
/// [SPARQL Query Results JSON Format](https://www.w3.org/TR/sparql11-results-json/) /// [SPARQL Query Results JSON Format](https://www.w3.org/TR/sparql11-results-json/)
Json, Json,
} }
impl QueryResultFormat { impl QueryResultsFormat {
/// The format canonical IRI according to the [Unique URIs for file formats registry](https://www.w3.org/ns/formats/). /// The format canonical IRI according to the [Unique URIs for file formats registry](https://www.w3.org/ns/formats/).
/// ///
/// ``` /// ```
/// use oxigraph::sparql::QueryResultFormat; /// use oxigraph::sparql::QueryResultsFormat;
/// ///
/// assert_eq!(QueryResultFormat::Json.iri(), "http://www.w3.org/ns/formats/SPARQL_Results_JSON") /// assert_eq!(QueryResultsFormat::Json.iri(), "http://www.w3.org/ns/formats/SPARQL_Results_JSON")
/// ``` /// ```
#[inline] #[inline]
pub fn iri(self) -> &'static str { pub fn iri(self) -> &'static str {
match self { match self {
QueryResultFormat::Xml => "http://www.w3.org/ns/formats/SPARQL_Results_XML", QueryResultsFormat::Xml => "http://www.w3.org/ns/formats/SPARQL_Results_XML",
QueryResultFormat::Json => "http://www.w3.org/ns/formats/SPARQL_Results_JSON", QueryResultsFormat::Json => "http://www.w3.org/ns/formats/SPARQL_Results_JSON",
} }
} }
/// The format [IANA media type](https://tools.ietf.org/html/rfc2046). /// The format [IANA media type](https://tools.ietf.org/html/rfc2046).
/// ///
/// ``` /// ```
/// use oxigraph::sparql::QueryResultFormat; /// use oxigraph::sparql::QueryResultsFormat;
/// ///
/// assert_eq!(QueryResultFormat::Json.media_type(), "application/sparql-results+json") /// assert_eq!(QueryResultsFormat::Json.media_type(), "application/sparql-results+json")
/// ``` /// ```
#[inline] #[inline]
pub fn media_type(self) -> &'static str { pub fn media_type(self) -> &'static str {
match self { match self {
QueryResultFormat::Xml => "application/sparql-results+xml", QueryResultsFormat::Xml => "application/sparql-results+xml",
QueryResultFormat::Json => "application/sparql-results+json", QueryResultsFormat::Json => "application/sparql-results+json",
} }
} }
/// The format [IANA-registered](https://tools.ietf.org/html/rfc2046) file extension. /// The format [IANA-registered](https://tools.ietf.org/html/rfc2046) file extension.
/// ///
/// ``` /// ```
/// use oxigraph::sparql::QueryResultFormat; /// use oxigraph::sparql::QueryResultsFormat;
/// ///
/// assert_eq!(QueryResultFormat::Json.file_extension(), "srj") /// assert_eq!(QueryResultsFormat::Json.file_extension(), "srj")
/// ``` /// ```
#[inline] #[inline]
pub fn file_extension(self) -> &'static str { pub fn file_extension(self) -> &'static str {
match self { match self {
QueryResultFormat::Xml => "srx", QueryResultsFormat::Xml => "srx",
QueryResultFormat::Json => "srj", QueryResultsFormat::Json => "srj",
} }
} }
/// Looks for a known format from a media type. /// Looks for a known format from a media type.
/// ///
/// It supports some media type aliases. /// It supports some media type aliases.
/// For example "application/xml" is going to return `QueryResultFormat::Xml` even if it is not its canonical media type. /// For example "application/xml" is going to return `Xml` even if it is not its canonical media type.
/// ///
/// Example: /// Example:
/// ``` /// ```
/// use oxigraph::sparql::QueryResultFormat; /// use oxigraph::sparql::QueryResultsFormat;
/// ///
/// assert_eq!(QueryResultFormat::from_media_type("application/sparql-results+json; charset=utf-8"), Some(QueryResultFormat::Json)) /// assert_eq!(QueryResultsFormat::from_media_type("application/sparql-results+json; charset=utf-8"), Some(QueryResultsFormat::Json))
/// ``` /// ```
pub fn from_media_type(media_type: &str) -> Option<Self> { pub fn from_media_type(media_type: &str) -> Option<Self> {
if let Some(base_type) = media_type.split(';').next() { if let Some(base_type) = media_type.split(';').next() {
match base_type { match base_type {
"application/sparql-results+xml" | "application/xml" | "text/xml" => { "application/sparql-results+xml" | "application/xml" | "text/xml" => {
Some(QueryResultFormat::Xml) Some(QueryResultsFormat::Xml)
} }
"application/sparql-results+json" | "application/json" | "text/json" => { "application/sparql-results+json" | "application/json" | "text/json" => {
Some(QueryResultFormat::Json) Some(QueryResultsFormat::Json)
} }
_ => None, _ => None,
} }
@ -197,26 +197,26 @@ impl QueryResultFormat {
} }
} }
/// An iterator over query result solutions /// An iterator over [`QuerySolution`s](struct.QuerySolution.html)
/// ///
/// ``` /// ```
/// use oxigraph::MemoryStore; /// use oxigraph::MemoryStore;
/// use oxigraph::sparql::{QueryResult, QueryOptions}; /// use oxigraph::sparql::{QueryResults, QueryOptions};
/// ///
/// let store = MemoryStore::new(); /// let store = MemoryStore::new();
/// if let QueryResult::Solutions(solutions) = store.query("SELECT ?s WHERE { ?s ?p ?o }", QueryOptions::default())? { /// if let QueryResults::Solutions(solutions) = store.query("SELECT ?s WHERE { ?s ?p ?o }", QueryOptions::default())? {
/// for solution in solutions { /// for solution in solutions {
/// println!("{:?}", solution?.get("s")); /// println!("{:?}", solution?.get("s"));
/// } /// }
/// } /// }
/// # Result::<_,Box<dyn std::error::Error>>::Ok(()) /// # Result::<_,Box<dyn std::error::Error>>::Ok(())
/// ``` /// ```
pub struct QuerySolutionsIterator { pub struct QuerySolutionIter {
variables: Rc<Vec<Variable>>, variables: Rc<Vec<Variable>>,
iter: Box<dyn Iterator<Item = Result<Vec<Option<Term>>, EvaluationError>>>, iter: Box<dyn Iterator<Item = Result<Vec<Option<Term>>, EvaluationError>>>,
} }
impl QuerySolutionsIterator { impl QuerySolutionIter {
pub fn new( pub fn new(
variables: Rc<Vec<Variable>>, variables: Rc<Vec<Variable>>,
iter: Box<dyn Iterator<Item = Result<Vec<Option<Term>>, EvaluationError>>>, iter: Box<dyn Iterator<Item = Result<Vec<Option<Term>>, EvaluationError>>>,
@ -228,10 +228,10 @@ impl QuerySolutionsIterator {
/// ///
/// ``` /// ```
/// use oxigraph::MemoryStore; /// use oxigraph::MemoryStore;
/// use oxigraph::sparql::{QueryResult, QueryOptions, Variable}; /// use oxigraph::sparql::{QueryResults, QueryOptions, Variable};
/// ///
/// let store = MemoryStore::new(); /// let store = MemoryStore::new();
/// if let QueryResult::Solutions(solutions) = store.query("SELECT ?s ?o WHERE { ?s ?p ?o }", QueryOptions::default())? { /// if let QueryResults::Solutions(solutions) = store.query("SELECT ?s ?o WHERE { ?s ?p ?o }", QueryOptions::default())? {
/// assert_eq!(solutions.variables(), &[Variable::new("s"), Variable::new("o")]); /// assert_eq!(solutions.variables(), &[Variable::new("s"), Variable::new("o")]);
/// } /// }
/// # Result::<_,Box<dyn std::error::Error>>::Ok(()) /// # Result::<_,Box<dyn std::error::Error>>::Ok(())
@ -242,7 +242,7 @@ impl QuerySolutionsIterator {
} }
} }
impl Iterator for QuerySolutionsIterator { impl Iterator for QuerySolutionIter {
type Item = Result<QuerySolution, EvaluationError>; type Item = Result<QuerySolution, EvaluationError>;
#[inline] #[inline]
@ -344,21 +344,21 @@ impl VariableSolutionIndex for Variable {
/// ///
/// ``` /// ```
/// use oxigraph::MemoryStore; /// use oxigraph::MemoryStore;
/// use oxigraph::sparql::{QueryResult, QueryOptions}; /// use oxigraph::sparql::{QueryResults, QueryOptions};
/// ///
/// let store = MemoryStore::new(); /// let store = MemoryStore::new();
/// if let QueryResult::Graph(triples) = store.query("CONSTRUCT WHERE { ?s ?p ?o }", QueryOptions::default())? { /// if let QueryResults::Graph(triples) = store.query("CONSTRUCT WHERE { ?s ?p ?o }", QueryOptions::default())? {
/// for triple in triples { /// for triple in triples {
/// println!("{}", triple?); /// println!("{}", triple?);
/// } /// }
/// } /// }
/// # Result::<_,Box<dyn std::error::Error>>::Ok(()) /// # Result::<_,Box<dyn std::error::Error>>::Ok(())
/// ``` /// ```
pub struct QueryTriplesIterator { pub struct QueryTripleIter {
pub(crate) iter: Box<dyn Iterator<Item = Result<Triple, EvaluationError>>>, pub(crate) iter: Box<dyn Iterator<Item = Result<Triple, EvaluationError>>>,
} }
impl Iterator for QueryTriplesIterator { impl Iterator for QueryTripleIter {
type Item = Result<Triple, EvaluationError>; type Item = Result<Triple, EvaluationError>;
#[inline] #[inline]

@ -12,19 +12,20 @@ use quick_xml::events::Event;
use quick_xml::Reader; use quick_xml::Reader;
use quick_xml::Writer; use quick_xml::Writer;
use std::collections::BTreeMap; use std::collections::BTreeMap;
use std::io;
use std::io::BufRead; use std::io::BufRead;
use std::io::Write; use std::io::Write;
use std::iter::empty; use std::iter::empty;
use std::rc::Rc; use std::rc::Rc;
pub fn write_xml_results(results: QueryResult, sink: impl Write) -> Result<(), EvaluationError> { pub fn write_xml_results(results: QueryResults, sink: impl Write) -> Result<(), EvaluationError> {
match results { match results {
QueryResult::Boolean(value) => { QueryResults::Boolean(value) => {
write_boolean(value, sink).map_err(map_xml_error)?; write_boolean(value, sink).map_err(map_xml_error)?;
Ok(()) Ok(())
} }
QueryResult::Solutions(solutions) => write_solutions(solutions, sink), QueryResults::Solutions(solutions) => write_solutions(solutions, sink),
QueryResult::Graph(_) => Err(invalid_input_error( QueryResults::Graph(_) => Err(invalid_input_error(
"Graphs could not be formatted to SPARQL query results XML format", "Graphs could not be formatted to SPARQL query results XML format",
) )
.into()), .into()),
@ -50,10 +51,7 @@ fn write_boolean(value: bool, sink: impl Write) -> Result<(), quick_xml::Error>
Ok(()) Ok(())
} }
fn write_solutions( fn write_solutions(solutions: QuerySolutionIter, sink: impl Write) -> Result<(), EvaluationError> {
solutions: QuerySolutionsIterator,
sink: impl Write,
) -> Result<(), EvaluationError> {
let mut writer = Writer::new(sink); let mut writer = Writer::new(sink);
writer writer
.write_event(Event::Decl(BytesDecl::new(b"1.0", None, None))) .write_event(Event::Decl(BytesDecl::new(b"1.0", None, None)))
@ -148,7 +146,7 @@ fn write_solutions(
Ok(()) Ok(())
} }
pub fn read_xml_results(source: impl BufRead + 'static) -> Result<QueryResult, EvaluationError> { pub fn read_xml_results(source: impl BufRead + 'static) -> Result<QueryResults, io::Error> {
enum State { enum State {
Start, Start,
Sparql, Sparql,
@ -176,8 +174,7 @@ pub fn read_xml_results(source: impl BufRead + 'static) -> Result<QueryResult, E
return Err(invalid_data_error(format!( return Err(invalid_data_error(format!(
"Unexpected namespace found in RDF/XML query result: {}", "Unexpected namespace found in RDF/XML query result: {}",
reader.decode(ns).map_err(map_xml_error)? reader.decode(ns).map_err(map_xml_error)?
)) )));
.into());
} }
} }
event event
@ -188,14 +185,14 @@ pub fn read_xml_results(source: impl BufRead + 'static) -> Result<QueryResult, E
if event.name() == b"sparql" { if event.name() == b"sparql" {
state = State::Sparql; state = State::Sparql;
} else { } else {
return Err(invalid_data_error(format!("Expecting <sparql> tag, found {}", reader.decode(event.name()).map_err(map_xml_error)?)).into()); return Err(invalid_data_error(format!("Expecting <sparql> tag, found {}", reader.decode(event.name()).map_err(map_xml_error)?)));
} }
} }
State::Sparql => { State::Sparql => {
if event.name() == b"head" { if event.name() == b"head" {
state = State::Head; state = State::Head;
} else { } else {
return Err(invalid_data_error(format!("Expecting <head> tag, found {}", reader.decode(event.name()).map_err(map_xml_error)?)).into()); return Err(invalid_data_error(format!("Expecting <head> tag, found {}", reader.decode(event.name()).map_err(map_xml_error)?)));
} }
} }
State::Head => { State::Head => {
@ -208,7 +205,7 @@ pub fn read_xml_results(source: impl BufRead + 'static) -> Result<QueryResult, E
} else if event.name() == b"link" { } else if event.name() == b"link" {
// no op // no op
} else { } else {
return Err(invalid_data_error(format!("Expecting <variable> or <link> tag, found {}", reader.decode(event.name()).map_err(map_xml_error)?)).into()); return Err(invalid_data_error(format!("Expecting <variable> or <link> tag, found {}", reader.decode(event.name()).map_err(map_xml_error)?)));
} }
} }
State::AfterHead => { State::AfterHead => {
@ -219,7 +216,7 @@ pub fn read_xml_results(source: impl BufRead + 'static) -> Result<QueryResult, E
for (i,var) in variables.iter().enumerate() { for (i,var) in variables.iter().enumerate() {
mapping.insert(var.as_bytes().to_vec(), i); mapping.insert(var.as_bytes().to_vec(), i);
} }
return Ok(QueryResult::Solutions(QuerySolutionsIterator::new( return Ok(QueryResults::Solutions(QuerySolutionIter::new(
Rc::new(variables.into_iter().map(Variable::new).collect()), Rc::new(variables.into_iter().map(Variable::new).collect()),
Box::new(ResultsIterator { Box::new(ResultsIterator {
reader, reader,
@ -229,17 +226,17 @@ pub fn read_xml_results(source: impl BufRead + 'static) -> Result<QueryResult, E
}), }),
))); )));
} else if event.name() != b"link" && event.name() != b"results" && event.name() != b"boolean" { } else if event.name() != b"link" && event.name() != b"results" && event.name() != b"boolean" {
return Err(invalid_data_error(format!("Expecting sparql tag, found {}", reader.decode(event.name()).map_err(map_xml_error)?)).into()); return Err(invalid_data_error(format!("Expecting sparql tag, found {}", reader.decode(event.name()).map_err(map_xml_error)?)));
} }
} }
State::Boolean => return Err(invalid_data_error(format!("Unexpected tag inside of <boolean> tag: {}", reader.decode(event.name()).map_err(map_xml_error)?)).into()) State::Boolean => return Err(invalid_data_error(format!("Unexpected tag inside of <boolean> tag: {}", reader.decode(event.name()).map_err(map_xml_error)?)))
}, },
Event::Empty(event) => match state { Event::Empty(event) => match state {
State::Sparql => { State::Sparql => {
if event.name() == b"head" { if event.name() == b"head" {
state = State::AfterHead; state = State::AfterHead;
} else { } else {
return Err(invalid_data_error(format!("Expecting <head> tag, found {}", reader.decode(event.name()).map_err(map_xml_error)?)).into()); return Err(invalid_data_error(format!("Expecting <head> tag, found {}", reader.decode(event.name()).map_err(map_xml_error)?)));
} }
} }
State::Head => { State::Head => {
@ -252,42 +249,42 @@ pub fn read_xml_results(source: impl BufRead + 'static) -> Result<QueryResult, E
} else if event.name() == b"link" { } else if event.name() == b"link" {
// no op // no op
} else { } else {
return Err(invalid_data_error(format!("Expecting <variable> or <link> tag, found {}", reader.decode(event.name()).map_err(map_xml_error)?)).into()); return Err(invalid_data_error(format!("Expecting <variable> or <link> tag, found {}", reader.decode(event.name()).map_err(map_xml_error)?)));
} }
}, },
State::AfterHead => { State::AfterHead => {
if event.name() == b"results" { if event.name() == b"results" {
return Ok(QueryResult::Solutions(QuerySolutionsIterator::new( return Ok(QueryResults::Solutions(QuerySolutionIter::new(
Rc::new(variables.into_iter().map(Variable::new).collect()), Rc::new(variables.into_iter().map(Variable::new).collect()),
Box::new(empty()), Box::new(empty()),
))) )))
} else { } else {
return Err(invalid_data_error(format!("Unexpected autoclosing tag <{}>", reader.decode(event.name()).map_err(map_xml_error)?)).into()) return Err(invalid_data_error(format!("Unexpected autoclosing tag <{}>", reader.decode(event.name()).map_err(map_xml_error)?)))
} }
} }
_ => return Err(invalid_data_error(format!("Unexpected autoclosing tag <{}>", reader.decode(event.name()).map_err(map_xml_error)?)).into()) _ => return Err(invalid_data_error(format!("Unexpected autoclosing tag <{}>", reader.decode(event.name()).map_err(map_xml_error)?)))
}, },
Event::Text(event) => { Event::Text(event) => {
let value = event.unescaped().map_err(map_xml_error)?; let value = event.unescaped().map_err(map_xml_error)?;
return match state { return match state {
State::Boolean => { State::Boolean => {
return if value.as_ref() == b"true" { return if value.as_ref() == b"true" {
Ok(QueryResult::Boolean(true)) Ok(QueryResults::Boolean(true))
} else if value.as_ref() == b"false" { } else if value.as_ref() == b"false" {
Ok(QueryResult::Boolean(false)) Ok(QueryResults::Boolean(false))
} else { } else {
Err(invalid_data_error(format!("Unexpected boolean value. Found {}", reader.decode(&value).map_err(map_xml_error)?)).into()) Err(invalid_data_error(format!("Unexpected boolean value. Found {}", reader.decode(&value).map_err(map_xml_error)?)))
}; };
} }
_ => Err(invalid_data_error(format!("Unexpected textual value found: {}", reader.decode(&value).map_err(map_xml_error)?)).into()) _ => Err(invalid_data_error(format!("Unexpected textual value found: {}", reader.decode(&value).map_err(map_xml_error)?)))
}; };
}, },
Event::End(_) => if let State::Head = state { Event::End(_) => if let State::Head = state {
state = State::AfterHead; state = State::AfterHead;
} else { } else {
return Err(invalid_data_error("Unexpected early file end. All results file should have a <head> and a <result> or <boolean> tag").into()); return Err(invalid_data_error("Unexpected early file end. All results file should have a <head> and a <result> or <boolean> tag"));
}, },
Event::Eof => return Err(invalid_data_error("Unexpected early file end. All results file should have a <head> and a <result> or <boolean> tag").into()), Event::Eof => return Err(invalid_data_error("Unexpected early file end. All results file should have a <head> and a <result> or <boolean> tag")),
_ => (), _ => (),
} }
} }
@ -521,10 +518,10 @@ fn build_literal(
} }
} }
fn map_xml_error(error: quick_xml::Error) -> EvaluationError { fn map_xml_error(error: quick_xml::Error) -> io::Error {
match error { match error {
quick_xml::Error::Io(error) => error, quick_xml::Error::Io(error) => error,
quick_xml::Error::UnexpectedEof(_) => io::Error::new(io::ErrorKind::UnexpectedEof, error),
_ => invalid_data_error(error), _ => invalid_data_error(error),
} }
.into()
} }

@ -3,7 +3,7 @@
use crate::error::{invalid_input_error, UnwrapInfallible}; use crate::error::{invalid_input_error, UnwrapInfallible};
use crate::io::{DatasetFormat, DatasetParser, GraphFormat, GraphParser}; use crate::io::{DatasetFormat, DatasetParser, GraphFormat, GraphParser};
use crate::model::*; use crate::model::*;
use crate::sparql::{EvaluationError, Query, QueryOptions, QueryResult, SimplePreparedQuery}; use crate::sparql::{EvaluationError, Query, QueryOptions, QueryResults, SimplePreparedQuery};
use crate::store::numeric_encoder::{ use crate::store::numeric_encoder::{
Decoder, ReadEncoder, StrContainer, StrEncodingAware, StrId, StrLookup, WriteEncoder, Decoder, ReadEncoder, StrContainer, StrEncodingAware, StrId, StrLookup, WriteEncoder,
}; };
@ -23,14 +23,14 @@ use std::vec::IntoIter;
use std::{fmt, io}; use std::{fmt, io};
/// In-memory store. /// In-memory store.
/// It encodes a [RDF dataset](https://www.w3.org/TR/rdf11-concepts/#dfn-rdf-dataset) and allows to query and update it using SPARQL. /// It encodes a [RDF dataset](https://www.w3.org/TR/rdf11-concepts/#dfn-rdf-dataset) and allows to query it using SPARQL.
/// It is cheap to build using the [`MemoryStore::new()`](#method.new) method. /// It is cheap to build using the [`MemoryStore::new()`](#method.new) method.
/// ///
/// Usage example: /// Usage example:
/// ``` /// ```
/// use oxigraph::MemoryStore; /// use oxigraph::MemoryStore;
/// use oxigraph::model::*; /// use oxigraph::model::*;
/// use oxigraph::sparql::{QueryResult, QueryOptions}; /// use oxigraph::sparql::{QueryResults, QueryOptions};
/// ///
/// let store = MemoryStore::new(); /// let store = MemoryStore::new();
/// ///
@ -44,7 +44,7 @@ use std::{fmt, io};
/// assert_eq!(vec![quad], results); /// assert_eq!(vec![quad], results);
/// ///
/// // SPARQL query /// // SPARQL query
/// if let QueryResult::Solutions(mut solutions) = store.query("SELECT ?s WHERE { ?s ?p ?o }", QueryOptions::default())? { /// if let QueryResults::Solutions(mut solutions) = store.query("SELECT ?s WHERE { ?s ?p ?o }", QueryOptions::default())? {
/// assert_eq!(solutions.next().unwrap()?.get("s"), Some(&ex.into())); /// assert_eq!(solutions.next().unwrap()?.get("s"), Some(&ex.into()));
/// } /// }
/// # Result::<_,Box<dyn std::error::Error>>::Ok(()) /// # Result::<_,Box<dyn std::error::Error>>::Ok(())
@ -94,7 +94,7 @@ impl MemoryStore {
/// ``` /// ```
/// use oxigraph::MemoryStore; /// use oxigraph::MemoryStore;
/// use oxigraph::model::*; /// use oxigraph::model::*;
/// use oxigraph::sparql::{QueryResult, QueryOptions}; /// use oxigraph::sparql::{QueryResults, QueryOptions};
/// ///
/// let store = MemoryStore::new(); /// let store = MemoryStore::new();
/// ///
@ -103,7 +103,7 @@ impl MemoryStore {
/// store.insert(Quad::new(ex.clone(), ex.clone(), ex.clone(), None)); /// store.insert(Quad::new(ex.clone(), ex.clone(), ex.clone(), None));
/// ///
/// // SPARQL query /// // SPARQL query
/// if let QueryResult::Solutions(mut solutions) = store.query("SELECT ?s WHERE { ?s ?p ?o }", QueryOptions::default())? { /// if let QueryResults::Solutions(mut solutions) = store.query("SELECT ?s WHERE { ?s ?p ?o }", QueryOptions::default())? {
/// assert_eq!(solutions.next().unwrap()?.get("s"), Some(&ex.into())); /// assert_eq!(solutions.next().unwrap()?.get("s"), Some(&ex.into()));
/// } /// }
/// # Result::<_,Box<dyn std::error::Error>>::Ok(()) /// # Result::<_,Box<dyn std::error::Error>>::Ok(())
@ -112,7 +112,7 @@ impl MemoryStore {
&self, &self,
query: impl TryInto<Query, Error = impl Into<EvaluationError>>, query: impl TryInto<Query, Error = impl Into<EvaluationError>>,
options: QueryOptions, options: QueryOptions,
) -> Result<QueryResult, EvaluationError> { ) -> Result<QueryResults, EvaluationError> {
self.prepare_query(query, options)?.exec() self.prepare_query(query, options)?.exec()
} }
@ -123,7 +123,7 @@ impl MemoryStore {
/// ``` /// ```
/// use oxigraph::MemoryStore; /// use oxigraph::MemoryStore;
/// use oxigraph::model::*; /// use oxigraph::model::*;
/// use oxigraph::sparql::{QueryResult, QueryOptions}; /// use oxigraph::sparql::{QueryResults, QueryOptions};
/// ///
/// let store = MemoryStore::new(); /// let store = MemoryStore::new();
/// ///
@ -133,7 +133,7 @@ impl MemoryStore {
/// ///
/// // SPARQL query /// // SPARQL query
/// let prepared_query = store.prepare_query("SELECT ?s WHERE { ?s ?p ?o }", QueryOptions::default())?; /// let prepared_query = store.prepare_query("SELECT ?s WHERE { ?s ?p ?o }", QueryOptions::default())?;
/// if let QueryResult::Solutions(mut solutions) = prepared_query.exec()? { /// if let QueryResults::Solutions(mut solutions) = prepared_query.exec()? {
/// assert_eq!(solutions.next().unwrap()?.get("s"), Some(&ex.into())); /// assert_eq!(solutions.next().unwrap()?.get("s"), Some(&ex.into()));
/// } /// }
/// # Result::<_,Box<dyn std::error::Error>>::Ok(()) /// # Result::<_,Box<dyn std::error::Error>>::Ok(())
@ -164,8 +164,8 @@ impl MemoryStore {
/// let quad = Quad::new(ex.clone(), ex.clone(), ex.clone(), None); /// let quad = Quad::new(ex.clone(), ex.clone(), ex.clone(), None);
/// store.insert(quad.clone()); /// store.insert(quad.clone());
/// ///
/// // quad filter /// // quad filter by object
/// let results: Vec<Quad> = store.quads_for_pattern(None, None, None, None).collect(); /// let results: Vec<Quad> = store.quads_for_pattern(None, None, Some((&ex).into()), None).collect();
/// assert_eq!(vec![quad], results); /// assert_eq!(vec![quad], results);
/// # Result::<_,Box<dyn std::error::Error>>::Ok(()) /// # Result::<_,Box<dyn std::error::Error>>::Ok(())
/// ``` /// ```
@ -241,13 +241,11 @@ impl MemoryStore {
/// let ex = NamedNode::new("http://example.com")?; /// let ex = NamedNode::new("http://example.com")?;
/// let quad = Quad::new(ex.clone(), ex.clone(), ex.clone(), None); /// let quad = Quad::new(ex.clone(), ex.clone(), ex.clone(), None);
/// ///
/// // transaction
/// store.transaction(|transaction| { /// store.transaction(|transaction| {
/// transaction.insert(quad.clone()); /// transaction.insert(quad.clone());
/// Ok(()) as Result<(),Infallible> /// Ok(()) as Result<(),Infallible>
/// })?; /// })?;
/// ///
/// // quad filter
/// assert!(store.contains(&quad)); /// assert!(store.contains(&quad));
/// # Result::<_,Box<dyn std::error::Error>>::Ok(()) /// # Result::<_,Box<dyn std::error::Error>>::Ok(())
/// ``` /// ```
@ -289,10 +287,9 @@ impl MemoryStore {
/// let file = b"<http://example.com> <http://example.com> <http://example.com> ."; /// let file = b"<http://example.com> <http://example.com> <http://example.com> .";
/// store.load_graph(file.as_ref(), GraphFormat::NTriples, &GraphName::DefaultGraph, None)?; /// store.load_graph(file.as_ref(), GraphFormat::NTriples, &GraphName::DefaultGraph, None)?;
/// ///
/// // quad filter /// // we inspect the store contents
/// let results: Vec<Quad> = store.quads_for_pattern(None, None, None, None).collect(); /// let ex = NamedNodeRef::new("http://example.com").unwrap();
/// let ex = NamedNode::new("http://example.com")?; /// assert!(store.contains(QuadRef::new(ex, ex, ex, None)));
/// assert_eq!(vec![Quad::new(ex.clone(), ex.clone(), ex.clone(), None)], results);
/// # Result::<_,Box<dyn std::error::Error>>::Ok(()) /// # Result::<_,Box<dyn std::error::Error>>::Ok(())
/// ``` /// ```
/// ///
@ -328,10 +325,9 @@ impl MemoryStore {
/// let file = b"<http://example.com> <http://example.com> <http://example.com> <http://example.com> ."; /// let file = b"<http://example.com> <http://example.com> <http://example.com> <http://example.com> .";
/// store.load_dataset(file.as_ref(), DatasetFormat::NQuads, None)?; /// store.load_dataset(file.as_ref(), DatasetFormat::NQuads, None)?;
/// ///
/// // quad filter /// // we inspect the store contents
/// let results: Vec<Quad> = store.quads_for_pattern(None, None, None, None).collect(); /// let ex = NamedNodeRef::new("http://example.com").unwrap();
/// let ex = NamedNode::new("http://example.com")?; /// assert!(store.contains(QuadRef::new(ex, ex, ex, ex)));
/// assert_eq!(vec![Quad::new(ex.clone(), ex.clone(), ex.clone(), Some(ex.into()))], results);
/// # Result::<_,Box<dyn std::error::Error>>::Ok(()) /// # Result::<_,Box<dyn std::error::Error>>::Ok(())
/// ``` /// ```
/// ///
@ -396,9 +392,6 @@ impl MemoryStore {
/// assert_eq!(file, buffer.as_slice()); /// assert_eq!(file, buffer.as_slice());
/// # std::io::Result::Ok(()) /// # std::io::Result::Ok(())
/// ``` /// ```
///
/// Errors related to parameter validation like the base IRI use the [`InvalidInput`](https://doc.rust-lang.org/std/io/enum.ErrorKind.html#variant.InvalidInput) error kind.
/// Errors related to a bad syntax in the loaded file use the [`InvalidData`](https://doc.rust-lang.org/std/io/enum.ErrorKind.html#variant.InvalidData) or [`UnexpectedEof`](https://doc.rust-lang.org/std/io/enum.ErrorKind.html#variant.UnexpectedEof) error kinds.
pub fn dump_graph<'a>( pub fn dump_graph<'a>(
&self, &self,
writer: impl Write, writer: impl Write,
@ -413,7 +406,7 @@ impl MemoryStore {
) )
} }
/// Dumps the store dataset into a file. /// Dumps the store into a file.
/// ///
/// Usage example: /// Usage example:
/// ``` /// ```
@ -430,9 +423,6 @@ impl MemoryStore {
/// assert_eq!(file, buffer.as_slice()); /// assert_eq!(file, buffer.as_slice());
/// # std::io::Result::Ok(()) /// # std::io::Result::Ok(())
/// ``` /// ```
///
/// Errors related to parameter validation like the base IRI use the [`InvalidInput`](https://doc.rust-lang.org/std/io/enum.ErrorKind.html#variant.InvalidInput) error kind.
/// Errors related to a bad syntax in the loaded file use the [`InvalidData`](https://doc.rust-lang.org/std/io/enum.ErrorKind.html#variant.InvalidData) or [`UnexpectedEof`](https://doc.rust-lang.org/std/io/enum.ErrorKind.html#variant.UnexpectedEof) error kinds.
pub fn dump_dataset(&self, writer: impl Write, format: DatasetFormat) -> Result<(), io::Error> { pub fn dump_dataset(&self, writer: impl Write, format: DatasetFormat) -> Result<(), io::Error> {
dump_dataset( dump_dataset(
self.quads_for_pattern(None, None, None, None).map(Ok), self.quads_for_pattern(None, None, None, None).map(Ok),
@ -1087,7 +1077,7 @@ pub struct MemoryPreparedQuery(SimplePreparedQuery<MemoryStore>);
impl MemoryPreparedQuery { impl MemoryPreparedQuery {
/// Evaluates the query and returns its results /// Evaluates the query and returns its results
pub fn exec(&self) -> Result<QueryResult, EvaluationError> { pub fn exec(&self) -> Result<QueryResults, EvaluationError> {
self.0.exec() self.0.exec()
} }
} }
@ -1119,16 +1109,18 @@ impl MemoryTransaction {
/// transaction.load_graph(file.as_ref(), GraphFormat::NTriples, &GraphName::DefaultGraph, None) /// transaction.load_graph(file.as_ref(), GraphFormat::NTriples, &GraphName::DefaultGraph, None)
/// })?; /// })?;
/// ///
/// // quad filter /// // we inspect the store content
/// let results: Vec<Quad> = store.quads_for_pattern(None, None, None, None).collect(); /// let ex = NamedNodeRef::new("http://example.com").unwrap();
/// let ex = NamedNode::new("http://example.com").unwrap(); /// assert!(store.contains(&Quad::new(ex.clone(), ex.clone(), ex.clone(), ex.clone())));
/// assert_eq!(vec![Quad::new(ex.clone(), ex.clone(), ex.clone(), None)], results);
/// # Result::<_, oxigraph::sparql::EvaluationError>::Ok(()) /// # Result::<_, oxigraph::sparql::EvaluationError>::Ok(())
/// ``` /// ```
/// ///
/// If the file parsing fails in the middle of the file, the triples read before are still /// If the file parsing fails in the middle of the file, the triples read before are still
/// considered by the transaction. Rollback the transaction by making the transaction closure /// considered by the transaction. Rollback the transaction by making the transaction closure
/// return an error if you don't want that. /// return an error if you don't want that.
///
/// Errors related to parameter validation like the base IRI use the [`InvalidInput`](https://doc.rust-lang.org/std/io/enum.ErrorKind.html#variant.InvalidInput) error kind.
/// Errors related to a bad syntax in the loaded file use the [`InvalidData`](https://doc.rust-lang.org/std/io/enum.ErrorKind.html#variant.InvalidData) or [`UnexpectedEof`](https://doc.rust-lang.org/std/io/enum.ErrorKind.html#variant.UnexpectedEof) error kinds.
pub fn load_graph<'a>( pub fn load_graph<'a>(
&mut self, &mut self,
reader: impl BufRead, reader: impl BufRead,
@ -1164,16 +1156,18 @@ impl MemoryTransaction {
/// let file = b"<http://example.com> <http://example.com> <http://example.com> <http://example.com> ."; /// let file = b"<http://example.com> <http://example.com> <http://example.com> <http://example.com> .";
/// store.load_dataset(file.as_ref(), DatasetFormat::NQuads, None)?; /// store.load_dataset(file.as_ref(), DatasetFormat::NQuads, None)?;
/// ///
/// // quad filter /// // we inspect the store content
/// let results: Vec<Quad> = store.quads_for_pattern(None, None, None, None).collect(); /// let ex = NamedNodeRef::new("http://example.com").unwrap();
/// let ex = NamedNode::new("http://example.com").unwrap(); /// assert!(store.contains(QuadRef::new(ex, ex, ex, ex)));
/// assert_eq!(vec![Quad::new(ex.clone(), ex.clone(), ex.clone(), Some(ex.into()))], results);
/// # Result::<_, oxigraph::sparql::EvaluationError>::Ok(()) /// # Result::<_, oxigraph::sparql::EvaluationError>::Ok(())
/// ``` /// ```
/// ///
/// If the file parsing fails in the middle of the file, the quads read before are still /// If the file parsing fails in the middle of the file, the quads read before are still
/// considered by the transaction. Rollback the transaction by making the transaction closure /// considered by the transaction. Rollback the transaction by making the transaction closure
/// return an error if you don't want that. /// return an error if you don't want that.
///
/// Errors related to parameter validation like the base IRI use the [`InvalidInput`](https://doc.rust-lang.org/std/io/enum.ErrorKind.html#variant.InvalidInput) error kind.
/// Errors related to a bad syntax in the loaded file use the [`InvalidData`](https://doc.rust-lang.org/std/io/enum.ErrorKind.html#variant.InvalidData) or [`UnexpectedEof`](https://doc.rust-lang.org/std/io/enum.ErrorKind.html#variant.UnexpectedEof) error kinds.
pub fn load_dataset( pub fn load_dataset(
&mut self, &mut self,
reader: impl BufRead, reader: impl BufRead,

@ -1,7 +1,4 @@
//! RDF quads storage implementations. //! RDF [RDF dataset](https://www.w3.org/TR/rdf11-concepts/#dfn-rdf-dataset) storage implementations.
//!
//! They encode a [RDF dataset](https://www.w3.org/TR/rdf11-concepts/#dfn-rdf-dataset)
//! and allow querying and updating them using SPARQL.
#[cfg(any(feature = "rocksdb", feature = "sled"))] #[cfg(any(feature = "rocksdb", feature = "sled"))]
mod binary_encoder; mod binary_encoder;

@ -3,7 +3,7 @@
use crate::error::invalid_data_error; use crate::error::invalid_data_error;
use crate::io::{DatasetFormat, GraphFormat}; use crate::io::{DatasetFormat, GraphFormat};
use crate::model::*; use crate::model::*;
use crate::sparql::{EvaluationError, Query, QueryOptions, QueryResult, SimplePreparedQuery}; use crate::sparql::{EvaluationError, Query, QueryOptions, QueryResults, SimplePreparedQuery};
use crate::store::binary_encoder::*; use crate::store::binary_encoder::*;
use crate::store::numeric_encoder::{ use crate::store::numeric_encoder::{
Decoder, ReadEncoder, StrContainer, StrEncodingAware, StrLookup, WriteEncoder, Decoder, ReadEncoder, StrContainer, StrEncodingAware, StrLookup, WriteEncoder,
@ -24,7 +24,7 @@ use std::sync::Arc;
use std::{fmt, str}; use std::{fmt, str};
/// Store based on the [RocksDB](https://rocksdb.org/) key-value database. /// Store based on the [RocksDB](https://rocksdb.org/) key-value database.
/// It encodes a [RDF dataset](https://www.w3.org/TR/rdf11-concepts/#dfn-rdf-dataset) and allows to query and update it using SPARQL. /// It encodes a [RDF dataset](https://www.w3.org/TR/rdf11-concepts/#dfn-rdf-dataset) and allows to query it using SPARQL.
/// ///
/// To use it, the `"rocksdb"` feature needs to be activated. /// To use it, the `"rocksdb"` feature needs to be activated.
/// ///
@ -32,7 +32,7 @@ use std::{fmt, str};
/// ``` /// ```
/// use oxigraph::RocksDbStore; /// use oxigraph::RocksDbStore;
/// use oxigraph::model::*; /// use oxigraph::model::*;
/// use oxigraph::sparql::{QueryOptions, QueryResult}; /// use oxigraph::sparql::{QueryOptions, QueryResults};
/// # use std::fs::remove_dir_all; /// # use std::fs::remove_dir_all;
/// ///
/// # { /// # {
@ -48,7 +48,7 @@ use std::{fmt, str};
/// assert_eq!(vec![quad], results?); /// assert_eq!(vec![quad], results?);
/// ///
/// // SPARQL query /// // SPARQL query
/// if let QueryResult::Solutions(mut solutions) = store.query("SELECT ?s WHERE { ?s ?p ?o }", QueryOptions::default())? { /// if let QueryResults::Solutions(mut solutions) = store.query("SELECT ?s WHERE { ?s ?p ?o }", QueryOptions::default())? {
/// assert_eq!(solutions.next().unwrap()?.get("s"), Some(&ex.into())); /// assert_eq!(solutions.next().unwrap()?.get("s"), Some(&ex.into()));
/// } /// }
/// # /// #
@ -126,7 +126,7 @@ impl RocksDbStore {
&self, &self,
query: impl TryInto<Query, Error = impl Into<EvaluationError>>, query: impl TryInto<Query, Error = impl Into<EvaluationError>>,
options: QueryOptions, options: QueryOptions,
) -> Result<QueryResult, EvaluationError> { ) -> Result<QueryResults, EvaluationError> {
self.prepare_query(query, options)?.exec() self.prepare_query(query, options)?.exec()
} }
@ -314,7 +314,7 @@ impl RocksDbStore {
) )
} }
/// Dumps the store dataset into a file. /// Dumps the store into a file.
/// ///
/// See [`MemoryStore`](../memory/struct.MemoryStore.html#method.dump_dataset) for a usage example. /// See [`MemoryStore`](../memory/struct.MemoryStore.html#method.dump_dataset) for a usage example.
pub fn dump_dataset(&self, writer: impl Write, syntax: DatasetFormat) -> Result<(), io::Error> { pub fn dump_dataset(&self, writer: impl Write, syntax: DatasetFormat) -> Result<(), io::Error> {
@ -729,7 +729,7 @@ pub struct RocksDbPreparedQuery(SimplePreparedQuery<RocksDbStore>);
impl RocksDbPreparedQuery { impl RocksDbPreparedQuery {
/// Evaluates the query and returns its results /// Evaluates the query and returns its results
pub fn exec(&self) -> Result<QueryResult, EvaluationError> { pub fn exec(&self) -> Result<QueryResults, EvaluationError> {
self.0.exec() self.0.exec()
} }
} }

@ -3,7 +3,7 @@
use crate::error::invalid_data_error; use crate::error::invalid_data_error;
use crate::io::{DatasetFormat, GraphFormat}; use crate::io::{DatasetFormat, GraphFormat};
use crate::model::*; use crate::model::*;
use crate::sparql::{EvaluationError, Query, QueryOptions, QueryResult, SimplePreparedQuery}; use crate::sparql::{EvaluationError, Query, QueryOptions, QueryResults, SimplePreparedQuery};
use crate::store::binary_encoder::*; use crate::store::binary_encoder::*;
use crate::store::numeric_encoder::{ use crate::store::numeric_encoder::{
Decoder, ReadEncoder, StrContainer, StrEncodingAware, StrLookup, WriteEncoder, Decoder, ReadEncoder, StrContainer, StrEncodingAware, StrLookup, WriteEncoder,
@ -25,7 +25,7 @@ use std::path::Path;
use std::{fmt, io, str}; use std::{fmt, io, str};
/// Store based on the [Sled](https://sled.rs/) key-value database. /// Store based on the [Sled](https://sled.rs/) key-value database.
/// It encodes a [RDF dataset](https://www.w3.org/TR/rdf11-concepts/#dfn-rdf-dataset) and allows to query and update it using SPARQL. /// It encodes a [RDF dataset](https://www.w3.org/TR/rdf11-concepts/#dfn-rdf-dataset) and allows to query it using SPARQL.
/// ///
/// To use it, the `"sled"` feature needs to be activated. /// To use it, the `"sled"` feature needs to be activated.
/// ///
@ -34,7 +34,7 @@ use std::{fmt, io, str};
/// Usage example: /// Usage example:
/// ``` /// ```
/// use oxigraph::SledStore; /// use oxigraph::SledStore;
/// use oxigraph::sparql::{QueryOptions, QueryResult}; /// use oxigraph::sparql::{QueryOptions, QueryResults};
/// use oxigraph::model::*; /// use oxigraph::model::*;
/// # use std::fs::remove_dir_all; /// # use std::fs::remove_dir_all;
/// ///
@ -51,7 +51,7 @@ use std::{fmt, io, str};
/// assert_eq!(vec![quad], results?); /// assert_eq!(vec![quad], results?);
/// ///
/// // SPARQL query /// // SPARQL query
/// if let QueryResult::Solutions(mut solutions) = store.query("SELECT ?s WHERE { ?s ?p ?o }", QueryOptions::default())? { /// if let QueryResults::Solutions(mut solutions) = store.query("SELECT ?s WHERE { ?s ?p ?o }", QueryOptions::default())? {
/// assert_eq!(solutions.next().unwrap()?.get("s"), Some(&ex.into())); /// assert_eq!(solutions.next().unwrap()?.get("s"), Some(&ex.into()));
/// }; /// };
/// # /// #
@ -136,7 +136,7 @@ impl SledStore {
&self, &self,
query: impl TryInto<Query, Error = impl Into<EvaluationError>>, query: impl TryInto<Query, Error = impl Into<EvaluationError>>,
options: QueryOptions, options: QueryOptions,
) -> Result<QueryResult, EvaluationError> { ) -> Result<QueryResults, EvaluationError> {
self.prepare_query(query, options)?.exec() self.prepare_query(query, options)?.exec()
} }
@ -348,7 +348,7 @@ impl SledStore {
) )
} }
/// Dumps the store dataset into a file. /// Dumps the store into a file.
/// ///
/// See [`MemoryStore`](../memory/struct.MemoryStore.html#method.dump_dataset) for a usage example. /// See [`MemoryStore`](../memory/struct.MemoryStore.html#method.dump_dataset) for a usage example.
pub fn dump_dataset(&self, writer: impl Write, format: DatasetFormat) -> Result<(), io::Error> { pub fn dump_dataset(&self, writer: impl Write, format: DatasetFormat) -> Result<(), io::Error> {
@ -1161,7 +1161,7 @@ pub struct SledPreparedQuery(SimplePreparedQuery<SledStore>);
impl SledPreparedQuery { impl SledPreparedQuery {
/// Evaluates the query and returns its results /// Evaluates the query and returns its results
pub fn exec(&self) -> Result<QueryResult, EvaluationError> { pub fn exec(&self) -> Result<QueryResults, EvaluationError> {
self.0.exec() self.0.exec()
} }
} }

@ -12,7 +12,7 @@ use pyo3::{PyIterProtocol, PyObjectProtocol, PySequenceProtocol};
use std::io::BufReader; use std::io::BufReader;
/// In-memory store. /// In-memory store.
/// It encodes a `RDF dataset <https://www.w3.org/TR/rdf11-concepts/#dfn-rdf-dataset>`_ and allows to query and update it using SPARQL. /// It encodes a `RDF dataset <https://www.w3.org/TR/rdf11-concepts/#dfn-rdf-dataset>`_ and allows to query it using SPARQL.
/// ///
/// ///
/// The :py:func:`str` function provides a serialization of the store data compatible with NTriples, Turtle and SPARQL: /// The :py:func:`str` function provides a serialization of the store data compatible with NTriples, Turtle and SPARQL:

@ -14,7 +14,7 @@ use std::io::BufReader;
/// Store based on the `Sled <https://sled.rs/>`_ key-value database. /// Store based on the `Sled <https://sled.rs/>`_ key-value database.
/// ///
/// In-memory store. /// In-memory store.
/// It encodes a `RDF dataset <https://www.w3.org/TR/rdf11-concepts/#dfn-rdf-dataset>`_ and allows to query and update it using SPARQL. /// It encodes a `RDF dataset <https://www.w3.org/TR/rdf11-concepts/#dfn-rdf-dataset>`_ and allows to query it using SPARQL.
/// ///
/// :param path: the path of the directory in which Sled should read and write its data. If the directoty does not exist, it is created. If no directory is provided a temporary one is created and removed when the Python garbage collector removes the store. /// :param path: the path of the directory in which Sled should read and write its data. If the directoty does not exist, it is created. If no directory is provided a temporary one is created and removed when the Python garbage collector removes the store.
/// :type path: str or None /// :type path: str or None

@ -1,7 +1,7 @@
use crate::model::*; use crate::model::*;
use oxigraph::model::*; use oxigraph::model::*;
use oxigraph::sparql::{ use oxigraph::sparql::{
EvaluationError, QueryResult, QuerySolution, QuerySolutionsIterator, QueryTriplesIterator, EvaluationError, QueryResults, QuerySolution, QuerySolutionIter, QueryTripleIter,
}; };
use pyo3::exceptions::{IOError, RuntimeError, SyntaxError, TypeError, ValueError}; use pyo3::exceptions::{IOError, RuntimeError, SyntaxError, TypeError, ValueError};
use pyo3::prelude::*; use pyo3::prelude::*;
@ -47,11 +47,11 @@ pub fn extract_quads_pattern(
)) ))
} }
pub fn query_results_to_python(py: Python<'_>, results: QueryResult) -> PyResult<PyObject> { pub fn query_results_to_python(py: Python<'_>, results: QueryResults) -> PyResult<PyObject> {
Ok(match results { Ok(match results {
QueryResult::Solutions(inner) => QuerySolutionIter { inner }.into_py(py), QueryResults::Solutions(inner) => PyQuerySolutionIter { inner }.into_py(py),
QueryResult::Graph(inner) => TripleResultIter { inner }.into_py(py), QueryResults::Graph(inner) => PyQueryTripleIter { inner }.into_py(py),
QueryResult::Boolean(b) => b.into_py(py), QueryResults::Boolean(b) => b.into_py(py),
}) })
} }
@ -102,13 +102,13 @@ impl PyMappingProtocol for PyQuerySolution {
} }
} }
#[pyclass(unsendable)] #[pyclass(unsendable, name = QuerySolutionIter)]
pub struct QuerySolutionIter { pub struct PyQuerySolutionIter {
inner: QuerySolutionsIterator, inner: QuerySolutionIter,
} }
#[pyproto] #[pyproto]
impl PyIterProtocol for QuerySolutionIter { impl PyIterProtocol for PyQuerySolutionIter {
fn __iter__(slf: PyRefMut<Self>) -> Py<Self> { fn __iter__(slf: PyRefMut<Self>) -> Py<Self> {
slf.into() slf.into()
} }
@ -123,13 +123,13 @@ impl PyIterProtocol for QuerySolutionIter {
} }
} }
#[pyclass(unsendable)] #[pyclass(unsendable, name = QueryTripleIter)]
pub struct TripleResultIter { pub struct PyQueryTripleIter {
inner: QueryTriplesIterator, inner: QueryTripleIter,
} }
#[pyproto] #[pyproto]
impl PyIterProtocol for TripleResultIter { impl PyIterProtocol for PyQueryTripleIter {
fn __iter__(slf: PyRefMut<Self>) -> Py<Self> { fn __iter__(slf: PyRefMut<Self>) -> Py<Self> {
slf.into() slf.into()
} }

@ -22,7 +22,7 @@ use http_types::{
}; };
use oxigraph::io::{DatasetFormat, GraphFormat}; use oxigraph::io::{DatasetFormat, GraphFormat};
use oxigraph::model::{GraphName, NamedNode}; use oxigraph::model::{GraphName, NamedNode};
use oxigraph::sparql::{Query, QueryOptions, QueryResult, QueryResultFormat, ServiceHandler}; use oxigraph::sparql::{Query, QueryOptions, QueryResults, QueryResultsFormat, ServiceHandler};
use oxigraph::RocksDbStore; use oxigraph::RocksDbStore;
use std::fmt; use std::fmt;
use std::io::{BufReader, Cursor}; use std::io::{BufReader, Cursor};
@ -183,7 +183,7 @@ async fn evaluate_sparql_query(
let options = QueryOptions::default().with_service_handler(HttpService::default()); let options = QueryOptions::default().with_service_handler(HttpService::default());
let results = store.query(query, options)?; let results = store.query(query, options)?;
//TODO: stream //TODO: stream
if let QueryResult::Graph(_) = results { if let QueryResults::Graph(_) = results {
let format = content_negotiation( let format = content_negotiation(
request, request,
&[ &[
@ -202,10 +202,10 @@ async fn evaluate_sparql_query(
let format = content_negotiation( let format = content_negotiation(
request, request,
&[ &[
QueryResultFormat::Xml.media_type(), QueryResultsFormat::Xml.media_type(),
QueryResultFormat::Json.media_type(), QueryResultsFormat::Json.media_type(),
], ],
QueryResultFormat::from_media_type, QueryResultsFormat::from_media_type,
)?; )?;
let mut body = Vec::default(); let mut body = Vec::default();
results.write(&mut body, format)?; results.write(&mut body, format)?;
@ -325,7 +325,7 @@ impl ServiceHandler for HttpService {
&self, &self,
service_name: NamedNode, service_name: NamedNode,
query: Query, query: Query,
) -> std::result::Result<QueryResult, HttpServiceError> { ) -> std::result::Result<QueryResults, HttpServiceError> {
let mut request = Request::new( let mut request = Request::new(
Method::Post, Method::Post,
Url::parse(service_name.as_str()).map_err(Error::from)?, Url::parse(service_name.as_str()).map_err(Error::from)?,
@ -343,7 +343,7 @@ impl ServiceHandler for HttpService {
let (content_type, data) = response?; let (content_type, data) = response?;
let syntax = if let Some(content_type) = content_type { let syntax = if let Some(content_type) = content_type {
QueryResultFormat::from_media_type(content_type.essence()).ok_or_else(|| { QueryResultsFormat::from_media_type(content_type.essence()).ok_or_else(|| {
format_err!( format_err!(
"Unexpected federated query result type from {}: {}", "Unexpected federated query result type from {}: {}",
service_name, service_name,
@ -351,9 +351,9 @@ impl ServiceHandler for HttpService {
) )
})? })?
} else { } else {
QueryResultFormat::Xml QueryResultsFormat::Xml
}; };
Ok(QueryResult::read(Cursor::new(data), syntax).map_err(Error::from)?) Ok(QueryResults::read(Cursor::new(data), syntax).map_err(Error::from)?)
} }
} }

@ -131,12 +131,12 @@ fn evaluate_sparql_test(test: &Test) -> Result<()> {
fn load_sparql_query_result(url: &str) -> Result<StaticQueryResults> { fn load_sparql_query_result(url: &str) -> Result<StaticQueryResults> {
if url.ends_with(".srx") { if url.ends_with(".srx") {
StaticQueryResults::from_query_results( StaticQueryResults::from_query_results(
QueryResult::read(read_file(url)?, QueryResultFormat::Xml)?, QueryResults::read(read_file(url)?, QueryResultsFormat::Xml)?,
false, false,
) )
} else if url.ends_with(".srj") { } else if url.ends_with(".srj") {
StaticQueryResults::from_query_results( StaticQueryResults::from_query_results(
QueryResult::read(read_file(url)?, QueryResultFormat::Json)?, QueryResults::read(read_file(url)?, QueryResultsFormat::Json)?,
false, false,
) )
} else { } else {
@ -174,7 +174,7 @@ impl ServiceHandler for StaticServiceHandler {
&self, &self,
service_name: NamedNode, service_name: NamedNode,
query: Query, query: Query,
) -> std::result::Result<QueryResult, EvaluationError> { ) -> std::result::Result<QueryResults, EvaluationError> {
self.services self.services
.get(&service_name) .get(&service_name)
.ok_or_else(|| { .ok_or_else(|| {
@ -190,12 +190,12 @@ impl ServiceHandler for StaticServiceHandler {
} }
} }
fn to_dataset(result: QueryResult, with_order: bool) -> Result<MemoryStore> { fn to_dataset(result: QueryResults, with_order: bool) -> Result<MemoryStore> {
match result { match result {
QueryResult::Graph(graph) => Ok(graph QueryResults::Graph(graph) => Ok(graph
.map(|t| t.map(|t| t.in_graph(None))) .map(|t| t.map(|t| t.in_graph(None)))
.collect::<Result<_, _>>()?), .collect::<Result<_, _>>()?),
QueryResult::Boolean(value) => { QueryResults::Boolean(value) => {
let store = MemoryStore::new(); let store = MemoryStore::new();
let result_set = BlankNode::default(); let result_set = BlankNode::default();
store.insert(Quad::new( store.insert(Quad::new(
@ -212,7 +212,7 @@ fn to_dataset(result: QueryResult, with_order: bool) -> Result<MemoryStore> {
)); ));
Ok(store) Ok(store)
} }
QueryResult::Solutions(solutions) => { QueryResults::Solutions(solutions) => {
let store = MemoryStore::new(); let store = MemoryStore::new();
let result_set = BlankNode::default(); let result_set = BlankNode::default();
store.insert(Quad::new( store.insert(Quad::new(
@ -363,7 +363,7 @@ impl fmt::Display for StaticQueryResults {
} }
impl StaticQueryResults { impl StaticQueryResults {
fn from_query_results(results: QueryResult, with_order: bool) -> Result<StaticQueryResults> { fn from_query_results(results: QueryResults, with_order: bool) -> Result<StaticQueryResults> {
Ok(Self::from_dataset(to_dataset(results, with_order)?)) Ok(Self::from_dataset(to_dataset(results, with_order)?))
} }

@ -22,7 +22,7 @@ use http_types::{
}; };
use oxigraph::io::GraphFormat; use oxigraph::io::GraphFormat;
use oxigraph::model::NamedNode; use oxigraph::model::NamedNode;
use oxigraph::sparql::{Query, QueryOptions, QueryResult, QueryResultFormat, ServiceHandler}; use oxigraph::sparql::{Query, QueryOptions, QueryResults, QueryResultsFormat, ServiceHandler};
use oxigraph::RocksDbStore; use oxigraph::RocksDbStore;
use std::fmt; use std::fmt;
use std::io::Cursor; use std::io::Cursor;
@ -191,7 +191,7 @@ async fn evaluate_sparql_query(
.with_default_graph_as_union() .with_default_graph_as_union()
.with_service_handler(HttpService::default()); .with_service_handler(HttpService::default());
let results = store.query(query, options)?; let results = store.query(query, options)?;
if let QueryResult::Graph(_) = results { if let QueryResults::Graph(_) = results {
let format = content_negotiation( let format = content_negotiation(
request, request,
&[ &[
@ -210,10 +210,10 @@ async fn evaluate_sparql_query(
let format = content_negotiation( let format = content_negotiation(
request, request,
&[ &[
QueryResultFormat::Xml.media_type(), QueryResultsFormat::Xml.media_type(),
QueryResultFormat::Json.media_type(), QueryResultsFormat::Json.media_type(),
], ],
QueryResultFormat::from_media_type, QueryResultsFormat::from_media_type,
)?; )?;
let mut body = Vec::default(); let mut body = Vec::default();
results.write(&mut body, format)?; results.write(&mut body, format)?;
@ -315,7 +315,7 @@ impl ServiceHandler for HttpService {
&self, &self,
service_name: NamedNode, service_name: NamedNode,
query: Query, query: Query,
) -> std::result::Result<QueryResult, HttpServiceError> { ) -> std::result::Result<QueryResults, HttpServiceError> {
let mut request = Request::new( let mut request = Request::new(
Method::Post, Method::Post,
Url::parse(service_name.as_str()).map_err(Error::from)?, Url::parse(service_name.as_str()).map_err(Error::from)?,
@ -333,7 +333,7 @@ impl ServiceHandler for HttpService {
let (content_type, data) = response?; let (content_type, data) = response?;
let syntax = if let Some(content_type) = content_type { let syntax = if let Some(content_type) = content_type {
QueryResultFormat::from_media_type(content_type.essence()).ok_or_else(|| { QueryResultsFormat::from_media_type(content_type.essence()).ok_or_else(|| {
format_err!( format_err!(
"Unexpected federated query result type from {}: {}", "Unexpected federated query result type from {}: {}",
service_name, service_name,
@ -341,9 +341,9 @@ impl ServiceHandler for HttpService {
) )
})? })?
} else { } else {
QueryResultFormat::Xml QueryResultsFormat::Xml
}; };
Ok(QueryResult::read(Cursor::new(data), syntax).map_err(Error::from)?) Ok(QueryResults::read(Cursor::new(data), syntax).map_err(Error::from)?)
} }
} }

Loading…
Cancel
Save