diff options
Diffstat (limited to 'test')
-rw-r--r-- | test/triple_store/sparql/test_parse_fetch.py | 263 | ||||
-rw-r--r-- | test/triple_store/sparql/test_parse_filter.py | 150 | ||||
-rw-r--r-- | test/triple_store/sparql/test_sparql.py | 70 | ||||
-rw-r--r-- | test/triple_store/sparql/test_utils.py | 155 | ||||
-rw-r--r-- | test/triple_store/test_base.py | 3 |
5 files changed, 566 insertions, 75 deletions
diff --git a/test/triple_store/sparql/test_parse_fetch.py b/test/triple_store/sparql/test_parse_fetch.py new file mode 100644 index 0000000..0961789 --- /dev/null +++ b/test/triple_store/sparql/test_parse_fetch.py @@ -0,0 +1,263 @@ +""" + +Part of the bsfs test suite. +A copy of the license is provided with the project. +Author: Matthias Baumgartner, 2022 +""" +# imports +import rdflib +import unittest + +# bsie imports +from bsfs import schema +from bsfs.namespace import Namespace, ns +from bsfs.query import ast +from bsfs.utils import errors, URI + +# objects to test +from bsfs.triple_store.sparql.parse_fetch import Fetch + + +## code ## + +bsfs = Namespace('http://bsfs.ai/schema', fsep='/') +bse = Namespace('http://bsfs.ai/schema/Entity') +bst = Namespace('http://bsfs.ai/schema/Tag') +bsc = Namespace('http://bsfs.ai/schema/Collection') + +class TestParseFetch(unittest.TestCase): + + def setUp(self): + self.schema = schema.from_string(''' + prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> + prefix xsd: <http://www.w3.org/2001/XMLSchema#> + + prefix bsfs: <http://bsfs.ai/schema/> + prefix bse: <http://bsfs.ai/schema/Entity#> + prefix bst: <http://bsfs.ai/schema/Tag#> + prefix bsc: <http://bsfs.ai/schema/Collection#> + + # nodes + bsfs:Entity rdfs:subClassOf bsfs:Node . + bsfs:Tag rdfs:subClassOf bsfs:Node . + bsfs:Collection rdfs:subClassOf bsfs:Node . + + # literals + xsd:integer rdfs:subClassOf bsfs:Literal . + xsd:string rdfs:subClassOf bsfs:Literal . + + # predicates + bse:tag rdfs:subClassOf bsfs:Predicate ; + rdfs:domain bsfs:Entity ; + rdfs:range bsfs:Tag . + + bse:collection rdfs:subClassOf bsfs:Predicate ; + rdfs:domain bsfs:Entity ; + rdfs:range bsfs:Collection . + + bse:filename rdfs:subClassOf bsfs:Predicate ; + rdfs:domain bsfs:Entity ; + rdfs:range xsd:string . + + bse:rank rdfs:subClassOf bsfs:Predicate ; + rdfs:domain bsfs:Entity ; + rdfs:range xsd:integer . + + bst:main rdfs:subClassOf bsfs:Predicate ; + rdfs:domain bsfs:Tag ; + rdfs:range bsfs:Entity . + + bst:label rdfs:subClassOf bsfs:Predicate ; + rdfs:domain bsfs:Tag ; + rdfs:range xsd:string . + + bsc:tag rdfs:subClassOf bsfs:Predicate ; + rdfs:domain bsfs:Collection ; + rdfs:range bsfs:Tag . + + bsc:label rdfs:subClassOf bsfs:Predicate ; + rdfs:domain bsfs:Collection ; + rdfs:range xsd:string . + + bsc:rating rdfs:subClassOf bsfs:Predicate ; + rdfs:domain bsfs:Collection ; + rdfs:range xsd:integer . + + ''') + + # graph to test queries + self.graph = rdflib.Graph() + # schema hierarchies + self.graph.add((rdflib.URIRef('http://bsfs.ai/schema/Entity'), rdflib.RDFS.subClassOf, rdflib.URIRef('http://bsfs.ai/schema/Node'))) + self.graph.add((rdflib.URIRef('http://bsfs.ai/schema/Collection'), rdflib.RDFS.subClassOf, rdflib.URIRef('http://bsfs.ai/schema/Node'))) + self.graph.add((rdflib.URIRef('http://bsfs.ai/schema/Tag'), rdflib.RDFS.subClassOf, rdflib.URIRef('http://bsfs.ai/schema/Node'))) + # entities + self.graph.add((rdflib.URIRef('http://example.com/entity#1234'), rdflib.RDF.type, rdflib.URIRef('http://bsfs.ai/schema/Entity'))) + self.graph.add((rdflib.URIRef('http://example.com/entity#4321'), rdflib.RDF.type, rdflib.URIRef('http://bsfs.ai/schema/Entity'))) + # tags + self.graph.add((rdflib.URIRef('http://example.com/tag#1234'), rdflib.RDF.type, rdflib.URIRef('http://bsfs.ai/schema/Tag'))) + self.graph.add((rdflib.URIRef('http://example.com/tag#4321'), rdflib.RDF.type, rdflib.URIRef('http://bsfs.ai/schema/Tag'))) + # collections + self.graph.add((rdflib.URIRef('http://example.com/collection#1234'), rdflib.RDF.type, rdflib.URIRef('http://bsfs.ai/schema/Collection'))) + self.graph.add((rdflib.URIRef('http://example.com/collection#4321'), rdflib.RDF.type, rdflib.URIRef('http://bsfs.ai/schema/Collection'))) + # entity literals + self.graph.add((rdflib.URIRef('http://example.com/entity#1234'), rdflib.URIRef(bse.rank), rdflib.Literal('1234', datatype=rdflib.XSD.integer))) + self.graph.add((rdflib.URIRef('http://example.com/entity#1234'), rdflib.URIRef(bse.filename), rdflib.Literal('filename_1234', datatype=rdflib.XSD.string))) + #self.graph.add((rdflib.URIRef('http://example.com/entity#4321'), rdflib.URIRef(bse.rank), rdflib.Literal('4321', datatype=rdflib.XSD.integer))) + self.graph.add((rdflib.URIRef('http://example.com/entity#4321'), rdflib.URIRef(bse.filename), rdflib.Literal('filename_4321', datatype=rdflib.XSD.string))) + # tag literals + self.graph.add((rdflib.URIRef('http://example.com/tag#1234'), rdflib.URIRef(bst.label), rdflib.Literal('tag_label_1234', datatype=rdflib.XSD.string))) + self.graph.add((rdflib.URIRef('http://example.com/tag#4321'), rdflib.URIRef(bst.label), rdflib.Literal('tag_label_4321', datatype=rdflib.XSD.string))) + # collection literals + self.graph.add((rdflib.URIRef('http://example.com/collection#1234'), rdflib.URIRef(bsc.label), rdflib.Literal('collection_label_1234', datatype=rdflib.XSD.string))) + self.graph.add((rdflib.URIRef('http://example.com/collection#1234'), rdflib.URIRef(bsc.rating), rdflib.Literal('1234', datatype=rdflib.XSD.integer))) + self.graph.add((rdflib.URIRef('http://example.com/collection#4321'), rdflib.URIRef(bsc.label), rdflib.Literal('collection_label_4321', datatype=rdflib.XSD.string))) + self.graph.add((rdflib.URIRef('http://example.com/collection#4321'), rdflib.URIRef(bsc.rating), rdflib.Literal('4321', datatype=rdflib.XSD.integer))) + # entity-tag links + self.graph.add((rdflib.URIRef('http://example.com/entity#1234'), rdflib.URIRef(bse.tag), rdflib.URIRef('http://example.com/tag#1234'))) + self.graph.add((rdflib.URIRef('http://example.com/entity#4321'), rdflib.URIRef(bse.tag), rdflib.URIRef('http://example.com/tag#4321'))) + # entity-collection links + self.graph.add((rdflib.URIRef('http://example.com/entity#1234'), rdflib.URIRef(bse.collection), rdflib.URIRef('http://example.com/collection#1234'))) + self.graph.add((rdflib.URIRef('http://example.com/entity#4321'), rdflib.URIRef(bse.collection), rdflib.URIRef('http://example.com/collection#4321'))) + # collection-tag links + self.graph.add((rdflib.URIRef('http://example.com/collection#1234'), rdflib.URIRef(bsc.tag), rdflib.URIRef('http://example.com/tag#1234'))) + self.graph.add((rdflib.URIRef('http://example.com/collection#4321'), rdflib.URIRef(bsc.tag), rdflib.URIRef('http://example.com/tag#4321'))) + # tag-entity links # NOTE: cross-over + self.graph.add((rdflib.URIRef('http://example.com/tag#1234'), rdflib.URIRef(bst.main), rdflib.URIRef('http://example.com/entity#4321'))) + self.graph.add((rdflib.URIRef('http://example.com/tag#4321'), rdflib.URIRef(bst.main), rdflib.URIRef('http://example.com/entity#1234'))) + + # default parser + self.parser = Fetch(self.schema) + self.ent = self.schema.node(ns.bsfs.Entity) + + + def test_call(self): + # NOTE: The individual ast components are considered in the respective tests. Here, we test __call__ specifics. + + # __call__ requires a valid root type + self.assertRaises(errors.BackendError, self.parser, self.schema.literal(ns.bsfs.Literal), ast.fetch.This('this')) + self.assertRaises(errors.ConsistencyError, self.parser, self.schema.node(ns.bsfs.Node).child(ns.bsfs.Invalid), ast.fetch.This('this')) + # __call__ requires a parseable root + self.assertRaises(errors.BackendError, self.parser, self.ent, ast.filter.FilterExpression()) + # __call__ returns an executable query + q = self.parser(self.ent, ast.fetch.Fetch(bse.tag, ast.fetch.Value(bst.label, 'label'))) + self.assertSetEqual(set(q(self.graph)), { + (rdflib.URIRef('http://example.com/entity#1234'), rdflib.Literal('tag_label_1234', datatype=rdflib.XSD.string)), + (rdflib.URIRef('http://example.com/entity#4321'), rdflib.Literal('tag_label_4321', datatype=rdflib.XSD.string)), + }) + + + def test_routing(self): + self.assertRaises(errors.BackendError, self.parser._parse_fetch_expression, self.ent, ast.fetch.FetchExpression(), '?head') + + + def test_all(self): + # multiple values query + q = self.parser(self.ent, ast.fetch.All( + ast.fetch.Value(bse.filename, name='filename'), + ast.fetch.Value(bse.rank, name='rank')), + ) + self.assertSetEqual(set(q.names), {'filename', 'rank'}) + if q.names == ('filename', 'rank'): + self.assertSetEqual(set(q(self.graph)), { + (rdflib.URIRef('http://example.com/entity#1234'), rdflib.Literal('filename_1234', datatype=rdflib.XSD.string), rdflib.Literal('1234', datatype=rdflib.XSD.integer)), + (rdflib.URIRef('http://example.com/entity#4321'), rdflib.Literal('filename_4321', datatype=rdflib.XSD.string), None), + }) + else: + self.assertSetEqual(set(q(self.graph)), { + (rdflib.URIRef('http://example.com/entity#1234'), rdflib.Literal('1234', datatype=rdflib.XSD.integer), rdflib.Literal('filename_1234', datatype=rdflib.XSD.string)), + (rdflib.URIRef('http://example.com/entity#4321'), None, rdflib.Literal('filename_4321', datatype=rdflib.XSD.string)), + }) + # mixed values and node query + q = self.parser(self.ent, ast.fetch.All( + ast.fetch.Value(bse.filename, name='filename'), + ast.fetch.Node(bse.tag, name='tag'), + )) + self.assertSetEqual(set(q.names), {'filename', 'tag'}) + if q.names == ('filename', 'tag'): + self.assertSetEqual(set(q(self.graph)), { + (rdflib.URIRef('http://example.com/entity#1234'), rdflib.Literal('filename_1234', datatype=rdflib.XSD.string), rdflib.URIRef('http://example.com/tag#1234')), + (rdflib.URIRef('http://example.com/entity#4321'), rdflib.Literal('filename_4321', datatype=rdflib.XSD.string), rdflib.URIRef('http://example.com/tag#4321')), + }) + else: + self.assertSetEqual(set(q(self.graph)), { + (rdflib.URIRef('http://example.com/entity#1234'), rdflib.URIRef('http://example.com/tag#1234'), rdflib.Literal('filename_1234', datatype=rdflib.XSD.string)), + (rdflib.URIRef('http://example.com/entity#4321'), rdflib.URIRef('http://example.com/tag#4321'), rdflib.Literal('filename_4321', datatype=rdflib.XSD.string)), + }) + # multiple values and second hop + q = self.parser(self.ent, ast.fetch.Fetch(bse.tag, ast.fetch.All( + ast.fetch.This(name='tag'), + ast.fetch.Value(bst.label, name='label'), + ))) + self.assertSetEqual(set(q.names), {'tag', 'label'}) + if q.names == ('tag', 'label'): + self.assertSetEqual(set(q(self.graph)), { + (rdflib.URIRef('http://example.com/entity#1234'), rdflib.URIRef('http://example.com/tag#1234'), rdflib.Literal('tag_label_1234', datatype=rdflib.XSD.string)), + (rdflib.URIRef('http://example.com/entity#4321'), rdflib.URIRef('http://example.com/tag#4321'), rdflib.Literal('tag_label_4321', datatype=rdflib.XSD.string)), + }) + else: + self.assertSetEqual(set(q(self.graph)), { + (rdflib.URIRef('http://example.com/entity#1234'), rdflib.Literal('tag_label_1234', datatype=rdflib.XSD.string), rdflib.URIRef('http://example.com/tag#1234')), + (rdflib.URIRef('http://example.com/entity#4321'), rdflib.Literal('tag_label_4321', datatype=rdflib.XSD.string), rdflib.URIRef('http://example.com/tag#4321')), + }) + + + + def test_fetch(self): + # two-hop query + q = self.parser(self.ent, ast.fetch.Fetch(bse.tag, ast.fetch.Value(bst.label, 'tag_label'))) + self.assertSetEqual(set(q(self.graph)), { + (rdflib.URIRef('http://example.com/entity#1234'), rdflib.Literal('tag_label_1234', datatype=rdflib.XSD.string)), + (rdflib.URIRef('http://example.com/entity#4321'), rdflib.Literal('tag_label_4321', datatype=rdflib.XSD.string)), + }) + # three-hop-query + q = self.parser(self.ent, ast.fetch.Fetch(bse.tag, ast.fetch.Fetch(bst.main, ast.fetch.Value(bse.rank, 'entity_rank')))) + self.assertSetEqual(set(q(self.graph)), { + (rdflib.URIRef('http://example.com/entity#1234'), None), + (rdflib.URIRef('http://example.com/entity#4321'), rdflib.Literal('1234', datatype=rdflib.XSD.integer)), + }) + + + def test_node(self): + # cannot use the internal hop name + self.assertRaises(errors.BackendError, self.parser, self.ent, ast.fetch.Node(bse.tag, self.parser.ngen.prefix[1:] + '123')) + # a simple Node statement + q = self.parser(self.ent, ast.fetch.Node(bse.tag, 'tag')) + self.assertSetEqual(set(q.names), {'tag'}) + self.assertSetEqual(set(q(self.graph)), { + (rdflib.URIRef('http://example.com/entity#1234'), rdflib.URIRef('http://example.com/tag#1234')), + (rdflib.URIRef('http://example.com/entity#4321'), rdflib.URIRef('http://example.com/tag#4321')), + }) + + + def test_value(self): + # cannot use the internal hop name + self.assertRaises(errors.BackendError, self.parser, self.schema.node(ns.bsfs.Entity), ast.fetch.Value(bse.filename, self.parser.ngen.prefix[1:] + '123')) + # a simple Value statement + q = self.parser(self.ent, ast.fetch.Value(bse.filename, 'filename')) + self.assertSetEqual(set(q.names), {'filename'}) + self.assertSetEqual(set(q(self.graph)), { + (rdflib.URIRef('http://example.com/entity#1234'), rdflib.Literal('filename_1234', datatype=rdflib.XSD.string)), + (rdflib.URIRef('http://example.com/entity#4321'), rdflib.Literal('filename_4321', datatype=rdflib.XSD.string)), + }) + + + def test_this(self): + # cannot use the internal hop name + self.assertRaises(errors.BackendError, self.parser, self.ent, ast.fetch.This(self.parser.ngen.prefix[1:] + '123')) + # a simple This statement + self.assertEqual(self.parser._this(self.ent, ast.fetch.This('this'), '?head'), + ({('?head', 'this')}, '')) + q = self.parser(self.ent, ast.fetch.This('this')) + self.assertSetEqual(set(q(self.graph)), { + (rdflib.URIRef('http://example.com/entity#1234'), rdflib.URIRef('http://example.com/entity#1234')), + (rdflib.URIRef('http://example.com/entity#4321'), rdflib.URIRef('http://example.com/entity#4321')), + }) + + +## main ## + +if __name__ == '__main__': + unittest.main() + +## EOF ## diff --git a/test/triple_store/sparql/test_parse_filter.py b/test/triple_store/sparql/test_parse_filter.py index 8764535..6fa0cd3 100644 --- a/test/triple_store/sparql/test_parse_filter.py +++ b/test/triple_store/sparql/test_parse_filter.py @@ -149,13 +149,13 @@ class TestParseFilter(unittest.TestCase): ast.filter.Or( ast.filter.Is('http://example.com/entity#1234'), ast.filter.Is('http://example.com/entity#5678'))) - self.assertSetEqual({str(guid) for guid, in self.graph.query(q)}, {'http://example.com/entity#1234'}) + self.assertSetEqual({str(guid) for guid, in q(self.graph)}, {'http://example.com/entity#1234'}) # root is optional q = self.parser(self.schema.node(ns.bsfs.Entity)) - self.assertSetEqual({str(guid) for guid, in self.graph.query(q)}, + self.assertSetEqual({str(guid) for guid, in q(self.graph)}, {'http://example.com/entity#1234', 'http://example.com/entity#4321', 'http://example.com/image#1234', 'http://example.com/image#4321'}) q = self.parser(self.schema.node(ns.bsfs.Tag)) - self.assertSetEqual({str(guid) for guid, in self.graph.query(q)}, + self.assertSetEqual({str(guid) for guid, in q(self.graph)}, {'http://example.com/tag#1234', 'http://example.com/tag#4321'}) @@ -164,7 +164,7 @@ class TestParseFilter(unittest.TestCase): self.assertRaises(errors.BackendError, self.parser._is, self.schema.literal(ns.bsfs.Literal), ast.filter.Is('http://example.com/entity#1234'), '?ent') # a single Is statement q = self.parser(self.schema.node(ns.bsfs.Entity), ast.filter.Is('http://example.com/entity#1234')) - self.assertSetEqual({str(guid) for guid, in self.graph.query(q)}, + self.assertSetEqual({str(guid) for guid, in q(self.graph)}, {'http://example.com/entity#1234'}) # an aggregate of Is statements q = self.parser(self.schema.node(ns.bsfs.Entity), @@ -172,7 +172,7 @@ class TestParseFilter(unittest.TestCase): ast.filter.Is('http://example.com/entity#1234'), ast.filter.Is('http://example.com/entity#4321'), )) - self.assertSetEqual({str(guid) for guid, in self.graph.query(q)}, + self.assertSetEqual({str(guid) for guid, in q(self.graph)}, {'http://example.com/entity#1234', 'http://example.com/entity#4321'}) # combined with other filters q = self.parser(self.schema.node(ns.bsfs.Entity), @@ -185,12 +185,12 @@ class TestParseFilter(unittest.TestCase): ast.filter.Equals('Me, Myself, and I') ), )) - self.assertSetEqual({str(guid) for guid, in self.graph.query(q)}, + self.assertSetEqual({str(guid) for guid, in q(self.graph)}, {'http://example.com/entity#1234'}) # as argument of Any/All q = self.parser(self.schema.node(ns.bsfs.Entity), ast.filter.Any(ns.bse.tag, ast.filter.Is('http://example.com/tag#1234'))) - self.assertSetEqual({str(guid) for guid, in self.graph.query(q)}, + self.assertSetEqual({str(guid) for guid, in q(self.graph)}, {'http://example.com/entity#1234', 'http://example.com/image#1234'}) @@ -199,15 +199,15 @@ class TestParseFilter(unittest.TestCase): self.assertRaises(errors.BackendError, self.parser._equals, self.schema.node(ns.bsfs.Entity), ast.filter.Equals('hello world'), '?ent') # a single Equals statement q = self.parser(self.schema.node(ns.bsfs.Entity), ast.filter.Any(ns.bse.comment, ast.filter.Equals('hello world'))) - self.assertSetEqual({str(guid) for guid, in self.graph.query(q)}, + self.assertSetEqual({str(guid) for guid, in q(self.graph)}, {'http://example.com/entity#1234', 'http://example.com/entity#4321'}) # a single Equals statement that includes subtypes q = self.parser(self.schema.node(ns.bsfs.Entity), ast.filter.Any(ns.bse.comment, ast.filter.Equals('Me, Myself, and I'))) - self.assertSetEqual({str(guid) for guid, in self.graph.query(q)}, + self.assertSetEqual({str(guid) for guid, in q(self.graph)}, {'http://example.com/entity#1234', 'http://example.com/image#1234'}) # an Equals statement on an integer q = self.parser(self.schema.node(ns.bsfs.Entity), ast.filter.Any(ns.bse.filesize, ast.filter.Equals(4321))) - self.assertSetEqual({str(guid) for guid, in self.graph.query(q)}, + self.assertSetEqual({str(guid) for guid, in q(self.graph)}, {'http://example.com/entity#4321', 'http://example.com/image#4321'}) @@ -216,18 +216,18 @@ class TestParseFilter(unittest.TestCase): self.assertRaises(errors.BackendError, self.parser._substring, self.schema.node(ns.bsfs.Entity), ast.filter.Substring('hello world'), '?ent') # a single Substring statement q = self.parser(self.schema.node(ns.bsfs.Entity), ast.filter.Any(ns.bse.comment, ast.filter.Substring('hello'))) - self.assertSetEqual({str(guid) for guid, in self.graph.query(q)}, + self.assertSetEqual({str(guid) for guid, in q(self.graph)}, {'http://example.com/entity#1234', 'http://example.com/entity#4321'}) q = self.parser(self.schema.node(ns.bsfs.Entity), ast.filter.Any(ns.bse.comment, ast.filter.Substring('lo wo'))) - self.assertSetEqual({str(guid) for guid, in self.graph.query(q)}, + self.assertSetEqual({str(guid) for guid, in q(self.graph)}, {'http://example.com/entity#1234', 'http://example.com/entity#4321'}) # a single Substring statement that includes subtypes q = self.parser(self.schema.node(ns.bsfs.Entity), ast.filter.Any(ns.bse.comment, ast.filter.Substring('Myself'))) - self.assertSetEqual({str(guid) for guid, in self.graph.query(q)}, + self.assertSetEqual({str(guid) for guid, in q(self.graph)}, {'http://example.com/entity#1234', 'http://example.com/image#1234'}) # an Substring statement on an integer q = self.parser(self.schema.node(ns.bsfs.Entity), ast.filter.Any(ns.bse.filesize, ast.filter.Substring('32'))) - self.assertSetEqual({str(guid) for guid, in self.graph.query(q)}, + self.assertSetEqual({str(guid) for guid, in q(self.graph)}, {'http://example.com/entity#4321', 'http://example.com/image#4321'}) @@ -236,15 +236,15 @@ class TestParseFilter(unittest.TestCase): self.assertRaises(errors.BackendError, self.parser._starts_with, self.schema.node(ns.bsfs.Entity), ast.filter.StartsWith('hello world'), '?ent') # a single StartsWith statement q = self.parser(self.schema.node(ns.bsfs.Entity), ast.filter.Any(ns.bse.comment, ast.filter.StartsWith('hello'))) - self.assertSetEqual({str(guid) for guid, in self.graph.query(q)}, + self.assertSetEqual({str(guid) for guid, in q(self.graph)}, {'http://example.com/entity#1234', 'http://example.com/entity#4321'}) # a single StartsWith statement that includes subtypes q = self.parser(self.schema.node(ns.bsfs.Entity), ast.filter.Any(ns.bse.comment, ast.filter.StartsWith('Me, Mys'))) - self.assertSetEqual({str(guid) for guid, in self.graph.query(q)}, + self.assertSetEqual({str(guid) for guid, in q(self.graph)}, {'http://example.com/entity#1234', 'http://example.com/image#1234'}) # an StartsWith statement on an integer q = self.parser(self.schema.node(ns.bsfs.Entity), ast.filter.Any(ns.bse.filesize, ast.filter.StartsWith(432))) - self.assertSetEqual({str(guid) for guid, in self.graph.query(q)}, + self.assertSetEqual({str(guid) for guid, in q(self.graph)}, {'http://example.com/entity#4321', 'http://example.com/image#4321'}) @@ -253,15 +253,15 @@ class TestParseFilter(unittest.TestCase): self.assertRaises(errors.BackendError, self.parser._ends_with, self.schema.node(ns.bsfs.Entity), ast.filter.EndsWith('hello world'), '?ent') # a single EndsWith statement q = self.parser(self.schema.node(ns.bsfs.Entity), ast.filter.Any(ns.bse.comment, ast.filter.EndsWith('orld'))) - self.assertSetEqual({str(guid) for guid, in self.graph.query(q)}, + self.assertSetEqual({str(guid) for guid, in q(self.graph)}, {'http://example.com/entity#1234', 'http://example.com/entity#4321'}) # a single EndsWith statement that includes subtypes q = self.parser(self.schema.node(ns.bsfs.Entity), ast.filter.Any(ns.bse.comment, ast.filter.EndsWith('and I'))) - self.assertSetEqual({str(guid) for guid, in self.graph.query(q)}, + self.assertSetEqual({str(guid) for guid, in q(self.graph)}, {'http://example.com/entity#1234', 'http://example.com/image#1234'}) # an EndsWith statement on an integer q = self.parser(self.schema.node(ns.bsfs.Entity), ast.filter.Any(ns.bse.filesize, ast.filter.EndsWith(321))) - self.assertSetEqual({str(guid) for guid, in self.graph.query(q)}, + self.assertSetEqual({str(guid) for guid, in q(self.graph)}, {'http://example.com/entity#4321', 'http://example.com/image#4321'}) @@ -270,22 +270,22 @@ class TestParseFilter(unittest.TestCase): self.assertRaises(errors.BackendError, self.parser._less_than, self.schema.node(ns.bsfs.Entity), ast.filter.LessThan(2000), '?ent') # a single LessThan statement q = self.parser(self.schema.node(ns.bsfs.Image), ast.filter.Any(ns.bse.iso, ast.filter.LessThan(2000))) - self.assertSetEqual({str(guid) for guid, in self.graph.query(q)}, + self.assertSetEqual({str(guid) for guid, in q(self.graph)}, {'http://example.com/image#1234'}) # _less_than respects boundary q = self.parser(self.schema.node(ns.bsfs.Image), ast.filter.Any(ns.bse.iso, ast.filter.LessThan(1234, strict=True))) - self.assertSetEqual({str(guid) for guid, in self.graph.query(q)}, set()) + self.assertSetEqual({str(guid) for guid, in q(self.graph)}, set()) q = self.parser(self.schema.node(ns.bsfs.Image), ast.filter.Any(ns.bse.iso, ast.filter.LessThan(1234, strict=False))) - self.assertSetEqual({str(guid) for guid, in self.graph.query(q)}, + self.assertSetEqual({str(guid) for guid, in q(self.graph)}, {'http://example.com/image#1234'}) # a single LessThan statement that includes subtypes q = self.parser(self.schema.node(ns.bsfs.Entity), ast.filter.Any(ns.bse.filesize, ast.filter.LessThan(2000))) - self.assertSetEqual({str(guid) for guid, in self.graph.query(q)}, + self.assertSetEqual({str(guid) for guid, in q(self.graph)}, {'http://example.com/entity#1234', 'http://example.com/image#1234'}) # an LessThan statement on a string # always negative; note that http://example.com/tag#4321 is also not returned although its comment is a pure number q = self.parser(self.schema.node(ns.bsfs.Entity), ast.filter.Any(ns.bse.comment, ast.filter.LessThan(10_000))) - self.assertSetEqual({str(guid) for guid, in self.graph.query(q)}, set()) + self.assertSetEqual({str(guid) for guid, in q(self.graph)}, set()) def test_greater_than(self): @@ -293,22 +293,22 @@ class TestParseFilter(unittest.TestCase): self.assertRaises(errors.BackendError, self.parser._greater_than, self.schema.node(ns.bsfs.Entity), ast.filter.GreaterThan(2000), '?ent') # a single GreaterThan statement q = self.parser(self.schema.node(ns.bsfs.Image), ast.filter.Any(ns.bse.iso, ast.filter.GreaterThan(2000))) - self.assertSetEqual({str(guid) for guid, in self.graph.query(q)}, + self.assertSetEqual({str(guid) for guid, in q(self.graph)}, {'http://example.com/image#4321'}) # _greater_than respects boundary q = self.parser(self.schema.node(ns.bsfs.Image), ast.filter.Any(ns.bse.iso, ast.filter.GreaterThan(4321, strict=True))) - self.assertSetEqual({str(guid) for guid, in self.graph.query(q)}, set()) + self.assertSetEqual({str(guid) for guid, in q(self.graph)}, set()) q = self.parser(self.schema.node(ns.bsfs.Image), ast.filter.Any(ns.bse.iso, ast.filter.GreaterThan(4321, strict=False))) - self.assertSetEqual({str(guid) for guid, in self.graph.query(q)}, + self.assertSetEqual({str(guid) for guid, in q(self.graph)}, {'http://example.com/image#4321'}) # a single GreaterThan statement that includes subtypes q = self.parser(self.schema.node(ns.bsfs.Entity), ast.filter.Any(ns.bse.filesize, ast.filter.GreaterThan(2000))) - self.assertSetEqual({str(guid) for guid, in self.graph.query(q)}, + self.assertSetEqual({str(guid) for guid, in q(self.graph)}, {'http://example.com/entity#4321', 'http://example.com/image#4321'}) # an GreaterThan statement on a string # always positive q = self.parser(self.schema.node(ns.bsfs.Entity), ast.filter.Any(ns.bse.comment, ast.filter.GreaterThan(0))) - self.assertSetEqual({str(guid) for guid, in self.graph.query(q)}, + self.assertSetEqual({str(guid) for guid, in q(self.graph)}, {'http://example.com/entity#1234', 'http://example.com/entity#4321', 'http://example.com/image#1234'}) @@ -331,7 +331,7 @@ class TestParseFilter(unittest.TestCase): ast.filter.Any(ns.bse.filesize, ast.filter.Equals(1234)), ast.filter.Any(ns.bse.comment, ast.filter.Equals('Me, Myself, and I')), )) - self.assertSetEqual({str(guid) for guid, in self.graph.query(q)}, + self.assertSetEqual({str(guid) for guid, in q(self.graph)}, {'http://example.com/entity#1234'}) # all conditions have to match q = self.parser(self.schema.node(ns.bsfs.Entity), @@ -340,21 +340,21 @@ class TestParseFilter(unittest.TestCase): ast.filter.Any(ns.bse.filesize, ast.filter.Equals(1234)), ast.filter.Any(ns.bse.comment, ast.filter.Equals('Me, Myself, and I')), )) - self.assertSetEqual({str(guid) for guid, in self.graph.query(q)}, set()) + self.assertSetEqual({str(guid) for guid, in q(self.graph)}, set()) q = self.parser(self.schema.node(ns.bsfs.Entity), ast.filter.And( ast.filter.Is('http://example.com/entity#1234'), ast.filter.Any(ns.bse.filesize, ast.filter.Equals(4321)), ast.filter.Any(ns.bse.comment, ast.filter.Equals('Me, Myself, and I')), )) - self.assertSetEqual({str(guid) for guid, in self.graph.query(q)}, set()) + self.assertSetEqual({str(guid) for guid, in q(self.graph)}, set()) q = self.parser(self.schema.node(ns.bsfs.Entity), ast.filter.And( ast.filter.Is('http://example.com/entity#1234'), ast.filter.Any(ns.bse.filesize, ast.filter.Equals(1234)), ast.filter.Any(ns.bse.comment, ast.filter.Equals('foobar')), )) - self.assertSetEqual({str(guid) for guid, in self.graph.query(q)}, set()) + self.assertSetEqual({str(guid) for guid, in q(self.graph)}, set()) # And can be nested q = self.parser(self.schema.node(ns.bsfs.Entity), ast.filter.And( @@ -364,7 +364,7 @@ class TestParseFilter(unittest.TestCase): ast.filter.Any(ns.bse.comment, ast.filter.Equals('Me, Myself, and I')), ), )) - self.assertSetEqual({str(guid) for guid, in self.graph.query(q)}, + self.assertSetEqual({str(guid) for guid, in q(self.graph)}, {'http://example.com/entity#1234'}) @@ -387,7 +387,7 @@ class TestParseFilter(unittest.TestCase): ast.filter.Any(ns.bse.filesize, ast.filter.Equals(4321)), ast.filter.Any(ns.bse.comment, ast.filter.Equals('Me, Myself, and I')), )) - self.assertSetEqual({str(guid) for guid, in self.graph.query(q)}, + self.assertSetEqual({str(guid) for guid, in q(self.graph)}, {'http://example.com/entity#1234', 'http://example.com/image#1234', 'http://example.com/entity#4321', 'http://example.com/image#4321'}) # at least one condition has to match q = self.parser(self.schema.node(ns.bsfs.Entity), @@ -396,14 +396,14 @@ class TestParseFilter(unittest.TestCase): ast.filter.Any(ns.bse.filesize, ast.filter.Equals(8765)), ast.filter.Any(ns.bse.comment, ast.filter.Equals('foobar')), )) - self.assertSetEqual({str(guid) for guid, in self.graph.query(q)}, set()) + self.assertSetEqual({str(guid) for guid, in q(self.graph)}, set()) q = self.parser(self.schema.node(ns.bsfs.Entity), ast.filter.Or( ast.filter.Is('http://example.com/entity#1234'), ast.filter.Any(ns.bse.filesize, ast.filter.Equals(8765)), ast.filter.Any(ns.bse.comment, ast.filter.Equals('foobar')), )) - self.assertSetEqual({str(guid) for guid, in self.graph.query(q)}, + self.assertSetEqual({str(guid) for guid, in q(self.graph)}, {'http://example.com/entity#1234'}) q = self.parser(self.schema.node(ns.bsfs.Entity), ast.filter.Or( @@ -411,7 +411,7 @@ class TestParseFilter(unittest.TestCase): ast.filter.Any(ns.bse.filesize, ast.filter.Equals(4321)), ast.filter.Any(ns.bse.comment, ast.filter.Equals('foobar')), )) - self.assertSetEqual({str(guid) for guid, in self.graph.query(q)}, + self.assertSetEqual({str(guid) for guid, in q(self.graph)}, {'http://example.com/entity#4321', 'http://example.com/image#4321'}) q = self.parser(self.schema.node(ns.bsfs.Entity), ast.filter.Or( @@ -419,7 +419,7 @@ class TestParseFilter(unittest.TestCase): ast.filter.Any(ns.bse.filesize, ast.filter.Equals(8765)), ast.filter.Any(ns.bse.comment, ast.filter.Equals('Me, Myself, and I')), )) - self.assertSetEqual({str(guid) for guid, in self.graph.query(q)}, + self.assertSetEqual({str(guid) for guid, in q(self.graph)}, {'http://example.com/entity#1234', 'http://example.com/image#1234'}) # Or can be nested q = self.parser(self.schema.node(ns.bsfs.Entity), @@ -430,7 +430,7 @@ class TestParseFilter(unittest.TestCase): ast.filter.Any(ns.bse.comment, ast.filter.Equals('Me, Myself, and I')), ), )) - self.assertSetEqual({str(guid) for guid, in self.graph.query(q)}, + self.assertSetEqual({str(guid) for guid, in q(self.graph)}, {'http://example.com/entity#1234', 'http://example.com/image#1234', 'http://example.com/entity#4321', 'http://example.com/image#4321'}) @@ -451,14 +451,14 @@ class TestParseFilter(unittest.TestCase): # _any returns a valid query q = self.parser(self.schema.node(ns.bsfs.Entity), ast.filter.Any(ns.bse.filesize, ast.filter.Equals(1234))) - self.assertSetEqual({str(guid) for guid, in self.graph.query(q)}, + self.assertSetEqual({str(guid) for guid, in q(self.graph)}, {'http://example.com/entity#1234', 'http://example.com/image#1234'}) # _any can be nested q = self.parser(self.schema.node(ns.bsfs.Entity), ast.filter.Any(ns.bse.tag, ast.filter.Any(ns.bse.representative, ast.filter.Is('http://example.com/image#1234')))) - self.assertSetEqual({str(guid) for guid, in self.graph.query(q)}, + self.assertSetEqual({str(guid) for guid, in q(self.graph)}, {'http://example.com/entity#1234', 'http://example.com/image#1234'}) @@ -468,12 +468,12 @@ class TestParseFilter(unittest.TestCase): # All Nodes q = self.parser(self.schema.node(ns.bsfs.Entity), ast.filter.All(ns.bse.tag, ast.filter.Is('http://example.com/tag#1234'))) - self.assertSetEqual({str(guid) for guid, in self.graph.query(q)}, + self.assertSetEqual({str(guid) for guid, in q(self.graph)}, {'http://example.com/entity#1234', 'http://example.com/image#1234'}) # All values q = self.parser(self.schema.node(ns.bsfs.Entity), ast.filter.All(ns.bse.comment, ast.filter.Equals('hello world'))) - self.assertSetEqual({str(guid) for guid, in self.graph.query(q)}, + self.assertSetEqual({str(guid) for guid, in q(self.graph)}, {'http://example.com/entity#4321'}) # All on value within Or branch # entity#1234 is selected because all of its comments are in ("hello world", "Me, Myself, and I") @@ -481,12 +481,12 @@ class TestParseFilter(unittest.TestCase): ast.filter.All(ns.bse.comment, ast.filter.Or( ast.filter.Equals('hello world'), ast.filter.Equals('Me, Myself, and I')))) - self.assertSetEqual({str(guid) for guid, in self.graph.query(q)}, + self.assertSetEqual({str(guid) for guid, in q(self.graph)}, {'http://example.com/entity#1234', 'http://example.com/entity#4321', 'http://example.com/image#1234'}) # All requires at least one predicate/value q = self.parser(self.schema.node(ns.bsfs.Entity), ast.filter.All(ns.bse.comment, ast.filter.Equals('Me, Myself, and I'))) - self.assertSetEqual({str(guid) for guid, in self.graph.query(q)}, + self.assertSetEqual({str(guid) for guid, in q(self.graph)}, {'http://example.com/image#1234'}) # All within a statement q = self.parser(self.schema.node(ns.bsfs.Entity), @@ -498,18 +498,18 @@ class TestParseFilter(unittest.TestCase): )) ) ) - self.assertSetEqual({str(guid) for guid, in self.graph.query(q)}, + self.assertSetEqual({str(guid) for guid, in q(self.graph)}, {'http://example.com/entity#1234', 'http://example.com/image#1234'}) # All with reversed Predicate q = self.parser(self.schema.node(ns.bsfs.Tag), ast.filter.All(ast.filter.Predicate(ns.bse.tag, reverse=True), ast.filter.Is('http://example.com/entity#4321'))) - self.assertSetEqual({str(guid) for guid, in self.graph.query(q)}, + self.assertSetEqual({str(guid) for guid, in q(self.graph)}, {'http://example.com/tag#4321'}) # All with multiple predicates q = self.parser(self.schema.node(ns.bsfs.Entity), ast.filter.All(ast.filter.OneOf(ns.bse.tag, ns.bse.buddy), # entity#1234 (tag:tag#1234), entity#1234 (buddy:image#1234), image#1234(tag:tag#1234) ast.filter.Any(ns.bse.comment, ast.filter.Equals('Me, Myself, and I')))) # entity#1234, image#1234, tag#1234 - self.assertSetEqual({str(guid) for guid, in self.graph.query(q)}, + self.assertSetEqual({str(guid) for guid, in q(self.graph)}, {'http://example.com/entity#1234', 'http://example.com/image#1234'}) @@ -518,22 +518,22 @@ class TestParseFilter(unittest.TestCase): # Not applies on conditions q = self.parser(self.schema.node(ns.bsfs.Entity), ast.filter.Not(ast.filter.Is('http://example.com/entity#1234'))) - self.assertSetEqual({str(guid) for guid, in self.graph.query(q)}, + self.assertSetEqual({str(guid) for guid, in q(self.graph)}, {'http://example.com/image#1234', 'http://example.com/entity#4321', 'http://example.com/image#4321'}) # Not applies on conditions within branches q = self.parser(self.schema.node(ns.bsfs.Entity), ast.filter.Any(ns.bse.comment, ast.filter.Not(ast.filter.Equals('Me, Myself, and I')))) - self.assertSetEqual({str(guid) for guid, in self.graph.query(q)}, + self.assertSetEqual({str(guid) for guid, in q(self.graph)}, {'http://example.com/entity#1234', 'http://example.com/entity#4321'}) # Not applies on branches q = self.parser(self.schema.node(ns.bsfs.Entity), ast.filter.Not(ast.filter.Any(ns.bse.comment, ast.filter.Equals('Me, Myself, and I')))) - self.assertSetEqual({str(guid) for guid, in self.graph.query(q)}, + self.assertSetEqual({str(guid) for guid, in q(self.graph)}, {'http://example.com/entity#4321', 'http://example.com/image#4321'}) # Double Not cancel each other q = self.parser(self.schema.node(ns.bsfs.Entity), ast.filter.Not(ast.filter.Not(ast.filter.Is('http://example.com/entity#1234')))) - self.assertSetEqual({str(guid) for guid, in self.graph.query(q)}, + self.assertSetEqual({str(guid) for guid, in q(self.graph)}, {'http://example.com/entity#1234'}) # Not works within aggregation (and) q = self.parser(self.schema.node(ns.bsfs.Entity), @@ -541,7 +541,7 @@ class TestParseFilter(unittest.TestCase): ast.filter.Not(ast.filter.Is('http://example.com/entity#1234')), ast.filter.Any(ns.bse.comment, ast.filter.Equals('hello world')), )) - self.assertSetEqual({str(guid) for guid, in self.graph.query(q)}, + self.assertSetEqual({str(guid) for guid, in q(self.graph)}, {'http://example.com/entity#4321'}) # Not works within aggregation (or) q = self.parser(self.schema.node(ns.bsfs.Entity), @@ -549,7 +549,7 @@ class TestParseFilter(unittest.TestCase): ast.filter.Not(ast.filter.Is('http://example.com/entity#1234')), ast.filter.Any(ns.bse.comment, ast.filter.Equals('Me, Myself, and I')), )) - self.assertSetEqual({str(guid) for guid, in self.graph.query(q)}, + self.assertSetEqual({str(guid) for guid, in q(self.graph)}, {'http://example.com/entity#1234', 'http://example.com/entity#4321', 'http://example.com/image#1234', 'http://example.com/image#4321'}) # Not works outside aggregation (and) q = self.parser(self.schema.node(ns.bsfs.Entity), @@ -558,7 +558,7 @@ class TestParseFilter(unittest.TestCase): ast.filter.Is('http://example.com/entity#1234'), ast.filter.Any(ns.bse.comment, ast.filter.Equals('hello world')), ))) - self.assertSetEqual({str(guid) for guid, in self.graph.query(q)}, + self.assertSetEqual({str(guid) for guid, in q(self.graph)}, {'http://example.com/entity#4321', 'http://example.com/image#1234', 'http://example.com/image#4321'}) # Not works outside aggregation (or) q = self.parser(self.schema.node(ns.bsfs.Entity), @@ -567,7 +567,7 @@ class TestParseFilter(unittest.TestCase): ast.filter.Is('http://example.com/entity#4321'), ast.filter.Any(ns.bse.comment, ast.filter.Equals('Me, Myself, and I')), ))) - self.assertSetEqual({str(guid) for guid, in self.graph.query(q)}, + self.assertSetEqual({str(guid) for guid, in q(self.graph)}, {'http://example.com/image#4321'}) # Not mixed with branch, aggregation, id, and value q = self.parser(self.schema.node(ns.bsfs.Entity), @@ -580,7 +580,7 @@ class TestParseFilter(unittest.TestCase): ), ast.filter.Any(ns.bse.comment, ast.filter.Not(ast.filter.Equals('foobar'))), # entity#1234, entity#4321, image#1234 )) - self.assertSetEqual({str(guid) for guid, in self.graph.query(q)}, + self.assertSetEqual({str(guid) for guid, in q(self.graph)}, {'http://example.com/image#1234'}) @@ -590,21 +590,21 @@ class TestParseFilter(unittest.TestCase): # Has with GreaterThan constraint q = self.parser(self.schema.node(ns.bsfs.Entity), ast.filter.Has(ns.bse.comment, ast.filter.GreaterThan(0))) - self.assertSetEqual({str(guid) for guid, in self.graph.query(q)}, + self.assertSetEqual({str(guid) for guid, in q(self.graph)}, {'http://example.com/entity#1234', 'http://example.com/entity#4321', 'http://example.com/image#1234'}) q = self.parser(self.schema.node(ns.bsfs.Entity), ast.filter.Has(ns.bse.comment, ast.filter.GreaterThan(1))) - self.assertSetEqual({str(guid) for guid, in self.graph.query(q)}, + self.assertSetEqual({str(guid) for guid, in q(self.graph)}, {'http://example.com/entity#1234'}) # Has with Equals constraint q = self.parser(self.schema.node(ns.bsfs.Entity), ast.filter.Has(ns.bse.comment, 1)) - self.assertSetEqual({str(guid) for guid, in self.graph.query(q)}, + self.assertSetEqual({str(guid) for guid, in q(self.graph)}, {'http://example.com/entity#4321', 'http://example.com/image#1234'}) # Has with LessThan constraint q = self.parser(self.schema.node(ns.bsfs.Entity), ast.filter.Has(ns.bse.comment, ast.filter.LessThan(2))) - self.assertSetEqual({str(guid) for guid, in self.graph.query(q)}, + self.assertSetEqual({str(guid) for guid, in q(self.graph)}, {'http://example.com/entity#4321', 'http://example.com/image#1234', 'http://example.com/image#4321'}) # Has with multiple constraints self.graph.add((rdflib.URIRef('http://example.com/entity#1234'), rdflib.URIRef(ns.bse.comment), rdflib.Literal('extra1', datatype=rdflib.XSD.string))) @@ -616,17 +616,17 @@ class TestParseFilter(unittest.TestCase): self.graph.add((rdflib.URIRef('http://example.com/entity#4321'), rdflib.URIRef(ns.bse.comment), rdflib.Literal('extra2', datatype=rdflib.XSD.string))) q = self.parser(self.schema.node(ns.bsfs.Entity), ast.filter.Has(ns.bse.comment, ast.filter.And(ast.filter.GreaterThan(1), ast.filter.LessThan(5)))) - self.assertSetEqual({str(guid) for guid, in self.graph.query(q)}, + self.assertSetEqual({str(guid) for guid, in q(self.graph)}, {'http://example.com/entity#4321'}) # Has with OneOf predicate q = self.parser(self.schema.node(ns.bsfs.Entity), ast.filter.Has(ast.filter.OneOf(ns.bse.tag, ns.bse.buddy), ast.filter.GreaterThan(1))) - self.assertSetEqual({str(guid) for guid, in self.graph.query(q)}, + self.assertSetEqual({str(guid) for guid, in q(self.graph)}, {'http://example.com/entity#1234', 'http://example.com/entity#4321'}) # Has with reversed predicate q = self.parser(self.schema.node(ns.bsfs.Tag), ast.filter.Has(ast.filter.Predicate(ns.bse.tag, reverse=True), ast.filter.GreaterThan(1))) - self.assertSetEqual({str(guid) for guid, in self.graph.query(q)}, + self.assertSetEqual({str(guid) for guid, in q(self.graph)}, {'http://example.com/tag#1234'}) @@ -643,23 +643,23 @@ class TestParseFilter(unittest.TestCase): self.assertRaises(errors.ConsistencyError, self.parser._distance, self.schema.literal(ns.bsfs.Colors), ast.filter.Distance([1,2,3,4,5], 1), '') # _distance respects threshold q = self.parser(self.schema.node(ns.bsfs.Entity), ast.filter.Any(ns.bse.colors, ast.filter.Distance([2,4,3,1], 4))) - self.assertSetEqual({str(guid) for guid, in self.graph.query(q)}, + self.assertSetEqual({str(guid) for guid, in q(self.graph)}, {'http://example.com/entity#1234', 'http://example.com/entity#4321', 'http://example.com/image#1234'}) q = self.parser(self.schema.node(ns.bsfs.Entity), ast.filter.Any(ns.bse.colors, ast.filter.Distance([2,4,3,1], 3))) - self.assertSetEqual({str(guid) for guid, in self.graph.query(q)}, + self.assertSetEqual({str(guid) for guid, in q(self.graph)}, {'http://example.com/entity#4321', 'http://example.com/image#1234'}) q = self.parser(self.schema.node(ns.bsfs.Entity), ast.filter.Any(ns.bse.colors, ast.filter.Distance([2,4,3,1], 2))) - self.assertSetEqual({str(guid) for guid, in self.graph.query(q)}, + self.assertSetEqual({str(guid) for guid, in q(self.graph)}, {'http://example.com/image#1234'}) # result set can be empty q = self.parser(self.schema.node(ns.bsfs.Entity), ast.filter.Any(ns.bse.colors, ast.filter.Distance([2,4,3,1], 1))) - self.assertSetEqual({str(guid) for guid, in self.graph.query(q)}, set()) + self.assertSetEqual({str(guid) for guid, in q(self.graph)}, set()) # _distance respects strict q = self.parser(self.schema.node(ns.bsfs.Entity), ast.filter.Any(ns.bse.colors, ast.filter.Distance([1,2,3,4], 0, False))) - self.assertSetEqual({str(guid) for guid, in self.graph.query(q)}, + self.assertSetEqual({str(guid) for guid, in q(self.graph)}, {'http://example.com/entity#1234'}) q = self.parser(self.schema.node(ns.bsfs.Entity), ast.filter.Any(ns.bse.colors, ast.filter.Distance([1,2,3,4], 0, True))) - self.assertSetEqual({str(guid) for guid, in self.graph.query(q)}, set()) + self.assertSetEqual({str(guid) for guid, in q(self.graph)}, set()) def test_one_of(self): # _one_of expects a node @@ -725,7 +725,7 @@ class TestParseFilter(unittest.TestCase): ast.filter.Any(ast.filter.OneOf(ns.bse.tag, ns.bse.buddy), ast.filter.Any(ast.filter.OneOf(ns.bse.comment), ast.filter.Equals('Me, Myself, and I')))) - self.assertSetEqual({str(guid) for guid, in self.graph.query(q)}, + self.assertSetEqual({str(guid) for guid, in q(self.graph)}, {'http://example.com/entity#1234', 'http://example.com/image#1234'}) @@ -757,13 +757,13 @@ class TestParseFilter(unittest.TestCase): ast.filter.Any(ns.bse.representative, ast.filter.Any(ns.bse.filesize, ast.filter.Equals(1234))))) - self.assertSetEqual({str(guid) for guid, in self.graph.query(q)}, + self.assertSetEqual({str(guid) for guid, in q(self.graph)}, {'http://example.com/entity#1234', 'http://example.com/image#1234'}) q = self.parser(self.schema.node(ns.bsfs.Tag), ast.filter.Any(ast.filter.Predicate(ns.bse.tag, reverse=True), ast.filter.Any(ns.bse.filesize, ast.filter.LessThan(2000)))) - self.assertSetEqual({str(guid) for guid, in self.graph.query(q)}, + self.assertSetEqual({str(guid) for guid, in q(self.graph)}, {'http://example.com/tag#1234'}) diff --git a/test/triple_store/sparql/test_sparql.py b/test/triple_store/sparql/test_sparql.py index 7fbfb65..c58fae3 100644 --- a/test/triple_store/sparql/test_sparql.py +++ b/test/triple_store/sparql/test_sparql.py @@ -556,6 +556,76 @@ class TestSparqlStore(unittest.TestCase): self.assertSetEqual(set(q), tag_ids) + def test_fetch(self): + # store setup + store = SparqlStore.Open() + store.schema = self.schema + # add instances + ent_type = self.schema.node(ns.bsfs.Entity) + tag_type = self.schema.node(ns.bsfs.Tag) + ent_ids = {URI('http://example.com/me/entity#1234'), URI('http://example.com/me/entity#4321')} + tag_ids = {URI('http://example.com/me/tag#1234'), URI('http://example.com/me/tag#4321')} + store.create(ent_type, ent_ids) + store.create(tag_type, tag_ids) + store.set(ent_type, ent_ids, self.schema.predicate(ns.bse.tag), tag_ids) + store.set(ent_type, {URI('http://example.com/me/entity#1234')}, self.schema.predicate(ns.bse.filesize), {1234}) + store.set(ent_type, {URI('http://example.com/me/entity#4321')}, self.schema.predicate(ns.bse.filesize), {4321}) + store.set(ent_type, {URI('http://example.com/me/entity#1234')}, self.schema.predicate(ns.bse.comment), {'hello world'}) + # node_type must be a node from the schema + self.assertRaises(errors.ConsistencyError, list, store.fetch(self.schema.literal(ns.bsfs.Literal), + ast.filter.FilterExpression(), ast.fetch.FetchExpression())) + self.assertRaises(errors.ConsistencyError, list, store.fetch(self.schema.node(ns.bsfs.Node).child(ns.bsfs.Invalid), + ast.filter.FilterExpression(), ast.fetch.FetchExpression())) + # requires a filter and a fetch query + self.assertRaises(TypeError, list, store.fetch(self.schema.node(ns.bsfs.Entity), None, ast.fetch.FetchExpression())) + self.assertRaises(TypeError, list, store.fetch(self.schema.node(ns.bsfs.Entity), 1234, ast.fetch.FetchExpression())) + self.assertRaises(TypeError, list, store.fetch(self.schema.node(ns.bsfs.Entity), 'hello', ast.fetch.FetchExpression())) + self.assertRaises(TypeError, list, store.fetch(self.schema.node(ns.bsfs.Entity), ast.filter.FilterExpression(), None)) + self.assertRaises(TypeError, list, store.fetch(self.schema.node(ns.bsfs.Entity), ast.filter.FilterExpression(), 1234)) + self.assertRaises(TypeError, list, store.fetch(self.schema.node(ns.bsfs.Entity), ast.filter.FilterExpression(), 'hello')) + # fetch emits triples + self.assertSetEqual(set(store.fetch(self.schema.node(ns.bsfs.Entity), + ast.filter.Is('http://example.com/me/entity#1234'), + ast.fetch.Value(ns.bse.filesize, 'filesize'), + )), { + (URI('http://example.com/me/entity#1234'), 'filesize', 1234), + }) + # fetch respects filter query + self.assertSetEqual(set(store.fetch(self.schema.node(ns.bsfs.Entity), + ast.filter.IsIn('http://example.com/me/entity#1234', 'http://example.com/me/entity#4321'), + ast.fetch.Value(ns.bse.filesize, 'filesize'), + )), { + (URI('http://example.com/me/entity#1234'), 'filesize', 1234), + (URI('http://example.com/me/entity#4321'), 'filesize', 4321), + }) + # fetch ignores missing data + self.assertSetEqual(set(store.fetch(self.schema.node(ns.bsfs.Entity), + ast.filter.IsIn('http://example.com/me/entity#1234', 'http://example.com/me/entity#4321'), + ast.fetch.Value(ns.bse.comment, 'comment'), + )), { + (URI('http://example.com/me/entity#1234'), 'comment', 'hello world'), + }) + # fetch emits all triples + self.assertSetEqual(set(store.fetch(self.schema.node(ns.bsfs.Entity), + ast.filter.Is('http://example.com/me/entity#1234'), + ast.fetch.All( + ast.fetch.Value(ns.bse.filesize, 'filesize'), + ast.fetch.Node(ns.bse.tag, 'tag'), + ) + )), { + (URI('http://example.com/me/entity#1234'), 'filesize', 1234), + (URI('http://example.com/me/entity#1234'), 'tag', URI('http://example.com/me/tag#1234')), + (URI('http://example.com/me/entity#1234'), 'tag', URI('http://example.com/me/tag#4321')), + }) + # triples do not repeat + triples = list(store.fetch(self.schema.node(ns.bsfs.Entity), ast.filter.Is('http://example.com/me/entity#1234'), + ast.fetch.All( + ast.fetch.Value(ns.bse.filesize, 'filesize'), + ast.fetch.Node(ns.bse.tag, 'tag'), + ) + )) + self.assertEqual(len(triples), 3) + def test_exists(self): # store setup store = SparqlStore.Open() diff --git a/test/triple_store/sparql/test_utils.py b/test/triple_store/sparql/test_utils.py new file mode 100644 index 0000000..073b8f8 --- /dev/null +++ b/test/triple_store/sparql/test_utils.py @@ -0,0 +1,155 @@ +""" + +Part of the bsfs test suite. +A copy of the license is provided with the project. +Author: Matthias Baumgartner, 2022 +""" +# standard imports +import operator +import re +import unittest + +# external imports +import rdflib + +# bsie imports +from bsfs.namespace import ns + +# objects to test +from bsfs.triple_store.sparql.utils import GenHopName, Query + + +## code ## + +class TestGenHopName(unittest.TestCase): + def test_next(self): + # baseline + self.assertEqual(next(GenHopName(prefix='?foo', start=123)), '?foo123') + # respects prefix + self.assertEqual(next(GenHopName(prefix='?bar', start=123)), '?bar123') + # respects start + self.assertEqual(next(GenHopName(prefix='?foo', start=321)), '?foo321') + # counts up + cnt = GenHopName(prefix='?foo', start=998) + self.assertEqual(next(cnt), '?foo998') + self.assertEqual(next(cnt), '?foo999') + self.assertEqual(next(cnt), '?foo1000') + self.assertEqual(next(cnt), '?foo1001') + + def test_essentials(self): + # can get the prefix + self.assertEqual(GenHopName(prefix='?foo', start=123).prefix, '?foo') + # can get the counter + self.assertEqual(GenHopName(prefix='?foo', start=123).curr, 122) + + +class TestQuery(unittest.TestCase): + def setUp(self): + self.root_type = 'http://bsfs.ai/schema/Entity' + self.root_head = '?root' + self.select = (('?head', 'name'), ) + self.where = f'?root <{ns.bse.tag}> ?head' + + def test_essentials(self): + # can access members + q = Query(self.root_type, self.root_head, self.select, self.where) + self.assertEqual(q.root_type, self.root_type) + self.assertEqual(q.root_head, self.root_head) + self.assertEqual(q.select, self.select) + self.assertEqual(q.where, self.where) + # comparison + self.assertEqual(q, Query(self.root_type, self.root_head, self.select, self.where)) + self.assertEqual(hash(q), hash(Query(self.root_type, self.root_head, self.select, self.where))) + # comparison respects root_type + self.assertNotEqual(q, Query('http://bsfs.ai/schema/Tag', self.root_head, self.select, self.where)) + self.assertNotEqual(hash(q), hash(Query('http://bsfs.ai/schema/Tag', self.root_head, self.select, self.where))) + # comparison respects root_head + self.assertNotEqual(q, Query(self.root_type, '?foo', self.select, self.where)) + self.assertNotEqual(hash(q), hash(Query(self.root_type, '?foo', self.select, self.where))) + # comparison respects select + self.assertNotEqual(q, Query(self.root_type, self.root_head, (('?head', 'foo'), ), self.where)) + self.assertNotEqual(hash(q), hash(Query(self.root_type, self.root_head, (('?head', 'foo'), ), self.where))) + # comparison respects where + self.assertNotEqual(q, Query(self.root_type, self.root_head, self.select, '?root bse:filename ?head')) + self.assertNotEqual(hash(q), hash(Query(self.root_type, self.root_head, self.select, '?root bse:filename ?head'))) + # string conversion + self.assertEqual(str(q), q.query) + self.assertEqual(repr(q), "Query(http://bsfs.ai/schema/Entity, ?root, (('?head', 'name'),), ?root <http://bsfs.ai/schema/Entity#tag> ?head)") + + def test_add(self): + q = Query(self.root_type, self.root_head, self.select, self.where) + # can only add a query + self.assertRaises(TypeError, operator.add, q, 1234) + self.assertRaises(TypeError, operator.add, q, 'foobar') + # root type and head must match + self.assertRaises(ValueError, operator.add, q, Query('http://bsfs.ai/schema/Tag', self.root_head)) + self.assertRaises(ValueError, operator.add, q, Query(self.root_type, '?foobar')) + # select and were are combined + combo = q + Query(self.root_type, self.root_head, (('?foo', 'bar'), ), f'?root <{ns.bse.filename}> ?foo') + self.assertEqual(combo.select, (('?head', 'name'), ('?foo', 'bar'))) + self.assertEqual(combo.where, f'?root <{ns.bse.tag}> ?head . ?root <{ns.bse.filename}> ?foo') + # select can be empty + combo = q + Query(self.root_type, self.root_head, None, f'?root <{ns.bse.filename}> ?foo') + self.assertEqual(combo.select, (('?head', 'name'), )) + combo = Query(self.root_type, self.root_head, None, f'?root <{ns.bse.filename}> ?foo') + q + self.assertEqual(combo.select, (('?head', 'name'), )) + combo = Query(self.root_type, self.root_head, None, self.where) + Query(self.root_type, self.root_head, None, f'?root <{ns.bse.filename}> ?foo') + self.assertEqual(combo.select, tuple()) + # where can be empty + combo = q + Query(self.root_type, self.root_head, (('?foo', 'bar'), )) + self.assertEqual(combo.where, self.where) + combo = Query(self.root_type, self.root_head, (('?foo', 'bar'), )) + q + self.assertEqual(combo.where, self.where) + combo = Query(self.root_type, self.root_head, self.select) + Query(self.root_type, self.root_head, (('?foo', 'bar'), )) + self.assertEqual(combo.where, '') + + def test_names(self): + self.assertEqual(Query(self.root_type, self.root_head, (('?head', 'name'), ), self.where).names, + ('name', )) + self.assertEqual(Query(self.root_type, self.root_head, (('?head', 'name'), ('?foo', 'bar')), self.where).names, + ('name', 'bar')) + + def test_query(self): + def normalize(value): + value = value.strip() + value = value.lower() + value = value.replace(r'\n', ' ') + value, _ = re.subn('\s\s+', ' ', value) + return value + # query composes a valid query + q = Query(self.root_type, self.root_head, self.select, self.where) + self.assertEqual(normalize(q.query), normalize(f'select ?root (?head as ?name) where {{ ?root <{ns.rdf.type}>/<{ns.rdfs.subClassOf}>* <http://bsfs.ai/schema/Entity> . ?root <{ns.bse.tag}> ?head }}')) + # select and where are optional + q = Query(self.root_type, self.root_head) + self.assertEqual(normalize(q.query), normalize(f'select ?root where {{ ?root <{ns.rdf.type}>/<{ns.rdfs.subClassOf}>* <http://bsfs.ai/schema/Entity> . }}')) + # select and where need not to correspond + q = Query(self.root_type, self.root_head, (('?head', 'name'), )) + self.assertEqual(normalize(q.query), normalize(f'select ?root (?head as ?name) where {{ ?root <{ns.rdf.type}>/<{ns.rdfs.subClassOf}>* <http://bsfs.ai/schema/Entity> . }}')) + # query is used for string representation + self.assertEqual(str(q), q.query) + + def test_call(self): + graph = rdflib.Graph() + # schema + graph.add((rdflib.URIRef('http://bsfs.ai/schema/Document'), rdflib.URIRef(ns.rdfs.subClassOf), rdflib.URIRef('http://bsfs.ai/schema/Entity'))) + # nodes + graph.add((rdflib.URIRef('http://example.com/entity#1234'), rdflib.URIRef(ns.rdf.type), rdflib.URIRef('http://bsfs.ai/schema/Entity'))) + graph.add((rdflib.URIRef('http://example.com/doc#1234'), rdflib.URIRef(ns.rdf.type), rdflib.URIRef('http://bsfs.ai/schema/Document'))) + # links + graph.add((rdflib.URIRef('http://example.com/entity#1234'), rdflib.URIRef(ns.bse.tag), rdflib.Literal('tag#1234', datatype=rdflib.XSD.string))) + graph.add((rdflib.URIRef('http://example.com/doc#1234'), rdflib.URIRef(ns.bse.tag), rdflib.Literal('tag#1234', datatype=rdflib.XSD.string))) + # run query on a given graph + query = Query(self.root_type, self.root_head, self.select, self.where) + self.assertSetEqual(set(query(graph)), { + (rdflib.URIRef('http://example.com/entity#1234'), rdflib.Literal('tag#1234', datatype=rdflib.XSD.string)), + (rdflib.URIRef('http://example.com/doc#1234'), rdflib.Literal('tag#1234', datatype=rdflib.XSD.string)), + }) + # query actually considers the passed graph + self.assertSetEqual(set(query(rdflib.Graph())), set()) + +## main ## + +if __name__ == '__main__': + unittest.main() + +## EOF ## diff --git a/test/triple_store/test_base.py b/test/triple_store/test_base.py index a0c3260..56a2539 100644 --- a/test/triple_store/test_base.py +++ b/test/triple_store/test_base.py @@ -38,6 +38,9 @@ class DummyBase(TripleStoreBase): def get(self, node_type, query): pass + def fetch(self, node_type, filter, fetch): + pass + def exists(self, node_type, guids): pass |