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:
In a web browser, navigate to
http://localhost:<port>/console
, taking care to substitute the correct value for<port>
.Select the
family
data store to enable the SPARQL query editor.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
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.