Skip to content

Instantly share code, notes, and snippets.

@pchampin
Last active November 5, 2020 09:22
Show Gist options
  • Save pchampin/ab3d01d2c3c245042dc5 to your computer and use it in GitHub Desktop.
Save pchampin/ab3d01d2c3c245042dc5 to your computer and use it in GitHub Desktop.
A subclass of rdflib SPARQLUpdateStore with BNode support, for Virtuoso
from rdflib import BNode, URIRef
from rdflib.plugins.stores.sparqlstore import SPARQLUpdateStore, _node_to_sparql, _node_from_result, SPARQL_NS
from uuid import uuid4
def _virtuoso_compatible_generator():
return unicode(uuid4().int % 2**61)
# monkey patch BNode to make it virtuoso compatible
BNode.__new__.func_defaults = (None, _virtuoso_compatible_generator, 'b')
def _virtuoso_node_to_sparql(node):
if isinstance(node, BNode):
return '<nodeID://{}>'.format(node)
else:
return node.n3()
def _virtuoso_node_from_result(element):
if element.tag == '{%s}bnode' % SPARQL_NS:
return BNode(element.text[9:])
else:
return _node_from_result(element)
class VSPARQLStore(SPARQLUpdateStore):
def __init__(self, endpoint, username, password, **kwargs):
SPARQLUpdateStore.__init__(self, endpoint, endpoint,
node_to_sparql=_virtuoso_node_to_sparql,
node_from_result=_virtuoso_node_from_result,
**kwargs)
self.setHTTPAuth('digest')
self.setCredentials(username, password)
if __name__ == "__main__":
from rdflib import Namespace, Graph, Literal
from rdflib.collection import Collection
EX = Namespace('http://example.org/')
store = VSPARQLStore('http://localhost:8890/sparql-auth', 'dba', 'dba')
g = Graph(store, EX.g)
g.remove((None, None, None))
print(len(g))
bn = BNode()
g.add((EX.s, EX.pb, bn))
g.add((bn, EX.pb, Literal("foo")))
lh = BNode()
g.add((EX.s, EX.pl, lh))
lst = Collection(g, lh, map(Literal, [1,2,3]))
print(len(g))
print(g.serialize(format="turtle"))
g.remove((None, None, None))
@pchampin
Copy link
Author

pchampin commented Mar 10, 2016

Unfortunetaly, both solutions do not work, because of the very problems you envisioned in your comments.
Indeed, Virtuoso only accept up to 19 decimal digits with nodeID:// (and even then, some numbers are too big...).

@pchampin
Copy link
Author

Ok, I found a way to get rid of the global mapping table (revision 2).

It involves an ugly hack (monkeypatching the default parameters of BNode.__new__), and is not 100% robust (it will break with BNodes created with an explicit ID, for example those generated by some parsers). So I have mixed feelings about it.

@joernhees
Copy link

... you seem to like living in the danger zone 😄

Monkeypatching obviously isn't that optimal, but to be honest i currently don't see a better way to do this (so being able to also configure BNode creation from a SPARQLStore in a way that doesn't have the potential to break things outside of it) ...

so, suggestions welcome, but till then 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment