Warning: This document is for an old version of RDFox Docs. The latest version is 4.0.

3. Getting Started

This page will guide you through running RDFox for the first time.

3.1. Setup

Before you begin, make sure that you have downloaded the latest RDFox release for your system from www.oxfordsemantic.tech/downloads and unpacked it into a directory of your choosing. The following instructions refer to the directory as <working_directory>. You will also need to ensure that RDFox can find a valid license key. The quickest way to do this is to add a copy of your license key file directly to <working_directory> (see Section 2.4.3 for other ways). If you do not have a license key file, you can request one at www.oxfordsemantic.tech/request-eval.

3.2. Getting Started with the Shell

3.2.1. Creating a Data Store

In a terminal, change to <working_directory> and then launch the RDFox shell as follows:

./RDFox sandbox
RDFox.exe sandbox

You should be presented with a command prompt where you can run any of the commands described in Section 10.1.

All facts and rules in RDFox live inside a data store. Initially no data stores exist so we must create one before we can load any triples. To create a data store we use the dstore command as follows:

dstore create family par-complex-nn

The above command initializes our family data store as a parallel store with a complex indexing strategy. See Section 6.2.1 for the other available data store types.

Next, to ensure that subsequent shell commands address our new data store, we use the active command to set the family data store active within the shell:

active family

3.2.2. Importing Data

Load the following small RDF graph which is in Turtle format.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
@prefix : <https://oxfordsemantic.tech/RDFox/getting-started/> .

:peter :forename "Peter" ;
    a :Person ;
    :marriedTo :lois ;
    :gender "male" .

:lois :forename "Lois" ;
    a :Person ;
    :gender "female" .

:meg :forename "Meg" ;
    a :Person ;
    :hasParent :lois, :peter ;
    :gender "female" .

:chris :forename "Chris" ;
    a :Person ;
    :hasParent :peter ;
    :gender "male" .

:stewie :forename "Stewie" ;
    a :Person ;
    :hasParent :lois ;
    :gender "male" .

:brian :forename "Brian" . # Brian is a dog

Download data.ttl into <working_directory> and then import it into the active data store with:

import data.ttl

This command should report the time taken by the import and that 21 data items have been loaded from the file.

3.2.3. Running Queries

By default, RDFox does not specify where query answers must be sent. To instruct RDFox to print answers to the terminal, run the following command:

set output out

Define the : prefix in the shell, to simplify several of the subsequent commands, with:

PREFIX : <https://oxfordsemantic.tech/RDFox/getting-started/>

You are now ready to run your first query!

The primary query language recognized by RDFox is SPARQL. SPARQL queries can be typed or pasted directly into the shell.

Copy and paste the following SPARQL into the shell and hit enter to print all of the triples in the store:

SELECT ?s ?p ?o WHERE { ?s ?p ?o }

You should see a few lines beginning @prefix followed by a blank line and then the original 21 triples (facts) from data.ttl. After the triples, a summary of the number of answers returned and the time taken for the query is printed.

Note that the Turtle snippet is in a slightly different format to the answers, so the answers do not exactly match the file contents. Taking time now to satisfy yourself that the query answers really do contain the same information as data.ttl will be a good way to gain familiarity with the Turtle syntax. In addition, it helps to know that a single a character in the predicate position of a triple represents http://www.w3.org/1999/02/22-rdf-syntax-ns#type which denotes an entity’s type and will often appear as rdf:type.

To demonstrate a simple conjunction, print each person’s forename:

SELECT ?p ?n WHERE { ?p rdf:type :Person . ?p :forename ?n }

Note that :brian is not returned as this entity is not of type :Person as required by the first part of the where clause.

3.2.4. Inserting Data Using SPARQL

It is also possible to modify the contents of the data store via queries. For example, make the :marriedTo relationship symmetric:

INSERT { ?x :marriedTo ?y } WHERE { ?y :marriedTo ?x }

This adds one new triple to the data store reflecting the fact that Lois is married to Peter, which was derived from the fact that Peter is married to Lois. Running the query from step 3 again should now return 22 triples (use the up arrow key to step back through your command history).

3.2.5. Adding Rules

Reasoning is the ability to calculate the logical consequences of applying a set of rules to a set of facts. To illustrate how this can be useful, consider the calculation of the number of children for each parent. The above data has the opposite information - i.e. only :hasParent relationships - so querying for :hasChild, though more natural for our use case, will turn up no results. We could remodel the data but this could make the phrasing of any queries about parents less natural. We could add all the :hasChild relationships as new facts in the Turtle file but the dataset would then have a bunch of redundancy and we would need to make sure that we always add both relationships together when we add new people to the dataset.

With reasoning we can have the best of both worlds. Keeping the original dataset, we add a rule to state that if ?c has a parent ?p then ?p has a child ?c. RDFox can then determine all of the :hasChild relationships itself - including for any new families that we add later on.

RDFox uses the Datalog language for expressing rules. The above rule can be added with the following command:

import ! [?p, :hasChild, ?c] :- [?c, :hasParent, ?p] .

To check that we now have some triples with :hasChild as the predicate, evaluate the following query:

SELECT ?p ?c WHERE { ?p :hasChild ?c }

Four results are returned, all of which have all been added by RDFox by evaluating the rule defined above.

3.2.6. Deleting Facts

To prove that the rule really is ‘live’, delete the triple that says :stewie has :lois as a parent and check that the corresponding :hasChild relationship goes away too. Run:

DELETE { :stewie :hasParent :lois } WHERE { :stewie :hasParent :lois }

Now re-run the query from step 2 and note that the answer :lois :stewie . no longer appears.

3.2.7. Stopping and Re-starting RDFox

Shut down RDFox by typing the quit command. Since RDFox is an in-memory database, and because we started the process using the sandbox command, which disables any form of persistence, the contents of the data store will be dropped when the process exits. While experimenting with RDFox, it may therefore be useful to write the commands to initialize the data store and load data into a script which can be passed to RDFox at startup. The script at the bottom of this page repeats the whole of this tutorial, including starting the endpoint. Save it to a new file <working_directory>/start.txt and then run it with ./RDFox sandbox . start.txt (on Linux or Mac) or RDFox.exe sandbox . start.txt (on Windows). Finally, check that it has worked by re-running the query using the web-based console and verifying that the results have not changed.

dstore create family par-complex-nn
active family
import data.ttl
set output out
prefix : <https://oxfordsemantic.tech/RDFox/getting-started/>
SELECT ?s ?p ?o WHERE { ?s ?p ?o }
SELECT ?p ?n WHERE { ?p rdf:type :Person . ?p :forename ?n }
INSERT { ?x :marriedTo ?y } WHERE { ?y :marriedTo ?x }
import ! [?p, :hasChild, ?c] :- [?c, :hasParent, ?p] .
SELECT ?p ?c WHERE { ?p :hasChild ?c }
DELETE { :stewie :hasParent :lois } WHERE { :stewie :hasParent :lois }

For alternative ways of starting RDFox which do not disable persistence, see Section 10.

3.3. Getting Started with the REST API

For remote use, RDFox exposes a REST API which includes a SPARQL-over-HTTP endpoint. We next show how to use the REST API to achieve the same results as we did using the shell in the previous section.

The step-by-step examples use the cURL tool which is normally installed on MacOS, Linux and modern versions of Windows. At the end of this section, we give an example of achieving the same results using Python.

3.3.1. Starting the RDFox Endpoint

Open a terminal or command prompt window and start RDFox with the following command line:

./RDFox sandbox . "set endpoint.port 8080" "endpoint start"
RDFox.exe sandbox . "set endpoint.port 8080" "endpoint start"

You should see the RDFox startup text followed by a line showing the endpoint has been started:

The REST endpoint was successfully started at port number/service name 8080 with 7 threads.

This document assumes the REST endpoint is running on port 8080. A different port can be used by changing the number used in the set endpoint.port command.

3.3.2. Creating a Data Store

To perform the equivalent of the RDFox shell commands active family followed by init par-complex-nn we issue a POST request on the datastore/family path specifying the type of data store in the type query parameter.

curl -i -X POST "localhost:8080/datastores/family?type=par-complex-nn"

Note we run cURL with the -i option to include the response headers in the output which means the output will be similar to:

HTTP/1.1 201 Created
Date: Fri, 12 Jul 2019 12:13:32 GMT
Server: RDFox Endpoint
Location: /datastores/family
Content-Length: 0

We can confirm this data store exists by listing the data stores using a GET request on the datastores path:

curl -i -X GET localhost:8080/datastores

The results will be:

HTTP/1.1 200 OK
Date: Fri, 12 Jul 2019 12:15:04 GMT
Server: RDFox Endpoint
Content-Type: text/tab-separated-values; charset=UTF-8
Transfer-Encoding: chunked

?Name
"family"

3.3.3. Loading Data

To load data, we perform the equivalent of the import RDFox shell command shown in Section 3.2.2 by issuing a POST on the datastores/family/content path.

There is no mechanism to import a file that is located on the server, instead the data content must be sent in the body of the request. We can achieve this with cURL by creating a file and using the -data command line option to read this file and send it to RDFox.

Using the same data file data.ttl as before, issue the cURL command:

curl -i -X POST localhost:8080/datastores/family/content -H "Content-Type:" -d @data.ttl

This sends the turtle data to RDFox and reports success with:

HTTP/1.1 200 OK
Date: Fri, 12 Jul 2019 13:16:11 GMT
Server: RDFox Endpoint
Content-Type: text/plain; charset=UTF-8
Transfer-Encoding: chunked

prefix: : = https://oxfordsemantic.tech/RDFox/getting-started/
information: #facts = 21

Note the use of -H "Content-Type:" in the cURL command. By default, cURL will set the content type to application/x-www-form-urlencoded which is a not a supported RDFox Content-Type. Using -H "Content-Type:" instructs cURL not to set the Content-Type and allows RDFox to automatically detect that we have data in Turtle format. Alternatively, we could explicitly set the type using -H "Content-Type: text/turtle". For the different formats supported by RDFox, see Section 8.9.

3.3.4. Listing Triples

To list the triples in the data store, we send a GET request to the content path and use the Accept request header to obtain the results in Turtle.

curl -i -X GET localhost:8080/datastores/family/content -H "Accept: text/turtle"

which returns:

HTTP/1.1 200 OK
Date: Fri, 12 Jul 2019 13:16:41 GMT
Server: RDFox Endpoint
Content-Type: text/turtle; charset=UTF-8
Transfer-Encoding: chunked

@prefix a1: <https://oxfordsemantic.tech/RDFox/getting-started/> .
@prefix owl: <http://www.w3.org/2002/07/owl#> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix ruleml: <http://www.w3.org/2003/11/ruleml#> .
@prefix swrl: <http://www.w3.org/2003/11/swrl#> .
@prefix swrlb: <http://www.w3.org/2003/11/swrlb#> .
@prefix swrlx: <http://www.w3.org/2003/11/swrlx#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .

a1:peter a1:gender "male" ;
    a1:marriedTo a1:lois ;
    rdf:type a1:Person ;
    a1:forename "Peter" .

a1:lois a1:gender "female" ;
    rdf:type a1:Person ;
    a1:forename "Lois" .

a1:meg a1:gender "female" ;
    a1:hasParent a1:lois , a1:peter ;
    rdf:type a1:Person ;
    a1:forename "Meg" .

a1:chris a1:gender "male" ;
    a1:hasParent a1:peter ;
    rdf:type a1:Person ;
    a1:forename "Chris" .

a1:stewie a1:gender "male" ;
    a1:hasParent a1:lois ;
    rdf:type a1:Person ;
    a1:forename "Stewie" .

a1:brian a1:forename "Brian" .

Alternatively we could list the triples in N-Triples format with:

curl -i -X GET localhost:8080/datastores/family/content -H "Accept: application/n-triples"

3.3.5. Running Queries

To run a query we send a POST request to the sparql path and set the query parameter to a SPARQL query. To run the query SELECT ?p ?n WHERE { ?p a :Person . ?p :forename ?n } we run the command:

curl -i -X POST localhost:8080/datastores/family/sparql -d "query=PREFIX : <https://oxfordsemantic.tech/RDFox/getting-started/>  SELECT ?p ?n WHERE { ?p a :Person . ?p :forename ?n }"

This returns the result in the default format of tab-separated-values.

HTTP/1.1 200 OK
Date: Mon, 22 Jul 2019 16:05:56 GMT
Server: RDFox Endpoint
Content-Type: text/tab-separated-values; charset=UTF-8
Transfer-Encoding: chunked

?p      ?n
<https://oxfordsemantic.tech/RDFox/getting-started/peter>       "Peter"
<https://oxfordsemantic.tech/RDFox/getting-started/stewie>      "Stewie"
<https://oxfordsemantic.tech/RDFox/getting-started/chris>       "Chris"
<https://oxfordsemantic.tech/RDFox/getting-started/meg> "Meg"
<https://oxfordsemantic.tech/RDFox/getting-started/lois>        "Lois"

To view the results in SPARQL 1.1 JSON Format set the Accept header to application/sparql-results+json:

curl -i -X POST -H "Accept: application/sparql-results+json" localhost:8080/datastores/family/sparql -d "query=PREFIX : <https://oxfordsemantic.tech/RDFox/getting-started/>  SELECT ?p ?n WHERE { ?p a :Person . ?p :forename ?n }"

Notice the use of query=... in the post data. This is necessary because cURL will URL-encode post data by default and the SPARQL standard requires the query parameter is used when issuing a SPARQL query via POST with URL-encoded parameters Alternatively it is possible to issue a query via POST directly by setting the content type to application/sparql-query as shown below:

curl -i -X POST localhost:8080/datastores/family/sparql -H "Content-Type: application/sparql-query"  -d "PREFIX : <https://oxfordsemantic.tech/RDFox/getting-started/>  SELECT ?p ?n WHERE { ?p a :Person . ?p :forename ?n }"

3.3.6. Inserting Data Using SPARQL

To perform a SPARQL insert the update parameter should used:

curl -i -X POST localhost:8080/datastores/family/sparql -d "update=PREFIX : <https://oxfordsemantic.tech/RDFox/getting-started/> INSERT { ?x :marriedTo ?y } WHERE { ?y :marriedTo ?x }"

this returns the no content (204) status code to indicate success:

HTTP/1.1 204 No Content
Date: Mon, 22 Jul 2019 16:06:47 GMT
Server: RDFox Endpoint

3.3.7. Adding Rules

To add a Datalog rule we issue a POST request to the content path and send the rule text in the body of the request:

curl -i -X POST localhost:8080/datastores/family/content -H "Content-Type: application/x.datalog" --data-raw "@PREFIX : <https://oxfordsemantic.tech/RDFox/getting-started/> . [?p, :hasChild, ?c] :- [?c, :hasParent, ?p] ."

this returns the OK status code (200) to indicate success:

HTTP/1.1 200 OK
Date: Mon, 22 Jul 2019 16:07:49 GMT
Server: RDFox Endpoint
Content-Type: text/plain; charset=UTF-8
Transfer-Encoding: chunked

prefix: : = https://oxfordsemantic.tech/RDFox/getting-started/
information: #rules = 1
information: #rule-updates = 1

Note the use of the --data-raw option in the above example, this is because normally @ means insert the contents of a file in cURL, using --data-raw allows @ symbols to be sent as is.

To confirm this rule has added new facts issue a SELECT ?p ?c WHERE { ?p :hasChild ?c } query with:

curl -i -X GET -H "Accept: application/x.sparql-results+turtle" localhost:8080/datastores/family/sparql -d "query=PREFIX : <https://oxfordsemantic.tech/RDFox/getting-started/> SELECT ?p ?c WHERE { ?p :hasChild ?c }"

this returns an OK status code (200) and the results of the query in Turtle format:

HTTP/1.1 200 OK
Date: Mon, 22 Jul 2019 16:08:48 GMT
Server: RDFox Endpoint
Content-Type: application/x.sparql-results+turtle; charset=UTF-8
Transfer-Encoding: chunked

<https://oxfordsemantic.tech/RDFox/getting-started/lois> <https://oxfordsemantic.tech/RDFox/getting-started/meg> .
<https://oxfordsemantic.tech/RDFox/getting-started/peter> <https://oxfordsemantic.tech/RDFox/getting-started/meg> .
<https://oxfordsemantic.tech/RDFox/getting-started/peter> <https://oxfordsemantic.tech/RDFox/getting-started/chris> .
<https://oxfordsemantic.tech/RDFox/getting-started/lois> <https://oxfordsemantic.tech/RDFox/getting-started/stewie> .

3.3.8. Deleting Facts

To prove the :hasChild rule is live, delete the fact [:stewie :hasParent :lois]. We achieve this by sending a PATCH request to the content path and setting the mode parameter to delete:

curl -i -X PATCH -H "Content-Type: application/x.datalog" "localhost:8080/datastores/family/content?mode=delete" --data-raw "@PREFIX : <https://oxfordsemantic.tech/RDFox/getting-started/> . [:stewie, :hasParent, :lois] ."

When the SELECT ?p ?c WHERE { ?p :hasChild ?c } query is rerun with:

curl -i -X GET -H "Accept: application/x.sparql-results+turtle" localhost:8080/datastores/family/sparql -d "query=PREFIX : <https://oxfordsemantic.tech/RDFox/getting-started/> SELECT ?p ?c WHERE { ?p :hasChild ?c }"

we see that the result :lois, :stewie is no longer present:

HTTP/1.1 200 OK
date: Wed, 24 Jul 2019 16:59:38 GMT
server: RDFox Endpoint
content-type: application/x.sparql-results+turtle; charset=UTF-8
transfer-encoding: chunked

<https://oxfordsemantic.tech/RDFox/getting-started/lois> <https://oxfordsemantic.tech/RDFox/getting-started/meg> .
<https://oxfordsemantic.tech/RDFox/getting-started/peter> <https://oxfordsemantic.tech/RDFox/getting-started/meg> .
<https://oxfordsemantic.tech/RDFox/getting-started/peter> <https://oxfordsemantic.tech/RDFox/getting-started/chris> .

3.3.9. Python example

The following Python script uses the requests module to reproduce the same steps as used in the cURL examples presented previous.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
import requests

# helper function to raise exception if the REST endpoint returns an
# unexpected status code
def assert_reponse_ok(response, message):
    if not response.ok:
        raise Exception(
            message + "\nStatus received={}\n{}".format(response.status_code, response.text))

rdfox_server = "http://localhost:8080"

# Create the datastore
response = requests.post(
    rdfox_server + "/datastores/family", params={'type': 'par-complex-nn'})
assert_reponse_ok(response, "Failed to create datastore.")

# List datastores
response = requests.get(rdfox_server + "/datastores")
assert_reponse_ok(response, "Failed to obtain list of datastores")
print("== Datastore list ==")
print(response.text)

# Add facts
turtle_data = """
@prefix : <https://oxfordsemantic.tech/RDFox/getting-started/> .

:peter :forename "Peter" ;
    a :Person ;
    :marriedTo :lois ;
    :gender "male" .

:lois :forename "Lois" ;
    a :Person ;
    :gender "female" .

:meg :forename "Meg" ;
    a :Person ;
    :hasParent :lois, :peter ;
    :gender "female" .

:chris :forename "Chris" ;
    a :Person ;
    :hasParent :peter ;
    :gender "male" .

:stewie :forename "Stewie" ;
    a :Person ;
    :hasParent :lois ;
    :gender "male" .

:brian :forename "Brian" . # Brian is a dog
"""

response = requests.post(
    rdfox_server + "/datastores/family/content", data=turtle_data)
assert_reponse_ok(response, "Failed to add facts to datastore.")

# List facts
response = requests.get(
    rdfox_server + "/datastores/family/content", headers={"Accept": "text/turtle"})
assert_reponse_ok(response, "Failed to obtain facts from datastore.")
print("== Facts imported ==")
print(response.text)

# Issue select query
sparql_text = "PREFIX : <https://oxfordsemantic.tech/RDFox/getting-started/> " \
    "SELECT ?p ?n WHERE { ?p a :Person . ?p :forename ?n }"
response = requests.get(
    rdfox_server + "/datastores/family/sparql", params={"query": sparql_text})
assert_reponse_ok(response, "Failed to run select query.")
print("== Initial query result ==")
print(response.text)

# Issue insert
sparql_insert = "PREFIX : <https://oxfordsemantic.tech/RDFox/getting-started/> " \
    "INSERT { ?x :marriedTo ?y } WHERE { ?y :marriedTo ?x }"
response = requests.post(
    rdfox_server + "/datastores/family/sparql", data={"update": sparql_insert})
assert_reponse_ok(response, "Failed to insert fact via sparql.")

# Add rule
datalog_rule = "@PREFIX : <https://oxfordsemantic.tech/RDFox/getting-started/> . " \
    "[?p, :hasChild, ?c] :- [?c, :hasParent, ?p] ."
response = requests.post(
    rdfox_server + "/datastores/family/content", data=datalog_rule)
assert_reponse_ok(response, "Failed to add rule.")

# Check rule exists by listing rules.
response = requests.get(rdfox_server + "/datastores/family/content",
                        headers={"Content-Type": "application/x.datalog"})
assert_reponse_ok(response, "Failed to list rules.")
print("== Rules listing (after adding rule) ==")
print(response.text)

# Query to confirm rule
sparql_text = "PREFIX : <https://oxfordsemantic.tech/RDFox/getting-started/> " \
    "SELECT ?p ?c WHERE { ?p :hasChild ?c }"
response = requests.get(
    rdfox_server + "/datastores/family/sparql", params={"query": sparql_text})
assert_reponse_ok(response, "Failed to run select query.")
print("== Query for derived facts ==")
print(response.text)

# Delete fact
datalog_text = "@PREFIX : <https://oxfordsemantic.tech/RDFox/getting-started/> . " \
    "[:stewie, :hasParent, :lois] ."
response = requests.patch(
    rdfox_server + "/datastores/family/content", params={"mode": "delete"}, data=datalog_text)
assert_reponse_ok(response, "Failed to delete fact.")

# Query to confirm derived facts updated (stewie is no longer a child of lois)
sparql_text = "PREFIX : <https://oxfordsemantic.tech/RDFox/getting-started/> " \
    "SELECT ?p ?c WHERE { ?p :hasChild ?c }"
response = requests.get(
    rdfox_server + "/datastores/family/sparql", params={"query": sparql_text})
assert_reponse_ok(response, "Failed to run select query.")
print("== Query for updated derived facts ==")
print(response.text)

# delete rule
datalog_rule = "@PREFIX : <https://oxfordsemantic.tech/RDFox/getting-started/> . " \
    "[?p, :hasChild, ?c] :- [?c, :hasParent, ?p] ."
response = requests.patch(
    rdfox_server + "/datastores/family/content", params={"mode": "delete"}, data=datalog_rule)
assert_reponse_ok(response, "Failed to delete rule.")

# list rules after deletion
response = requests.get(rdfox_server + "/datastores/family/content",
                        headers={"Accept": "application/x.datalog"})
assert_reponse_ok(response, "Failed to list rules.")
print("== Rules listing (after deleting rule) ==")
print(response.text)

Download: pythonRest.py.

3.4. Getting Started with the Web Console

As described in Section 3.3, RDFox exposes a REST API which includes a SPARQL over HTTP endpoint. The endpoint can be started with:

endpoint start

which will print the port number it is listening on. The endpoint also serves a web-based console application that itself uses the REST API.

At the moment, the console serves as a query interface for RDFox. To use it, we first need to create the relevant data stores and import data and rules. We can do this using the shell as we did before.

dstore create family par-complex-nn
active family
import data.ttl
import ! [?p, :hasChild, ?c] :- [?c, :hasParent, ?p] .

To use the console, we can now run the following steps:

  1. In a web browser, navigate to http://localhost:<port>/console, taking care to substitute the correct value for <port>.

  2. Select the family data store to enable the SPARQL query editor.

  3. In the query editor pane, replace the default query with:

    PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
    PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
    PREFIX : <https://oxfordsemantic.tech/RDFox/getting-started/>
    SELECT ?parent (count(distinct ?child) as ?childCount)
    WHERE {
      ?parent :hasChild ?child
    }
    GROUP BY ?parent
    
  4. Click ‘Run query’ to make the console run the query over REST and display the answers. You should see:

parent

childCount

1

:lois

1

2

:peter

2

Note that, at present, the web-based console is an experimental feature.