# Lightning fast, asynchronous, streaming RDF for JavaScript
[![Build Status](https://github.com/rdfjs/N3.js/workflows/CI/badge.svg)](https://github.com/rdfjs/N3.js/actions)
[![Coverage Status](https://coveralls.io/repos/github/rdfjs/N3.js/badge.svg)](https://coveralls.io/github/rdfjs/N3.js)
[![npm version](https://badge.fury.io/js/n3.svg)](https://www.npmjs.com/package/n3)
[![DOI](https://zenodo.org/badge/3058202.svg)](https://zenodo.org/badge/latestdoi/3058202)
The N3.js library is an implementation of the [RDF.js low-level specification](http://rdf.js.org/) that lets you handle [RDF](https://www.w3.org/TR/rdf-primer/) in JavaScript easily.
It offers:
- [**Parsing**](#parsing) triples/quads from
[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/),
[RDF*](https://blog.liu.se/olafhartig/2019/01/10/position-statement-rdf-star-and-sparql-star/)
and [Notation3 (N3)](https://www.w3.org/TeamSubmission/n3/)
- [**Writing**](#writing) triples/quads to
[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*](https://blog.liu.se/olafhartig/2019/01/10/position-statement-rdf-star-and-sparql-star/)
- [**Storage**](#storing) of triples/quads in memory
Parsing and writing is:
- 🎛 **asynchronous** – triples arrive as soon as possible
- 🚰 **streaming** – streams are parsed as data comes in, so you can parse files larger than memory
- ⚡️ **fast** – triples are flying out at high speeds
## Installation
For Node.js, N3.js comes as an [npm package](https://npmjs.org/package/n3).
```Bash
$ npm install n3
```
```JavaScript
const N3 = require('n3');
```
N3.js seamlessly works in browsers via [webpack](https://webpack.js.org/)
or [browserify](http://browserify.org/).
If you're unfamiliar with these tools,
you can read
[_webpack: Creating a Bundle – getting started_](https://webpack.js.org/guides/getting-started/#creating-a-bundle)
or
[_Introduction to browserify_](https://writingjavascript.org/posts/introduction-to-browserify).
You will need to create a "UMD bundle" and supply a name (e.g. with the `-s N3` option in browserify).
You can also load it via CDN:
```html
```
## Creating triples/quads
N3.js follows the [RDF.js low-level specification](http://rdf.js.org/).
`N3.DataFactory` will give you the [factory](http://rdf.js.org/#datafactory-interface) functions to create triples and quads:
```JavaScript
const { DataFactory } = N3;
const { namedNode, literal, defaultGraph, quad } = DataFactory;
const myQuad = quad(
namedNode('https://ruben.verborgh.org/profile/#me'), // Subject
namedNode('http://xmlns.com/foaf/0.1/givenName'), // Predicate
literal('Ruben', 'en'), // Object
defaultGraph(), // Graph
);
console.log(myQuad.termType); // Quad
console.log(myQuad.value); // ''
console.log(myQuad.subject.value); // https://ruben.verborgh.org/profile/#me
console.log(myQuad.object.value); // Ruben
console.log(myQuad.object.datatype.value); // http://www.w3.org/1999/02/22-rdf-syntax-ns#langString
console.log(myQuad.object.language); // en
```
In the rest of this document, we will treat “triples” and “quads” equally:
we assume that a quad is simply a triple in a named or default graph.
## Parsing
### From an RDF document to quads
`N3.Parser` transforms Turtle, TriG, N-Triples, or N-Quads document into quads through a callback:
```JavaScript
const parser = new N3.Parser();
parser.parse(
`PREFIX c:
c:Tom a c:Cat.
c:Jerry a c:Mouse;
c:smarterThan c:Tom.`,
(error, quad, prefixes) => {
if (quad)
console.log(quad);
else
console.log("# That's all, folks!", prefixes);
});
```
The callback's first argument is an optional error value, the second is a quad.
If there are no more quads,
the callback is invoked one last time with `null` for `quad`
and a hash of prefixes as third argument.
Pass a second callback to `parse` to retrieve prefixes as they are read.
If no callbacks are provided, parsing happens synchronously.
By default, `N3.Parser` parses a permissive superset of Turtle, TriG, N-Triples, and N-Quads.
For strict compatibility with any of those languages, pass a `format` argument upon creation:
```JavaScript
const parser1 = new N3.Parser({ format: 'N-Triples' });
const parser2 = new N3.Parser({ format: 'application/trig' });
```
Notation3 (N3) is supported _only_ through the `format` argument:
```JavaScript
const parser3 = new N3.Parser({ format: 'N3' });
const parser4 = new N3.Parser({ format: 'Notation3' });
const parser5 = new N3.Parser({ format: 'text/n3' });
```
It is possible to provide the base IRI of the document that you want to parse.
This is done by passing a `baseIRI` argument upon creation:
```JavaScript
const parser = new N3.Parser({ baseIRI: 'http://example.org/' });
```
By default, `N3.Parser` will prefix blank node labels with a `b{digit}_` prefix.
This is done to prevent collisions of unrelated blank nodes having identical
labels. The `blankNodePrefix` constructor argument can be used to modify the
prefix or, if set to an empty string, completely disable prefixing:
```JavaScript
const parser = new N3.Parser({ blankNodePrefix: '' });
```
### From an RDF stream to quads
`N3.Parser` can parse [Node.js streams](http://nodejs.org/api/stream.html) as they grow,
returning quads as soon as they're ready.
```JavaScript
const parser = new N3.Parser(),
rdfStream = fs.createReadStream('cartoons.ttl');
parser.parse(rdfStream, console.log);
```
`N3.StreamParser` is a [Node.js stream](http://nodejs.org/api/stream.html) and [RDF.js Sink](http://rdf.js.org/#sink-interface) implementation.
This solution is ideal if your consumer is slower,
since source data is only read when the consumer is ready.
```JavaScript
const streamParser = new N3.StreamParser(),
rdfStream = fs.createReadStream('cartoons.ttl');
rdfStream.pipe(streamParser);
streamParser.pipe(new SlowConsumer());
function SlowConsumer() {
const writer = new require('stream').Writable({ objectMode: true });
writer._write = (quad, encoding, done) => {
console.log(quad);
setTimeout(done, 1000);
};
return writer;
}
```
A dedicated `prefix` event signals every prefix with `prefix` and `term` arguments.
## Writing
### From quads to a string
`N3.Writer` serializes quads as an RDF document.
Write quads through `addQuad`.
```JavaScript
const writer = new N3.Writer({ prefixes: { c: 'http://example.org/cartoons#' } }); // Create a writer which uses `c` as a prefix for the namespace `http://example.org/cartoons#`
writer.addQuad(
namedNode('http://example.org/cartoons#Tom'), // Subject
namedNode('http://www.w3.org/1999/02/22-rdf-syntax-ns#type'), // Predicate
namedNode('http://example.org/cartoons#Cat') // Object
);
writer.addQuad(quad(
namedNode('http://example.org/cartoons#Tom'), // Subject
namedNode('http://example.org/cartoons#name'), // Predicate
literal('Tom') // Object
));
writer.end((error, result) => console.log(result));
```
By default, `N3.Writer` writes Turtle (or TriG if some quads are in a named graph).
To write N-Triples (or N-Quads) instead, pass a `format` argument upon creation:
```JavaScript
const writer1 = new N3.Writer({ format: 'N-Triples' });
const writer2 = new N3.Writer({ format: 'application/trig' });
```
### From quads to an RDF stream
`N3.Writer` can also write quads to a Node.js stream.
```JavaScript
const writer = new N3.Writer(process.stdout, { end: false, prefixes: { c: 'http://example.org/cartoons#' } });
writer.addQuad(
namedNode('http://example.org/cartoons#Tom'), // Subject
namedNode('http://www.w3.org/1999/02/22-rdf-syntax-ns#type'), // Predicate
namedNode('http://example.org/cartoons#Cat') // Object
);
writer.addQuad(quad(
namedNode('http://example.org/cartoons#Tom'), // Subject
namedNode('http://example.org/cartoons#name'), // Predicate
literal('Tom') // Object
));
writer.end();
```
### From a quad stream to an RDF stream
`N3.StreamWriter` is a [Node.js stream](http://nodejs.org/api/stream.html) and [RDF.js Sink](http://rdf.js.org/#sink-interface) implementation.
```JavaScript
const streamParser = new N3.StreamParser(),
inputStream = fs.createReadStream('cartoons.ttl'),
streamWriter = new N3.StreamWriter({ prefixes: { c: 'http://example.org/cartoons#' } });
inputStream.pipe(streamParser);
streamParser.pipe(streamWriter);
streamWriter.pipe(process.stdout);
```
### Blank nodes and lists
You might want to use the `[…]` and list `(…)` notations of Turtle and TriG.
However, a streaming writer cannot create these automatically:
the shorthand notations are only possible if blank nodes or list heads are not used later on,
which can only be determined conclusively at the end of the stream.
The `blank` and `list` functions allow you to create them manually instead:
```JavaScript
const writer = new N3.Writer({ prefixes: { c: 'http://example.org/cartoons#',
foaf: 'http://xmlns.com/foaf/0.1/' } });
writer.addQuad(
writer.blank(
namedNode('http://xmlns.com/foaf/0.1/givenName'),
literal('Tom', 'en')),
namedNode('http://www.w3.org/1999/02/22-rdf-syntax-ns#type'),
namedNode('http://example.org/cartoons#Cat')
);
writer.addQuad(quad(
namedNode('http://example.org/cartoons#Jerry'),
namedNode('http://xmlns.com/foaf/0.1/knows'),
writer.blank([{
predicate: namedNode('http://www.w3.org/1999/02/22-rdf-syntax-ns#type'),
object: namedNode('http://example.org/cartoons#Cat'),
},{
predicate: namedNode('http://xmlns.com/foaf/0.1/givenName'),
object: literal('Tom', 'en'),
}])
));
writer.addQuad(
namedNode('http://example.org/cartoons#Mammy'),
namedNode('http://example.org/cartoons#hasPets'),
writer.list([
namedNode('http://example.org/cartoons#Tom'),
namedNode('http://example.org/cartoons#Jerry'),
])
);
writer.end((error, result) => console.log(result));
```
## Storing
`N3.Store` allows you to store triples in memory and find them fast.
In this example, we create a new store and add the triples `:Pluto a :Dog.` and `:Mickey a :Mouse`.
Then, we find triples with `:Mickey` as subject.
```JavaScript
const store = new N3.Store();
store.add(
quad(
namedNode('http://ex.org/Pluto'),
namedNode('http://ex.org/type'),
namedNode('http://ex.org/Dog')
)
);
store.add(
quad(
namedNode('http://ex.org/Mickey'),
namedNode('http://ex.org/type'),
namedNode('http://ex.org/Mouse')
)
);
// Retrieve all quads
for (const quad of store)
console.log(quad);
// Retrieve Mickey's quads
for (const quad of store.match(namedNode('http://ex.org/Mickey'), null, null))
console.log(quad);
```
### [`DatasetCore` Interface](https://rdf.js.org/dataset-spec/#datasetcore-interface)
This store adheres to the `DatasetCore` interface which exposes the following properties
Attributes:
- `size` — A non-negative integer that specifies the number of quads in the set.
Methods:
- `add` — Adds the specified quad to the dataset. Existing quads, as defined in `Quad.equals`, will be ignored.
- `delete` — Removes the specified quad from the dataset.
- `has` — Determines whether a dataset includes a certain quad.
- `match` — Returns a new dataset that is comprised of all quads in the current instance matching the given arguments.
- `[Symbol.iterator]` — Implements the iterator protocol to allow iteration over all `quads` in the dataset as in the example above.
### Addition and deletion of quads
The store provides the following manipulation methods in addition to implementing the standard [`DatasetCore` Interface](https://rdf.js.org/dataset-spec/#datasetcore-interface)
([documentation](http://rdfjs.github.io/N3.js/docs/N3Store.html)):
- `addQuad` to insert one quad
- `addQuads` to insert an array of quads
- `removeQuad` to remove one quad
- `removeQuads` to remove an array of quads
- `remove` to remove a stream of quads
- `removeMatches` to remove all quads matching the given pattern
- `deleteGraph` to remove all quads with the given graph
- `createBlankNode` returns an unused blank node identifier
### Searching quads or entities
The store provides the following search methods
([documentation](http://rdfjs.github.io/N3.js/docs/N3Store.html)):
- `readQuads` returns a generator of quads matching the given pattern
- `getQuads` returns an array of quads matching the given pattern
- `match` returns a stream of quads matching the given pattern
- `countQuads` counts the number of quads matching the given pattern
- `forEach` executes a callback on all matching quads
- `every` returns whether a callback on matching quads always returns true
- `some` returns whether a callback on matching quads returns true at least once
- `getSubjects` returns an array of unique subjects occurring in matching quads
- `forSubjects` executes a callback on unique subjects occurring in matching quads
- `getPredicates` returns an array of unique predicates occurring in matching quad
- `forPredicates` executes a callback on unique predicates occurring in matching quads
- `getObjects` returns an array of unique objects occurring in matching quad
- `forObjects` executes a callback on unique objects occurring in matching quads
- `getGraphs` returns an array of unique graphs occurring in matching quad
- `forGraphs` executes a callback on unique graphs occurring in matching quads
## Compatibility
### Format specifications
The N3.js parser and writer is fully compatible with the following W3C specifications:
- [RDFÂ 1.1 Turtle](https://www.w3.org/TR/turtle/)
– [EARL report](https://raw.githubusercontent.com/rdfjs/N3.js/earl/n3js-earl-report-turtle.ttl)
- [RDFÂ 1.1 TriG](https://www.w3.org/TR/trig/)
– [EARL report](https://raw.githubusercontent.com/rdfjs/N3.js/earl/n3js-earl-report-trig.ttl)
- [RDFÂ 1.1 N-Triples](https://www.w3.org/TR/n-triples/)
– [EARL report](https://raw.githubusercontent.com/rdfjs/N3.js/earl/n3js-earl-report-ntriples.ttl)
- [RDFÂ 1.1 N-Quads](https://www.w3.org/TR/n-quads/)
– [EARL report](https://raw.githubusercontent.com/rdfjs/N3.js/earl/n3js-earl-report-nquads.ttl)
In addition, the N3.js parser also supports [Notation3 (N3)](https://www.w3.org/TeamSubmission/n3/) (no official specification yet).
The N3.js parser and writer are also fully compatible with the RDF* variants
of the W3C specifications.
The default mode is permissive
and allows a mixture of different syntaxes, including RDF*.
Pass a `format` option to the constructor with the name or MIME type of a format
for strict, fault-intolerant behavior.
If a format string contains `star` or `*`
(e.g., `turtlestar` or `TriG*`),
RDF* support for that format will be enabled.
### Interface specifications
The N3.js submodules are compatible with the following [RDF.js](http://rdf.js.org) interfaces:
- `N3.DataFactory` implements
[`DataFactory`](http://rdf.js.org/data-model-spec/#datafactory-interface)
- the terms it creates implement [`Term`](http://rdf.js.org/data-model-spec/#term-interface)
and one of
[`NamedNode`](http://rdf.js.org/data-model-spec/#namednode-interface),
[`BlankNode`](http://rdf.js.org/data-model-spec/#blanknode-interface),
[`Literal`](http://rdf.js.org/data-model-spec/#literal-interface),
[`Variable`](http://rdf.js.org/data-model-spec/#variable-interface),
[`DefaultGraph`](http://rdf.js.org/data-model-spec/#defaultgraph-interface)
- the triples/quads it creates implement
[`Term`](http://rdf.js.org/data-model-spec/#term-interface),
[`Triple`](http://rdf.js.org/data-model-spec/#triple-interface)
and
[`Quad`](http://rdf.js.org/data-model-spec/#quad-interface)
- `N3.StreamParser` implements
[`Stream`](http://rdf.js.org/stream-spec/#stream-interface)
and
[`Sink`](http://rdf.js.org/stream-spec/#sink-interface)
- `N3.StreamWriter` implements
[`Stream`](http://rdf.js.org/stream-spec/#stream-interface)
and
[`Sink`](http://rdf.js.org/stream-spec/#sink-interface)
- `N3.Store` implements
[`Store`](http://rdf.js.org/stream-spec/#store-interface)
[`Source`](http://rdf.js.org/stream-spec/#source-interface)
[`Sink`](http://rdf.js.org/stream-spec/#sink-interface)
[`DatasetCore`](https://rdf.js.org/dataset-spec/#datasetcore-interface)
## License and contributions
The N3.js library is copyrighted by [Ruben Verborgh](https://ruben.verborgh.org/)
and released under the [MIT License](https://github.com/rdfjs/N3.js/blob/master/LICENSE.md).
Contributions are welcome, and bug reports or pull requests are always helpful.
If you plan to implement a larger feature, it's best to contact me first.