diff --git a/js/README.md b/js/README.md index c67f6161..95263f70 100644 --- a/js/README.md +++ b/js/README.md @@ -10,6 +10,9 @@ Oxigraph is a work in progress graph database written in Rust implementing the [ 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. +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/). + + It is distributed using a [a NPM package](https://www.npmjs.com/package/oxigraph) that should work with nodeJS. ```bash @@ -116,6 +119,26 @@ if (store.query("ASK { ?s ?s ?s }")) { } ``` +### `MemoryStore.prototype.load(String data, String mimeType, NamedNode|String? baseIRI, NamedNode|BlankNode|DefaultGraph? toNamedGraph)` + +Loads serialized RDF triples or quad into the store. +The method arguments are: +1. `data`: the serialized RDF triples or quads. +2. `mimeType`: the MIME type of the serialization. See below for the supported mime types. +3. `baseIRI`: the base IRI to use to resolve the relative IRIs in the serialization. +4. `toNamedGraph`: for triple serialization formats, the name of the named graph the triple should be loaded to. + +The available formats are: +* [Turtle](https://www.w3.org/TR/turtle/): `text/turtle` +* [TriG](https://www.w3.org/TR/trig/): `application/trig` +* [N-Triples](https://www.w3.org/TR/n-triples/): `application/n-triples` +* [N-Quads](https://www.w3.org/TR/n-quads/): `application/n-quads` +* [RDF XML](https://www.w3.org/TR/rdf-syntax-grammar/): `application/rdf+xml` + +Example of loading a Turtle file into the named graph `` with the base IRI `http://example.com`: +```js +store.load(" <> .", "text/turtle", "http://example.com", store.dataFactory.namedNode("http://example.com/graph")); +``` ## Example diff --git a/js/src/model.rs b/js/src/model.rs index 43712322..4fbcfbff 100644 --- a/js/src/model.rs +++ b/js/src/model.rs @@ -101,7 +101,7 @@ impl JsDataFactory { #[wasm_bindgen(js_name = fromQuad)] pub fn convert_quad(&self, original: &JsValue) -> Result { - Ok(self.from_js.to_quad(original)?.into()) + Ok(self.from_js.to_quad(original)?) } } diff --git a/js/src/store.rs b/js/src/store.rs index d595e335..c29481ed 100644 --- a/js/src/store.rs +++ b/js/src/store.rs @@ -2,9 +2,14 @@ use crate::format_err; use crate::model::*; use crate::utils::to_err; use js_sys::{Array, Map}; +use oxigraph::model::NamedOrBlankNode; use oxigraph::sparql::{PreparedQuery, QueryOptions, QueryResult}; -use oxigraph::{Error, MemoryRepository, Repository, RepositoryConnection}; +use oxigraph::{ + DatasetSyntax, Error, FileSyntax, GraphSyntax, MemoryRepository, Repository, + RepositoryConnection, +}; use std::convert::TryInto; +use std::io::Cursor; use wasm_bindgen::prelude::*; #[wasm_bindgen(js_name = MemoryStore)] @@ -154,4 +159,63 @@ impl JsMemoryStore { }; Ok(output) } + + pub fn load( + &self, + data: &str, + mime_type: &str, + base_iri: &JsValue, + to_graph_name: &JsValue, + ) -> Result<(), JsValue> { + let base_iri = if base_iri.is_null() || base_iri.is_undefined() { + None + } else if base_iri.is_string() { + base_iri.as_string() + } else if let JsTerm::NamedNode(base_iri) = self.from_js.to_term(&base_iri)? { + Some(base_iri.value()) + } else { + return Err(format_err!( + "If provided, the base IRI should be a NamedNode or a string" + )); + }; + + let to_graph_name: Option = + match self.from_js.to_optional_term(to_graph_name)? { + Some(JsTerm::NamedNode(node)) => Some(node.into()), + Some(JsTerm::BlankNode(node)) => Some(node.into()), + Some(JsTerm::DefaultGraph(_)) => None, + Some(_) => { + return Err(format_err!( + "If provided, the target graph name should be a NamedNode or a BlankNode" + )) + } + None => None, + }; + + if let Some(graph_syntax) = GraphSyntax::from_mime_type(mime_type) { + self.store + .connection() + .map_err(to_err)? + .load_graph( + Cursor::new(data), + graph_syntax, + to_graph_name.as_ref(), + base_iri.as_deref(), + ) + .map_err(to_err) + } else if let Some(dataset_syntax) = DatasetSyntax::from_mime_type(mime_type) { + if to_graph_name.is_some() { + return Err(format_err!( + "The target graph name parameter is not available for dataset formats" + )); + } + self.store + .connection() + .map_err(to_err)? + .load_dataset(Cursor::new(data), dataset_syntax, base_iri.as_deref()) + .map_err(to_err) + } else { + Err(format_err!("Not supported MIME type: {}", mime_type)) + } + } } diff --git a/js/test/store.js b/js/test/store.js index dbe9113a..7e0b55d9 100644 --- a/js/test/store.js +++ b/js/test/store.js @@ -63,4 +63,36 @@ describe('MemoryStore', function() { assert(ex.equals(results[0].get("s"))); }); }); + + describe('#load()', function() { + it('load NTriples in the default graph', function() { + const store = new MemoryStore(); + store.load(" .", "application/n-triples"); + assert(store.has(dataFactory.triple(ex, ex, ex))); + }); + + it('load NTriples in an other graph', function() { + const store = new MemoryStore(); + store.load(" .", "application/n-triples", null, ex); + assert(store.has(dataFactory.quad(ex, ex, ex, ex))); + }); + + it('load Turtle with a base IRI', function() { + const store = new MemoryStore(); + store.load(" <> .", "text/turtle", "http://example.com"); + assert(store.has(dataFactory.triple(ex, ex, ex))); + }); + + it('load NQuads', function() { + const store = new MemoryStore(); + store.load(" .", "application/n-quads"); + assert(store.has(dataFactory.quad(ex, ex, ex, ex))); + }); + + it('load TriG with a base IRI', function() { + const store = new MemoryStore(); + store.load("GRAPH <> { <> }", "application/trig", "http://example.com"); + assert(store.has(dataFactory.quad(ex, ex, ex, ex))); + }); + }); });