rdfjs / N3.js

Lightning fast, spec-compatible, streaming RDF for JavaScript
http://rdf.js.org/N3.js/
Other
715 stars 132 forks source link

Lists re-encoded if stored then written #167

Closed radicand closed 5 years ago

radicand commented 5 years ago

Pardon if I have the wrong terminology here, new to the space.

I came across an issue creating a large list - N3.Writer correctly encodes the list under the below circumstances:

let N3 = require('n3');
let { DataFactory } = N3;
let { namedNode, literal, defaultGraph, quad } = DataFactory;
writer = N3.Writer();

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));

The result is then (as expected):

<http://example.org/cartoons#Mammy> <http://example.org/cartoons#hasPets> (<http://example.org/cartoons#Tom> <http://example.org/cartoons#Jerry>).

But if you store all quads first before writing as in:

let N3 = require('n3');
let { DataFactory } = N3;
let { namedNode, literal, defaultGraph, quad } = DataFactory;
writer = N3.Writer();
store = N3.Store();

store.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.addQuads(store.getQuads())
writer.end((error, result) => console.log(result));

The result is then (note the extra <>):

<http://example.org/cartoons#Mammy> <http://example.org/cartoons#hasPets> <(<http://example.org/cartoons#Tom> <http://example.org/cartoons#Jerry>)>.

It seems like this can be solved by adding a new "ListNode" function and class to DataFactory, similar to NamedNode (to bypass the re-encoding that happens in _encodeIriOrBlank), detected by fromId if id[0] === (. I've done something similar in my local codebase and it's working as expected; let me know if a PR is worthwhile or if you'd prefer to handle it differently.

RubenVerborgh commented 5 years ago

Hi @radicand,

The Writer#list method is for exclusive use with the Writer instance to which it belongs. It is a pretty-printing method, which should not be combined with the Store at all.

In the store, you should place regular rdf:first and rdf:rest triples.

radicand commented 5 years ago

Noted, thanks for clarification.

renyuneyun commented 1 year ago

I'm encountering this today, and found this old issue.

From an ergonomics point of view, why would Store.addQuad() accept the output of Writer.list() in the first place? I mean, why does Quad_Object[] both represent a JS list containing several things (that can be objects) and an RDF list containing several things? Presumably they are different internally for N3.js, otherwise Writer.list() makes no sense. Thus, shouldn't they be two distinct types?

RubenVerborgh commented 1 year ago

Thus, shouldn't they be two distinct types?

They are. The list node is SerializedTerm extends Term, which just so happens to match the input type of Store.addQuad().