Skip to content

Commit

Permalink
Merge pull request #691 from RDFLib/fix_609
Browse files Browse the repository at this point in the history
Added __iadd__ method to collections, much faster for adding multiple…
  • Loading branch information
gromgull committed Jan 19, 2017
2 parents 9e4bc5f + 4e96763 commit 89c49f6
Show file tree
Hide file tree
Showing 3 changed files with 64 additions and 25 deletions.
62 changes: 39 additions & 23 deletions rdflib/collection.py
@@ -1,7 +1,6 @@
from rdflib.namespace import RDF
from rdflib.term import BNode
from rdflib.term import Literal
from rdflib.graph import Graph
from rdflib.py3compat import format_doctest_out

__all__ = ['Collection']
Expand Down Expand Up @@ -43,8 +42,7 @@ class Collection(object):
def __init__(self, graph, uri, seq=[]):
self.graph = graph
self.uri = uri or BNode()
for item in seq:
self.append(item)
self += seq

def n3(self):
"""
Expand Down Expand Up @@ -184,6 +182,16 @@ def __iter__(self):
"""Iterator over items in Collections"""
return self.graph.items(self.uri)

def _end(self):
# find end of list
container = self.uri
while True:
rest = self.graph.value(container, RDF.rest)
if rest == None or rest == RDF.nil:
return container
else:
container = rest

def append(self, item):
"""
>>> from rdflib.graph import Graph
Expand All @@ -192,29 +200,36 @@ def append(self, item):
>>> c = Collection(g,listName,[Literal(1),Literal(2)])
>>> links = [
... list(g.subjects(object=i, predicate=RDF.first))[0] for i in c]
>>> len([i for i in links if (i,RDF.rest, RDF.nil) in g])
>>> len([i for i in links if (i, RDF.rest, RDF.nil) in g])
1
"""
container = self.uri
graph = self.graph
# iterate to the end of the linked list
rest = graph.value(container, RDF.rest)
while rest:
if rest == RDF.nil:
# the end, append to the end of the linked list
node = BNode()
graph.set((container, RDF.rest, node))
container = node
break
else:
# move down one link
if container != self.uri:
rest = graph.value(rest, RDF.rest)
if not rest == RDF.nil:
container = rest
graph.add((container, RDF.first, item))
graph.add((container, RDF.rest, RDF.nil))

end = self._end()
if (end, RDF.first, None) in self.graph:
# append new node to the end of the linked list
node = BNode()
self.graph.set((end, RDF.rest, node))
end = node

self.graph.add((end, RDF.first, item))
self.graph.add((end, RDF.rest, RDF.nil))

def __iadd__(self, other):

end = self._end()
self.graph.remove((end, RDF.rest, None))

for item in other:
if (end, RDF.first, None) in self.graph:
nxt = BNode()
self.graph.add((end, RDF.rest, nxt))
end = nxt

self.graph.add((end, RDF.first, item))


self.graph.add((end, RDF.rest, RDF.nil))

def clear(self):
container = self.uri
Expand All @@ -233,6 +248,7 @@ def test():
if __name__ == "__main__":
test()

from rdflib import Graph
g = Graph()

c = Collection(g, BNode())
Expand Down
23 changes: 23 additions & 0 deletions rdflib/graph.py
Expand Up @@ -266,6 +266,7 @@
from rdflib.parser import create_input_source
from rdflib.namespace import NamespaceManager
from rdflib.resource import Resource
from rdflib.collection import Collection
from rdflib import py3compat
b = py3compat.b

Expand Down Expand Up @@ -1177,6 +1178,28 @@ def all_nodes(self):
res.update(self.subjects())
return res

def collection(self, identifier):
"""Create a new ``Collection`` instance.
Parameters:
- ``identifier``: a URIRef or BNode instance.
Example::
>>> graph = Graph()
>>> uri = URIRef("http://example.org/resource")
>>> collection = graph.collection(uri)
>>> assert isinstance(collection, Collection)
>>> assert collection.uri is uri
>>> assert collection.graph is graph
>>> collection += [ Literal(1), Literal(2) ]
"""

return Collection(self, identifier)



def resource(self, identifier):
"""Create a new ``Resource`` instance.
Expand Down
4 changes: 2 additions & 2 deletions test/test_issue223.py
Expand Up @@ -11,9 +11,9 @@ def test_collection_with_duplicates():
g = Graph().parse(data=ttl, format="turtle")
for _,_,o in g.triples((URIRef("http://example.org/s"), URIRef("http://example.org/p"), None)):
break
c = Collection(g, o)
c = g.collection(o)
assert list(c) == list(URIRef("http://example.org/" + x) for x in ["a", "b", "a"])
assert len(c) == 3

if __name__ == '__main__':
test_collection_with_duplicates()

0 comments on commit 89c49f6

Please sign in to comment.