-
Notifications
You must be signed in to change notification settings - Fork 15
Description
Currently, a blank node resulting from one expression, cannot be reused in another. Following the example of https://github.com/solid/query-ldflex/issues/33, the following snippets return different results:
const alice = "https://drive.verborgh.org/public/2019/blanks.ttl#Alice";
for await (const name of solid.data[alice].friends.name)
console.log(`${name}`)const alice = "https://drive.verborgh.org/public/2019/blanks.ttl#Alice";
for await (const friend of solid.data[alice].friends)
console.log(`${await friend.name}`)This happens because Alice's friends are identified by blank nodes, and they lose context across multiple expressions. If we retry both snippets with Alice2, which has IRI friends, we get the same results.
We could strive to reuse blank nodes across expressions, by internally skolemizing them. Here is a sketch of how that could work:
- When outputting blank nodes, Comunica assigns an internal identifier to them. For instance,
_:b1is still output as aBlankNode, but has a special internal field.skolemizedthat containsurn:skolem:1234. - When a SPARQL query is generated from such a skolemized blank node, the skolemized IRI is used instead of a blank node.
- When returning results, any skolemized
NamedNodeis turned into a skolemized `BlankNode.
The key is inserting skolemization and deskolemization processing in the right place, for which I need to ask @rubensworks for help.
We could simply skolemize upon parsing, and then deskolemize right before results are returned. This works in all cases, except when Comunica directly operates on a store (the contents of which it did not parse, so it can contain actual blank nodes).
And alternative approach is a skolemizing store wrapper. It takes a store as an argument, and translates on the fly in its match etc. methods.
Perhaps both approaches can be used in conjunction: skolemization in parsers for all cases, except when a store is passed, then we wrap it.