.. _example13:

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:

.. literalinclude:: doctest_setup.py
   :language: python_rdf
   :start-after: BEGIN-CONNECT
   :end-before: END-CONNECT

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

.. testcode:: example13

   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 :meth:`prepareTupleQuery` and return results of type
:class:`.TupleQueryResult`. Query result can also be serialized in a
supported :class:`.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:

.. testcode:: example13

   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:

.. testoutput:: example13

   <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:

.. testcode:: example13

   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:

.. testoutput:: example13

   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
:meth:`prepareBooleanQuery`.

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

.. testcode:: example13

   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):
   
.. testoutput:: example13

   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 :meth:`prepareGraphQuery` and return a
:class:`.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 :meth:`addStatement` (or use an ``INSERT``
   query).

Let us consider a query that calculates a ``:sibling_of``
relationship:
   
.. testcode:: example13

   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.

.. testoutput:: example13

   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
:class:`RDFFormats <.RDFFormat>`:

.. testcode:: example13

   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.
                  
.. testoutput:: example13

   <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 :meth:`prepareGraphQuery`
and return :class:`.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:

.. testcode:: example13

   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:
       
.. testoutput:: example13
   :options: +SORT
                
   (<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 :class:`.RDFFormat`:

.. testcode:: example13

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

.. testoutput:: example13
   :options: +SORT

   <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> .


.. _Concise Bounded Description: https://www.w3.org/Submission/CBD/