aboutsummaryrefslogtreecommitdiffstats
path: root/bsfs/graph/graph.py
diff options
context:
space:
mode:
Diffstat (limited to 'bsfs/graph/graph.py')
-rw-r--r--bsfs/graph/graph.py113
1 files changed, 113 insertions, 0 deletions
diff --git a/bsfs/graph/graph.py b/bsfs/graph/graph.py
new file mode 100644
index 0000000..b7b9f1c
--- /dev/null
+++ b/bsfs/graph/graph.py
@@ -0,0 +1,113 @@
+"""
+
+Part of the BlackStar filesystem (bsfs) module.
+A copy of the license is provided with the project.
+Author: Matthias Baumgartner, 2022
+"""
+# imports
+import os
+import typing
+
+# bsfs imports
+from bsfs.schema import Schema
+from bsfs.triple_store import TripleStoreBase
+from bsfs.utils import URI, typename
+
+# inner-module imports
+from . import nodes as _nodes
+
+# exports
+__all__: typing.Sequence[str] = (
+ 'Graph',
+ )
+
+
+## code ##
+
+class Graph():
+ """The Graph class is
+
+ The Graph class provides a convenient interface to query and access a graph.
+ Since it logically builds on the concept of graphs it is easier to
+ navigate than raw triple stores. Naturally, it uses a triple store
+ as *backend*. It also controls actions via access permissions to a *user*.
+
+ """
+
+ # link to the triple storage backend.
+ _backend: TripleStoreBase
+
+ # user uri.
+ _user: URI
+
+ def __init__(self, backend: TripleStoreBase, user: URI):
+ self._backend = backend
+ self._user = user
+ # ensure Graph schema requirements
+ self.migrate(self._backend.schema)
+
+ def __hash__(self) -> int:
+ return hash((type(self), self._backend, self._user))
+
+ def __eq__(self, other) -> bool:
+ return isinstance(other, type(self)) \
+ and self._backend == other._backend \
+ and self._user == other._user
+
+ def __repr__(self) -> str:
+ return f'{typename(self)}(backend={repr(self._backend)}, user={self._user})'
+
+ def __str__(self) -> str:
+ return f'{typename(self)}({str(self._backend)}, {self._user})'
+
+ @property
+ def schema(self) -> Schema:
+ """Return the store's local schema."""
+ return self._backend.schema
+
+ def migrate(self, schema: Schema, append: bool = True) -> 'Graph':
+ """Migrate the current schema to a new *schema*.
+
+ Appends to the current schema by default; control this via *append*.
+ The `Graph` may add additional classes to the schema that are required for its interals.
+
+ """
+ # check args
+ if not isinstance(schema, Schema):
+ raise TypeError(schema)
+ # append to current schema
+ if append:
+ schema = schema + self._backend.schema
+ # add Graph schema requirements
+ with open(os.path.join(os.path.dirname(__file__), 'schema.nt'), mode='rt', encoding='UTF-8') as ifile:
+ schema = schema + Schema.from_string(ifile.read())
+ # migrate schema in backend
+ # FIXME: consult access controls!
+ self._backend.schema = schema
+ # return self
+ return self
+
+ def nodes(self, node_type: URI, guids: typing.Iterable[URI]) -> _nodes.Nodes:
+ """Return nodes *guids* of type *node_type* as a `bsfs.graph.Nodes` instance.
+
+ Note that the *guids* need not to exist (however, the *node_type* has
+ to be part of the schema). Inexistent guids will be created (using
+ *node_type*) once some data is assigned to them.
+
+ """
+ type_ = self.schema.node(node_type)
+ # NOTE: Nodes constructor materializes guids.
+ return _nodes.Nodes(self._backend, self._user, type_, guids)
+
+ def node(self, node_type: URI, guid: URI) -> _nodes.Nodes:
+ """Return node *guid* of type *node_type* as a `bsfs.graph.Nodes` instance.
+
+ Note that the *guids* need not to exist (however, the *node_type* has
+ to be part of the schema). An inexistent guid will be created (using
+ *node_type*) once some data is assigned to them.
+
+ """
+ type_ = self.schema.node(node_type)
+ return _nodes.Nodes(self._backend, self._user, type_, {guid})
+
+## EOF ##