Example 13: SPARQL query forms — AllegroGraph Python client 105.1.0 documentation

# Example 13: SPARQL query forms

SPARQL provides alternatives to the standard SELECT query. This example exercises these alternatives to show how AllegroGraph Server and the Python client handle them.

Let’s connect to the database:

    from franz.openrdf.connect import ag_connect

    conn = ag_connect("python-tutorial", create=True, clear=True)

We’ll need some sample data to illustrate all the query types. Our dataset will contain information about rulers of 17th century England.

    conn.addData("""
        @prefix : <ex://> .
        @prefix xsd: <http://www.w3.org/2001/XMLSchema#> .

        :james_i :reigned_from "1603-03-24"^^xsd:date ;
                 :reigned_to "1625-03-27"^^xsd:date .
        :charles_i :reigned_from "1625-03-27"^^xsd:date ;
                   :reigned_to "1649-01-30"^^xsd:date ;
                   :child_of :james_i .
        :charles_ii :reigned_from "1649-01-30"^^xsd:date ;
                 :reigned_to "1685-02-06"^^xsd:date ;
                 :child_of :charles_i .
        :james_ii :reigned_from "1685-02-06"^^xsd:date ;
                 :reigned_to "1688-12-11"^^xsd:date ;
                 :child_of :charles_i .
        :mary_ii :reigned_from "1689-02-13"^^xsd:date ;
                 :reigned_to "1694-12-28"^^xsd:date ;
                 :child_of :james_ii .
        :william_iii :reigned_from "1672-07-04"^^xsd:date ;
                     :reigned_to "1702-03-08"^^xsd:date .
        :anne :reigned_from "1707-05-01"^^xsd:date ;
              :reigned_to "1714-08-01"^^xsd:date ;
              :child_of :james_ii .
    """)

## `SELECT`

This kind of query returns a sequence of tuples, binding variables to matching elements of a search pattern. `SELECT` queries are created using `prepareTupleQuery()` and return results of type `TupleQueryResult`. Query result can also be serialized in a supported `TupleFormat` - in previous examples we used `output=True` and relied on the default `TupleFormat.TABLE`.

Here’s a sample query which locates all rulers whose grandchildren inherited the crown:

    conn.setNamespace('', 'ex://')
    query = conn.prepareTupleQuery(query="""
        SELECT DISTINCT ?name WHERE {
            ?grandchild :child_of/:child_of ?name .
        } ORDER BY ?name """)

    with query.evaluate() as result:
        for bindings in result:
            print(bindings.getValue('name'))

Two names are returned:

    <ex://charles_i>
    <ex://james_i>

We can also serialize the output instead of processing the result object. This time let us reverse the query and ask for rulers whose grandparents are also in the dataset:

    from franz.openrdf.rio.tupleformat import TupleFormat

    query = conn.prepareTupleQuery(query="""
       SELECT DISTINCT ?name WHERE {
          ?name :child_of/:child_of ?grandparent .
       } ORDER BY ?name """)

    query.evaluate(output=True, output_format=TupleFormat.CSV)

We get four results, serialized as CSV:

    name
    "ex://anne"
    "ex://charles_ii"
    "ex://james_ii"
    "ex://mary_ii"

## `ASK`

The `ASK` query returns a Boolean, depending on whether the triple pattern matched any triples. Queries of this type are created using `prepareBooleanQuery()`.

Let’s check if there were any co-regencies in the time period described by our dataset:

    query = conn.prepareBooleanQuery(query="""

        ASK { ?ruler1 :reigned_from ?r1from ;
                      :reigned_to ?r1to .
              ?ruler2 :reigned_from ?r2from ;
                      :reigned_to ?r2to .
              FILTER (?ruler1 != ?ruler2 &&
                      ?r1from >= ?r2from &&
                      ?r1from < ?r2to)
        }""")

    print(query.evaluate())

There was one (William and Mary):

    True

## `CONSTRUCT`

The `CONSTRUCT` query creates triples by substantiating provided templates with values resulting from matching a pattern. Queries of this kind are created using `prepareGraphQuery()` and return a `RepositoryResult` - which is an iterator over the constructed triples.

Note

Executing a `CONSTRUCT` query will not add any triples to the store. To insert the data we have to iterate over the result and add each triple using `addStatement()` (or use an `INSERT` query).

Let us consider a query that calculates a `:sibling_of` relationship:

    print('Size before: {0}'.format(conn.size()))
    query = conn.prepareGraphQuery(query="""
       CONSTRUCT {
           ?person1 :sibling_of ?person2 .
       } WHERE {
           ?person1 :child_of ?parent .
           ?person2 :child_of ?parent .
           filter (?person1 != ?person2) .
       } ORDER BY ?person1""")
    for stmt in query.evaluate():
        print('{0} <-> {1}'.format(stmt.getSubject(),
                                   stmt.getObject()))
    print('Size after: {0}'.format(conn.size()))

The returned object is an iterator over `Statement` objects. We can also see that no data has been added to the repository.

    Size before: 19
    <ex://anne> <-> <ex://mary_ii>
    <ex://charles_ii> <-> <ex://james_ii>
    <ex://james_ii> <-> <ex://charles_ii>
    <ex://mary_ii> <-> <ex://anne>
    Size after: 19

We can also serialize the result using any of the supported `RDFFormats`:

    from franz.openrdf.rio.rdfformat import RDFFormat

    query.evaluate(output=True,
                   output_format=RDFFormat.NTRIPLES)

Here we use the N-Triples format. This happens to be the default, so we could have omitted the `output_format` argument.

    <ex://anne> <ex://sibling_of> <ex://mary_ii> .
    <ex://charles_ii> <ex://sibling_of> <ex://james_ii> .
    <ex://james_ii> <ex://sibling_of> <ex://charles_ii> .
    <ex://mary_ii> <ex://sibling_of> <ex://anne> .

## `DESCRIBE`

The `DESCRIBE` query returns triples that ‘describe’ a given set of resources. Such queries are created using `prepareGraphQuery()` and return `RepositoryResult` objects.

The set of resources to be processed is specified by a query pattern. The SPARQL standard does not say what triples constitute a ‘description’ of a particular resource. AllegroGraph will return the Concise Bounded Description of the queried resources.

Let’s use a `DESCRIBE` query to see what data do we have regarding the children of Charles I:

    query = conn.prepareGraphQuery(query="""
        DESCRIBE ?child WHERE {
            ?child :child_of :charles_i
        }""")
    for stmt in query.evaluate():
        print(stmt)

In this case AllegroGraph will simply return all triples with subject in the specified set:

    (<ex://charles_ii>, <ex://reigned_from>, "1649-01-30"^^<http://www.w3.org/2001/XMLSchema#date>)
    (<ex://charles_ii>, <ex://reigned_to>, "1685-02-06"^^<http://www.w3.org/2001/XMLSchema#date>)
    (<ex://charles_ii>, <ex://child_of>, <ex://charles_i>)
    (<ex://james_ii>, <ex://reigned_from>, "1685-02-06"^^<http://www.w3.org/2001/XMLSchema#date>)
    (<ex://james_ii>, <ex://reigned_to>, "1688-12-11"^^<http://www.w3.org/2001/XMLSchema#date>)
    (<ex://james_ii>, <ex://child_of>, <ex://charles_i>)

DESCRIBE queries can be useful for exploring a dataset and learning what properties a certain object might have. The results of such queries can be serialized to any supported `RDFFormat`:

    query.evaluate(output=True,
                   output_format=RDFFormat.NTRIPLES)

    <ex://charles_ii> <ex://reigned_from> "1649-01-30"^^<http://www.w3.org/2001/XMLSchema#date> .
    <ex://charles_ii> <ex://reigned_to> "1685-02-06"^^<http://www.w3.org/2001/XMLSchema#date> .
    <ex://charles_ii> <ex://child_of> <ex://charles_i> .
    <ex://james_ii> <ex://reigned_from> "1685-02-06"^^<http://www.w3.org/2001/XMLSchema#date> .
    <ex://james_ii> <ex://reigned_to> "1688-12-11"^^<http://www.w3.org/2001/XMLSchema#date> .
    <ex://james_ii> <ex://child_of> <ex://charles_i> .