by

Where communities thrive


  • Join over 1.5M+ people
  • Join over 100K+ communities
  • Free without limits
  • Create your own community
People
Activity
Fabian Cook
@fabiancook

In the end all the generics allow me to write code like const boxModels = dataset.filter(isBoxModelQuad), where we have:

export function isBoxModelQuad(quad: Quad): quad is BoxModelQuad {
    return (
        isJSONQuad(quad) &&
        quad.subject.equals(BoxModelPredicate)
    )
}

So now whereever I use boxModels it is typed nicely

BoxModelQuad is then...

export interface BoxModelQuad extends JSONQuad<BoxModel, QuadSubject, typeof BoxModelPredicate> {

}

And JSONQuad is then...

export interface JSONQuad<OT, S extends QuadSubject = QuadSubject, P extends QuadPredicate = QuadPredicate, G extends QuadGraph = QuadGraph> extends Quad<S, P, JSONObject, G> {

}
Its a long winded way to get some nice type support for the quads you're actually using. Notice how for BoxModelQuad I even define the predicate type, which is a namedNode itself...
Fabian Cook
@fabiancook
This message was deleted
Fabian Cook
@fabiancook

I've been able to make this dataset observable as well by reporting changes using Symbol.asyncIterator, for each change I give a slice of the inserted & deleted values.. https://github.com/opennetwork/rdf-dataset-async/blob/0d2d175fe39d777a7a41dfffeec73b25ce55c0c9/src/index.ts#L6

Works exactly the same as my other dataset implementation, so it still can be queried in a sync way (so any point in time can be queried) using ReadonlyDataset

add, addAll, import, & delete all report their own changes, if someone doesn't want to know the changes and just want to know the current state, they would still use that main dataset rather than the "changes" datasets

Because of the way the ReadonlyDataset works you're able to create your "slice" and then on each change you can re-query that source dataset with the resulting ReadonlyDataset

To watch for changes you can do...
for await (const [writes, deletes] of dataset) {
    console.log("Changes", writes.size, deletes.size)
  }
for await (const [, deletes] of dataset) {
    console.log("Deletes", deletes.size)
  }
for await (const [writes] of dataset) {
    console.log("Writes", writes.size)
  }
If you know there are going to be no more changes you then call dataset.close() and all of the things "listening" will complete...
Reto Gmür
@retog

is it really limiting? I never had such an issue. I have not originally defined it that way but I think it's correct. all those methods should return self...

@tpluscode Actually I thought it would merely be an annoyance but that one could still implement the interface by returning instances of the same class fulfilling a different function, but this doesn't work as 'this' could be instantiated with a different subtype. For add and delete the return type this makes sense as it matches to spec requirement to return the dataset instance it was called on, but as the match-method returns a new dataset I don't see how the ts-interface can be implemented. Is there any class that implements RDF.DatasetCore?

Tomasz Pluskiewicz
@tpluscode
indeed! maybe all implementations are JS and not TS which is why no one noticed?
Reto Gmür
@retog
I think the return type for matchshould be DatasetCore
Fabian Cook
@fabiancook
I have a typescript implementation, but do not use this
Tomasz Pluskiewicz
@tpluscode
then you should have a compilation error, no?
Fabian Cook
@fabiancook
The overall signature of a Dataset is almost like what is spec'd, so return types are not this, instead I use a "more generic" return type of Dataset for mutating functions, and a "more generic" read only return type of ReadonlyDataset for all chainable query functions
Tomasz Pluskiewicz
@tpluscode
I see that you don't annotate your types with @types/rdf-js?
Reto Gmür
@retog
The spec is inconsistent where it says that the return type is Dataset, it should be DatasetCore. For add and delete it should be this in ts.
@tpluscode shall I do a PR or can you do the change?
Fabian Cook
@fabiancook
I am utilising an implementation that started with types.. https://www.unpkg.com/browse/@opennetwork/rdf-data-model@5.2.7/esnext/index.d.ts
Is also esm
They should be compatible with the relevant types, as far as I know
Tomasz Pluskiewicz
@tpluscode
@retog DefinitelyTyped/DefinitelyTyped#45720 draft for now. I don't have the time ATM to complete this
Fabian Cook
@fabiancook
rat10
@rat10_gitlab
[I hope this is the right place for this question] https://github.com/rdfjs/N3.js says that it now supports RDF but I couldn't find anythig more specific. Which mode does it support: PG (every RDF-triple is considered asserted) or SA (much like RDF standard reification where the reification quadlet only describes an assertion)?
rat10
@rat10_gitlab
Arg, the * stars in my post triggered italics. Let's try again... I'm asking about RDF*/RDF-Star support in rdfjs/n3.js: is it PG or SA mode?
Ruben Taelman
@rubensworks
At the moment it's just SA-mode AFAIK. But this may become configurable in one of the next releases.
rat10
@rat10_gitlab
Thanks!
Tomasz Pluskiewicz
@tpluscode

@blake-regalia hey, I'm trying Graphy writers but I have a problem that they stream quads and not string. I have it somewhat like this

import writer from '@graphy/content.ttl.write'

const quadStream = toStream([ ...someQuads ])
const turtleStream = writer.import(quadStream)

let turtle = ''
turtleStream.on('data', chunk => { turtle += chunk })

in the last line the chunk is a Quad

Blake Regalia
@blake-regalia
i would have to see what someQuads and toStream are doing, but in general i would say that converting an array to a stream is almost never a good idea. the objects are already in memory if they're in an array, so just use them:
import * as turtleWriter from '@graphy/content.ttl.write';

let turtle = '';
const turtleStream = turtleWriter();
turtleStream.on('data', chunk => turtle += chunk);

turtleStream.end({
    type: 'array',
    value: someQuads,
});

console.info(turtle);
Tomasz Pluskiewicz
@tpluscode
someQuads is indeed just an array. and toStream just wraps them in Readable and pushes those quads. this is how all RDF/JS sinks I've used work. I cannot just serializer.import(array). the param has to be a stream
hence I hoped I would use the graphy writer uniformly
Blake Regalia
@blake-regalia
@tpluscode i am noticing that .import(stream) returns the passed argument stream rather than this. So you could change your code to:
import * as writer from '@graphy/content.ttl.write'

const quadStream = toStream([ ...someQuads ])
const turtleWriter = writer();
turtleWriter.import(quadStream)

let turtle = ''
turtleWriter.on('data', chunk => { turtle += chunk })
Tomasz Pluskiewicz
@tpluscode
ah, so that's what's happening. makes sense. thank's I will give it a go
Tomasz Pluskiewicz
@tpluscode
does the input value have to be in the concise hash form to produce fully compact turtle? (bnode abbreviations, merging objects, etc)
Blake Regalia
@blake-regalia
no, but there are several Turtle features that cannot be used when given RDFJS objects such as collections, comments, anonymous blank nodes, nested blank node property lists, etc. Concise hash will let you do any of those
For your use-case i would suggest using the scriber @graphy/content.ttl.scribe instead of the writer. it will pretty-print RDFJS quads quite well
Blake Regalia
@blake-regalia
just as an example, creating some rdfjs objects by using spread operator on factory.c3:
import * as factory from '@graphy/core.data.factory';
import * as ttl_scribe = '@graphy/content.ttl.scribe';

const PREFIXES = {
    dbr: 'http://dbpedia.org/resource/',
    dbp: 'http://dbpedia.org/property/',
    dbo: 'http://dbpedia.org/ontology/',
    dt: 'http://dbpedia.org/datatype/',
    dct: 'http://purl.org/dc/terms/subject',
    'umbel-rc': 'http://umbel.org/umbel/rc/',
    xsd: 'http://www.w3.org/2001/XMLSchema#',
    rdf: 'http://www.w3.org/1999/02/22-rdf-syntax-ns#',
};

const rdfjs_objs = [...factory.c3({
    'dbr:Banana': {
        a: ['Plant', 'EukaryoticCell', 'BiologicalLivingObject']
            .map(s => `umbel-rc:${s}`),
        'dct:subject': ['Edible_fruits', 'Staple_foods', 'Bananas']
            .map(s => `dbr:Category:${s}`),
        'dbo:wikiPageID': 38940,
        'dbp:carbs': '^dt:gram"22.84',
    },
}, PREFIXES)];

const scriber = ttl_scribe({
    prefixes: PREFIXES,
});
scriber.import(toStream(rdfjs_objs));
scriber.pipe(process.stdout);
prints this:
@prefix dbr: <http://dbpedia.org/resource/> .
@prefix dbp: <http://dbpedia.org/property/> .
@prefix dbo: <http://dbpedia.org/ontology/> .
@prefix dt: <http://dbpedia.org/datatype/> .
@prefix dct: <http://purl.org/dc/terms/subject> .
@prefix umbel-rc: <http://umbel.org/umbel/rc/> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .

dbr:Banana rdf:type umbel-rc:Plant, umbel-rc:EukaryoticCell, umbel-rc:BiologicalLivingObject ;
    dct:subject dbr:Category:Edible_fruits, dbr:Category:Staple_foods, dbr:Category:Bananas ;
    dbo:wikiPageID "38940"^^xsd:integer ;
    dbp:carbs "22.84"^^dt:gram .
@tpluscode
Tomasz Pluskiewicz
@tpluscode
finally gave that a go @blake-regalia. the confusing part is scribe vs write. from the docs I assumed that scribe is the "simpler" serializer and it would not do smart formatting. whereas about write it says stylized RDF serialization
two followup questions for scribe:
  1. is it possible to set base URI?
  2. is it possible to have blank nodes shortened to [ ]?
Blake Regalia
@blake-regalia
@tpluscode yea, the writer is meant for feature-rich pretty-printing and scriber was added later to complement, but for historical reasons the writer will not condense pred/object pairs in turtle for RDFJS quads only, but that will change in the next major release
re 1: base URIs are not used by any of the serializers since this has never seemed like a needed/wanted feature; but i would like to push the writers now to support the full range of language features including optional use of long literals and so on, i'll make a note of adding base URI
re 2: anonymous blank nodes are supported via concise hash objects, which are currently graphy-specific. you can pass these in programatically. this is a unique feature that no other libs offer since these types of blank nodes are 'ephemeral' in a stream and can never be referenced again once they are serialized
Tomasz Pluskiewicz
@tpluscode
hm, curious about the ephemeral blanks. the docs only show exactly []. does it ever serialize their properties as [ :foo :bar ]?
Blake Regalia
@blake-regalia
it does not serialize blank node property lists, altho that would be cool -- seems doable with concise hash objects
Tomasz Pluskiewicz
@tpluscode
I guess the only missing piece is getting for a quad stream to the concise hash
maybe graphy could provide a function which turns the entire dataset/stream into a concise hash?
Blake Regalia
@blake-regalia
yeah actually it will do that thru https://graphy.link/memory.dataset.fast
which is basically an in-mem store. load quads into it, then pipe to any serializer. it will sort quads which ends up pretty-printing them in trig/turtle but as of now won't perform any blank node magic