Perform knowledge graph operations
Spin up docker containers¶
Spinning up docker containers requires knowledge about docker, please refer to Setting up Docker and Docker SDK for Python if you do not have relevant experience.
Please refer to Blazegraph and Fileserver for technical details, brief example of how to use them are provided below.
Blazegraph¶
Among many available triple stores, Blazegraph is commonly used within TheWorldAvatar
and we have provided a convenient docker image ghcr.io/cambridge-cares/blazegraph:1.1.0
for users to deploy.
To spin up a Blazegraph docker container locally using Python:
import docker
# Connect to Docker using the default socket or the configuration in your environment:
client = docker.from_env()
# Run Blazegraph container
# It returns a Container object that we will need later for stopping it
blazegraph = client.containers.run(
'ghcr.io/cambridge-cares/blazegraph:1.1.0',
ports={'8080/tcp': 9999}, # this binds the internal port 8080/tcp to the external port 9999
detach=True # this runs the container in the background
)
The Blazegraph should now be accessible at the below endpoint for both query and update:
SPARQL query and update endpoint might differ for other triple stores, e.g. RDF4J.
# This is the default namespace `kb`
# Note the port we are accessing is 9999 as specified when spinning it up
sparql_endpoint = 'http://localhost:9999/blazegraph/namespace/kb/sparql'
To stop the blazegraph docker container after all operations:
Fileserver¶
Similarly, we have provided a convenient docker image of fileserver ghcr.io/cambridge-cares/fileserver:1.1.0
for users to deploy.
To spin up a Fileserver docker container locally using Python:
import docker
# Connect to Docker using the default socket or the configuration in your environment:
client = docker.from_env()
# Run Fileserver container
# It returns a Container object that we will need later for stopping it
fileserver = client.containers.run(
'ghcr.io/cambridge-cares/fileserver:1.1.0',
ports={'8080/tcp': 9998}, # this binds the internal port 8080/tcp to the external port 9998
detach=True # this runs the container in the background
)
The Fileserver should now be accessible at the below endpoint for upload and download:
# Note the port we are accessing is 9998 as specified when spinning it up
fs_url = 'http://localhost:9998/FileServer/'
# The fileserver image comes with a default username and password
fs_user = 'fs_user'
fs_password = 'fs_pass'
For how to interact with Fileserver, please see examples in the section Interact with fileserver below.
To stop the fileserver docker container after all operations:
Use docker-compose.yml
¶
If you wish to use docker-compose.yml
instead, you may create the below yml file and compose it up:
NOTE please remember to create the file
./secrets/blazegraph_password.txt
and./secrets/fileserver_password.txt
with your desired password populated.NOTE the default username of blazegraph and fileserver are
bg_user
andfs_user
respectively.
version: "3.8"
services:
# Blazegraph
blazegraph:
image: ghcr.io/cambridge-cares/blazegraph:1.1.0
container_name: "blazegraph"
ports:
- 9999:8080
environment:
BLAZEGRAPH_PASSWORD_FILE: /run/secrets/blazegraph_password
# Add a secret to set the password for BASIC authentication
secrets:
- blazegraph_password
# File server
fileserver:
image: ghcr.io/cambridge-cares/fileserver:1.1.0
container_name: "fileserver"
ports:
- 9998:8080
# Add secret to set BASIC authentication password
secrets:
- file_server_password
# Secrets used to set runtime passwords
secrets:
blazegraph_password:
file: ./secrets/blazegraph_password.txt
file_server_password:
file: ./secrets/fileserver_password.txt
For more details on how to use
docker compose
, plese refer to Docker Compose overview.
Instantiation of the PySparqlClient
¶
To initialise a SPARQL client with the above sparql_endpoint
and fs_url
with the basic authentication:
NOTE remember to populate the passwords
<your_blazegraph_password>
and<your_fileserver_password>
based on those provided in your secret files.
from twa.kg_operations import PySparqlClient
# sparql_endpoint = 'http://localhost:9999/blazegraph/namespace/kb/sparql'
# fs_url = 'http://localhost:9998/FileServer/'
sparql_client = PySparqlClient(
query_endpoint = sparql_endpoint,
update_endpoint = sparql_endpoint,
kg_user = 'bg_user',
kg_password = '<your_blazegraph_password>',
fs_url = fs_url,
fs_user = 'fs_user',
fs_pwd = '<your_fileserver_password>'
)
We can swap sparql_endpoint
with URL of any other SPARQL endpoint what one wish to query/update.
SPARQL query and update¶
There are a few convenient functions provided in PySparqlClient
that we can use, e.g. to get the total amount of triples in the SPARQL endpoint:
To perform custom update and query:
# Update the triple store by inserting data
sparql_client.perform_update(
'INSERT DATA {<https://s> <https://p> <https://o>.}'
)
# Query the first 10 triples in the triple store
triples = sparql_client.perform_query(
'SELECT * WHERE {?s ?p ?o} LIMIT 10'
)
For tutorials of SPARQL operations, please refer to the official W3C documentation. Some useful tips and tricks can be found at SPARQL tips and tricks.
Use rdflib.Graph
object¶
Python package rdflib
is a convenient tool to work with RDF data. It has rdflib.Graph
object which can be used as a knowledge graph in memory to temporarily host data before uploading it to a triple store. It is more pythonic
compared to writing out the triples manually when constructing the SPARQL insert clause.
To add triples to rdflib.Graph
object and upload it to a triple store:
from rdflib import Graph, URIRef, Literal
from rdflib.namespace import FOAF, RDF
# Instantiate a rdflib.Graph object
g = Graph()
# Create dummy data
bob = URIRef("http://example.org/people/Bob")
name = Literal("Bob")
age = Literal(24)
# Add data to rdflib.Graph object
g.add((bob, RDF.type, FOAF.Person))
g.add((bob, FOAF.name, name))
g.add((bob, FOAF.age, age))
# Upload the triples to triple store
sparql_client.upload_graph(g)
For more tutorials on how to use
rdflib
, please refer to rdflib 7.0.0.
Upload TBox from a given URL to a triple store¶
In some use cases, in addition to data in ABox, we may want to upload the TBox to the same triple store to perform reasoning. We can again make use of rdflib.Graph
:
# Here we take OntoDoE as an example
tbox_url = 'https://raw.githubusercontent.com/cambridge-cares/TheWorldAvatar/main/JPS_Ontology/ontology/ontodoe/OntoDoE.owl'
# Parse the TBox to rdflib.Graph object
tbox_g = Graph()
tbox_g.parse(tbox_url, format='xml')
# Upload it to triple store
sparql_client.upload_graph(tbox_g)
Interact with fileserver¶
For use cases involving processing raw files, e.g. reports from HPLC jobs, one can use the below to upload/download files to/from the fileserver:
Authentication is required to interact with fileserver docker container, the default username is
fs_user
and the default password isfs_pass
.
# Upload local file to remote fileserver
local_file_path = '<local_file_path>'
remote_file_path, timestamp_upload = sparql_client.upload_file(local_file_path)
# Download remote file to local machine with user-specified file path
downloaded_file_path = '<downloaded_file_path>'
sparql_client.download_file(remote_file_path, downloaded_file_path)
NOTE this function does NOT instantiate relevant triples of the uploaded files in the triple store. A feature request exist for this which will be addressed in the next release. Please see here for a preliminary example attempted in another project.
For all available operations over Fileserver, please visit its technical details.
Create custom sparql client¶
Depends on use cases, users may want to define custom SPARQL queries/updates that can be re-used across different Python modules. One way to do this could be inheriting the PySparqlClient
class:
# Define custom sparql client class
class OntoKinSparqlClient(PySparqlClient):
def get_reaction_mechanisms(self, limit: int = 10):
query = f"""
PREFIX ontokin: <http://www.theworldavatar.com/ontology/ontokin/OntoKin.owl#>
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> SELECT ?mechanismIRI
WHERE {{ ?mechanismIRI rdf:type ontokin:ReactionMechanism . }} LIMIT {limit}
"""
response = self.perform_query(query)
return [list(res.values())[0] for res in response]
# Initialise custom sparql client in the same way as PySparqlClient
ontokin_sparql_client = OntoKinSparqlClient(sparql_endpoint, sparql_endpoint)
# Perform custom queries
ontokin_sparql_client.get_reaction_mechanisms()