aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatthias Baumgartner <dev@igsor.net>2023-03-02 14:55:57 +0100
committerMatthias Baumgartner <dev@igsor.net>2023-03-02 14:55:57 +0100
commit36d07cc6e0ec0f53001bfc5045437a374ebb895f (patch)
treeaa046ac75029f8879f0c03bc77ed2a5ac61f3aa0
parentcd27775b406482b11f44575ab196501a30d9b075 (diff)
downloadbsfs-36d07cc6e0ec0f53001bfc5045437a374ebb895f.tar.gz
bsfs-36d07cc6e0ec0f53001bfc5045437a374ebb895f.tar.bz2
bsfs-36d07cc6e0ec0f53001bfc5045437a374ebb895f.zip
empty fetch result in Nodes
-rw-r--r--bsfs/graph/nodes.py58
-rw-r--r--test/graph/ac/test_null.py9
-rw-r--r--test/graph/test_nodes.py60
3 files changed, 97 insertions, 30 deletions
diff --git a/bsfs/graph/nodes.py b/bsfs/graph/nodes.py
index 91cbb5d..c6bd5d8 100644
--- a/bsfs/graph/nodes.py
+++ b/bsfs/graph/nodes.py
@@ -249,32 +249,38 @@ class Nodes():
# add access controls to fetch
fetch = self._ac.fetch_read(self.node_type, fetch)
- # compose filter ast
- filter = ast.filter.IsIn(self.guids) # pylint: disable=redefined-builtin
- # add access controls to filter
- filter = self._ac.filter_read(self.node_type, filter)
-
- # validate queries
- validate.Filter(self._backend.schema)(self.node_type, filter)
- validate.Fetch(self._backend.schema)(self.node_type, fetch)
-
- # process results, convert if need be
- def triple_iter():
- # query the backend
- triples = self._backend.fetch(self.node_type, filter, fetch)
- # process triples
- for root, name, raw in triples:
- # get node
- node = Nodes(self._backend, self._ac, self.node_type, {root})
- # get path
- path, tail = name2path[name]
- # covert raw to value
- if isinstance(tail.range, bsc.Node):
- value = Nodes(self._backend, self._ac, tail.range, {raw})
- else:
- value = raw
- # emit triple
- yield node, path, value
+ if len(self._guids) == 0:
+ # shortcut: no need to query; no triples
+ # FIXME: if the Fetch query can given by the user, we might want to check its validity
+ def triple_iter():
+ return []
+ else:
+ # compose filter ast
+ filter = ast.filter.IsIn(self.guids) # pylint: disable=redefined-builtin
+ # add access controls to filter
+ filter = self._ac.filter_read(self.node_type, filter) # type: ignore [assignment]
+
+ # validate queries
+ validate.Filter(self._backend.schema)(self.node_type, filter)
+ validate.Fetch(self._backend.schema)(self.node_type, fetch)
+
+ # process results, convert if need be
+ def triple_iter():
+ # query the backend
+ triples = self._backend.fetch(self.node_type, filter, fetch)
+ # process triples
+ for root, name, raw in triples:
+ # get node
+ node = Nodes(self._backend, self._ac, self.node_type, {root})
+ # get path
+ path, tail = name2path[name]
+ # covert raw to value
+ if isinstance(tail.range, bsc.Node):
+ value = Nodes(self._backend, self._ac, tail.range, {raw})
+ else:
+ value = raw
+ # emit triple
+ yield node, path, value
# simplify by default
view_kwargs['node'] = view_kwargs.get('node', len(self._guids) != 1)
diff --git a/test/graph/ac/test_null.py b/test/graph/ac/test_null.py
index 544a01e..6053f81 100644
--- a/test/graph/ac/test_null.py
+++ b/test/graph/ac/test_null.py
@@ -136,6 +136,15 @@ class TestNullAC(unittest.TestCase):
# query can be none
self.assertIsNone(ac.filter_read(self.ent_type, None))
+ def test_fetch_read(self):
+ query = ast.fetch.All(
+ ast.fetch.Fetch(ns.bse.tag, ast.fetch.Value(ns.bse.label, 'tag_label')),
+ ast.fetch.Node(ns.bse.tag, 'tag_node'),
+ ast.fetch.Value(ns.bse.iso, 'iso'))
+ ac = NullAC(self.backend, self.user)
+ # NullAC returns query
+ self.assertEqual(query, ac.fetch_read(self.ent_type, query))
+
## main ##
diff --git a/test/graph/test_nodes.py b/test/graph/test_nodes.py
index 9541656..bf73e6e 100644
--- a/test/graph/test_nodes.py
+++ b/test/graph/test_nodes.py
@@ -346,6 +346,10 @@ class TestNodes(unittest.TestCase):
Nodes(self.backend, self.ac, self.user_type, {URI('http://example.com/me/user#1234'), URI('http://example.com/me/user#4321')}))
self.assertSetEqual(curr, set(self.backend._graph))
+ # can set on empty nodes
+ nodes = Nodes(self.backend, self.ac, self.ent_type, {})
+ self.assertEqual(nodes, nodes.set(self.p_filesize.uri, 1234))
+
def test_set_from_iterable(self):
self.assertSetEqual(set(self.backend._graph), self.schema_triples | set())
@@ -388,6 +392,11 @@ class TestNodes(unittest.TestCase):
(self.p_author.uri, Nodes(self.backend, self.ac, self.user_type, {URI('http://example.com/me/user#1234'), URI('http://example.com/me/user#4321')}))))
self.assertSetEqual(curr, set(self.backend._graph))
+ # can set on empty nodes
+ nodes = Nodes(self.backend, self.ac, self.ent_type, {})
+ self.assertEqual(nodes, nodes.set_from_iterable([(self.p_filesize.uri, 1234)]))
+
+
def test_get(self):
# setup: add some instances
Nodes(self.backend, self.ac, self.ent_type, {'http://example.com/me/entity#1234'}) \
@@ -403,6 +412,7 @@ class TestNodes(unittest.TestCase):
.set(bst.label, 'tag_label_4321')
# setup: get nodes instance
nodes = Nodes(self.backend, self.ac, self.ent_type, self.ent_ids)
+
# must pass at least one path
self.assertRaises(AttributeError, nodes.get)
# view must be list or dict
@@ -467,10 +477,52 @@ class TestNodes(unittest.TestCase):
Nodes(self.backend, self.ac, self.ent_type, {'http://example.com/me/entity#1234'}): {'hello world'},
})
- # FIXME: What if I call `get` with a single predicate and a single node, but
- # that node has no value for that predicate?
- # so, essentially, what if triples is empty? -> Also check in test_result!
- raise NotImplementedError()
+ # results can be empty
+ nodes = Nodes(self.backend, self.ac, self.ent_type, {'http://example.com/me/entity#4321'}) # has filesize, tag but no comment
+ # unique paths return the default value
+ self.assertIsNone(nodes.get(ns.bse.author))
+ self.assertEqual(nodes.get(ns.bse.author, default=1234), 1234)
+ # non-unique paths return an empty set
+ self.assertSetEqual(nodes.get(ns.bse.comment), set())
+
+ # nodes can have no guids
+ nodes = Nodes(self.backend, self.ac, self.ent_type, set())
+ # empty nodes does not excuse an invalid request
+ self.assertRaises(TypeError, nodes.get, 1234)
+ self.assertRaises(errors.ConsistencyError, nodes.get, ns.bse.invalid)
+ # list view always returns an empty list
+ self.assertListEqual(list(nodes.get(ns.bse.comment, view=list)), [])
+ self.assertListEqual(list(nodes.get(ns.bse.comment, view=list, node=True)), [])
+ self.assertListEqual(list(nodes.get(ns.bse.comment, view=list, path=True)), [])
+ self.assertListEqual(list(nodes.get(ns.bse.comment, view=list, node=True, path=True, value=True)), [])
+ self.assertListEqual(list(nodes.get(ns.bse.comment, view=list, node=False)), [])
+ self.assertListEqual(list(nodes.get(ns.bse.comment, view=list, path=False)), [])
+ self.assertListEqual(list(nodes.get(ns.bse.comment, view=list, node=False, path=False, value=False)), [])
+ self.assertListEqual(list(nodes.get(ns.bse.comment, ns.bse.filesize, view=list)), [])
+ self.assertListEqual(list(nodes.get(ns.bse.comment, ns.bse.filesize, view=list, node=True)), [])
+ self.assertListEqual(list(nodes.get(ns.bse.comment, ns.bse.filesize, view=list, path=True)), [])
+ self.assertListEqual(list(nodes.get(ns.bse.comment, ns.bse.filesize, view=list, node=True, path=True, value=True)), [])
+ self.assertListEqual(list(nodes.get(ns.bse.comment, ns.bse.filesize, view=list, node=False)), [])
+ self.assertListEqual(list(nodes.get(ns.bse.comment, ns.bse.filesize, view=list, path=False)), [])
+ self.assertListEqual(list(nodes.get(ns.bse.comment, ns.bse.filesize, view=list, node=False, path=False, value=False)), [])
+ # dict view returns an empty dict or an empty set
+ self.assertDictEqual(nodes.get(ns.bse.comment, view=dict), {})
+ self.assertDictEqual(nodes.get(ns.bse.comment, view=dict, node=True), {})
+ self.assertDictEqual(nodes.get(ns.bse.comment, view=dict, path=True), {})
+ self.assertDictEqual(nodes.get(ns.bse.comment, view=dict, node=True, path=True, value=True, default=None), {})
+ self.assertSetEqual(nodes.get(ns.bse.comment, view=dict, node=False), set())
+ self.assertDictEqual(nodes.get(ns.bse.comment, view=dict, path=False), {})
+ self.assertSetEqual(nodes.get(ns.bse.comment, view=dict, node=False, path=False), set())
+ self.assertSetEqual(nodes.get(ns.bse.comment, view=dict, node=False, path=False, value=False, default=None), set())
+ self.assertDictEqual(nodes.get(ns.bse.comment, ns.bse.filesize, view=dict), {})
+ self.assertDictEqual(nodes.get(ns.bse.comment, ns.bse.filesize, view=dict, node=True), {})
+ self.assertDictEqual(nodes.get(ns.bse.comment, ns.bse.filesize, view=dict, path=True), {})
+ self.assertDictEqual(nodes.get(ns.bse.comment, ns.bse.filesize, view=dict, node=True, path=True, value=True, default=None), {})
+ self.assertDictEqual(nodes.get(ns.bse.comment, ns.bse.filesize, view=dict, node=False), {})
+ self.assertDictEqual(nodes.get(ns.bse.comment, ns.bse.filesize, view=dict, path=False), {})
+ self.assertSetEqual(nodes.get(ns.bse.comment, ns.bse.filesize, view=dict, node=False, path=False), set())
+ self.assertSetEqual(nodes.get(ns.bse.comment, ns.bse.filesize, view=dict, node=False, path=False, value=False, default=None), set())
+
def test_getattr(self):
nodes = Nodes(self.backend, self.ac, self.ent_type, {'http://example.com/me/entity#1234'})