Examples

This document shows some real examples of usage of the TinySPARQL library.

Querying a remote endpoint

All SPARQL queries happen through a TrackerSparqlConnection, often these connections represent a remote endpoints maintained by another process or server.

This example demonstrates the use of these connections on a remote endpoint. Concretely creating a D-Bus TrackerSparqlConnection, creating a prepared statement from a SPARQL query string, executing the query, and obtaining the query results from the cursor.

The tracker_sparql_connection_query_statement() method can be used to obtain a TrackerSparqlStatement object holding a prepared SPARQL query that can then be executed with tracker_sparql_statement_execute(). The query string can contain ~name placeholders which can be replaced with arbitrary values before query execution with tracker_sparql_statement_bind_string() and similar functions. This allows parsing the query string only once and to execute it multiple times with different parameters with potentially significant performance gains.

Multiple functions offer asynchronous variants, so the application main loop is not blocked while these operations are executed.

Once you end up with the query, remember to call tracker_sparql_cursor_close(). The same applies to tracker_sparql_connection_close() when no longer needed.

In C:

#include <tinysparql.h>

#define REMOTE_NAME "org.freedesktop.Tracker3.Miner.Files"

int main (int argc, const char **argv)
{
  g_autoptr (GError) error = NULL;
  g_autoptr (TrackerSparqlConnection) connection = NULL;
  g_autoptr (TrackerSparqlCursor) cursor = NULL;
  g_autoptr (TrackerSparqlStatement) stmt = NULL;
  const char *query = "SELECT nie:url(?u) WHERE { ?u a nfo:FileDataObject ; nfo:fileName ~name }";
  const char *name = NULL;
  int i = 0;

  connection = tracker_sparql_connection_bus_new (REMOTE_NAME, NULL, NULL, &error);
  if (!connection) {
    g_printerr ("Couldn't obtain a connection to the Tracker store: %s",
                error ? error->message : "unknown error");
    return 1;
  }

  /* Create a prepared statement */
  stmt = tracker_sparql_connection_query_statement (connection,
                                                    query,
                                                    NULL,
                                                    &error);

  if (!stmt) {
    g_printerr ("Couldn't create a prepared statement: '%s'",
                error->message);
    return 1;
  }

  /* Bind a value to the query variables */
  name = argv[1] ? argv[1] : "";
  tracker_sparql_statement_bind_string (stmt, "name", name);

  /* Executes the SPARQL query with the currently bound values and get new cursor */
  cursor = tracker_sparql_statement_execute (stmt, NULL, &error);
  if (!cursor) {
    g_printerr ("Couldn't execute query: '%s'",
                error->message);
    return 1;
  }

  /* Iterate synchronously the results */
  while (tracker_sparql_cursor_next (cursor, NULL, &error)) {
    g_print ("Result [%d]: %s\n",
             i++,
             tracker_sparql_cursor_get_string (cursor, 0, NULL));
  }

  g_print ("A total of '%d' results were found\n", i);

  tracker_sparql_cursor_close (cursor);

  tracker_sparql_connection_close (connection);

  return 0;
}

In Python:

#!/usr/bin/python3

import gi, sys
from gi.repository import GLib, Gio, Tsparql

try:
    connection = Tsparql.SparqlConnection.bus_new(
        'org.freedesktop.Tracker3.Miner.Files',
        None, None)

    stmt = connection.query_statement (
        'SELECT DISTINCT nie:url(?u) WHERE { ' +
        '  ?u a nfo:FileDataObject ; ' +
        '     nfo:fileName ~name ' +
        '}', None)

    stmt.bind_string('name', sys.argv[1])

    cursor = stmt.execute()
    i = 0;

    while cursor.next():
        i += 1
        print('Result {0}: {1}'.format(i, cursor.get_string(0)[0]))

    print('A total of {0} results were found\n'.format(i))

    cursor.close()
    connection.close()

except Exception as e:
    print('Error: {0}'.format(e))
    sys.exit(-1)

In Javascript:

#!/usr/bin/gjs

const { GLib, Gio, Tsparql } = imports.gi

try {
    let connection = Tsparql.SparqlConnection.bus_new(
        'org.freedesktop.Tracker3.Miner.Files',
        null, null);

    let stmt = connection.query_statement (
        'SELECT DISTINCT nie:url(?u) WHERE { ' +
        '  ?u a nfo:FileDataObject ; ' +
        '     nfo:fileName ~name ' +
        '}', null);

    stmt.bind_string('name', ARGV[0]);

    let cursor = stmt.execute(null);
    let i = 0;

    while (cursor.next(null)) {
        i++;
        print(`Result ${i}: ${cursor.get_string(0)[0]}`);
    }

    print(`A total of ${i} results were found`);

    cursor.close();
    connection.close();
} catch (e) {
    printerr(`Error: ${e.message}`)
}

Creating a private database

Applications may create private RDF triple stores via the tracker_sparql_connection_new() constructor.

This example demonstrates the creation of a private store, for simplicity the example uses the builtin Nepomuk ontology, but the data structures may be defined by the application, see the documentation on creating custom ontologies for more information about this.

The example also demonstrates the use of TrackerResource and TrackerBatch for insertion of RDF data. It is also possible to use TrackerSparqlStatement for updates through the tracker_batch_add_statement() methods, or plain SPARQL strings through tracker_batch_add_sparql().

Multiple functions offer asynchronous variants, so the application main loop is not blocked while these operations are executed.

Once you no longer need the connection, remember to call tracker_sparql_connection_close().

In C:

#include <tinysparql.h>

int main (int argc, const char **argv)
{
  g_autoptr (GError) error = NULL;
  g_autoptr (TrackerSparqlConnection) connection = NULL;
  g_autoptr (TrackerResource) resource = NULL;
  g_autoptr (GFile) ontology = NULL;

  /* Create a private SPARQL store */
  ontology = tracker_sparql_get_ontology_nepomuk ();
  connection = tracker_sparql_connection_new (TRACKER_SPARQL_CONNECTION_FLAGS_NONE,
                          NULL, /* Database location, NULL creates it in-memory */
                          ontology, /* Ontology location */
                          NULL,
                          &error);
  if (connection) {
    g_printerr ("Couldn't create a Tracker store: %s",
                error->message);
    return 1;
  }

  /* Create a resource containing RDF data */
  resource = tracker_resource_new (NULL);
  tracker_resource_set_uri (resource, "rdf:type", "nmm:MusicPiece");

  /* Create a batch, and add the resource to it */
  batch = tracker_sparql_connection_create_batch (connection);
  tracker_batch_add_resource (batch, NULL, resource);

  /* Execute the batch to insert the data */
  if (!tracker_batch_execute (batch, NULL, &error)) {
    g_printerr ("Couldn't insert batch of resources: %s",
                error->message);
    return 1;
  }

  tracker_sparql_connection_close (connection);

  return 0;
}

In Python:

#!/usr/bin/python3

import gi, sys
from gi.repository import GLib, Gio, Tsparql

try:
    connection = Tsparql.SparqlConnection.new(
        Tsparql.SparqlConnectionFlags.NONE,
        None, # Database location, None creates it in-memory
        Tsparql.sparql_get_ontology_nepomuk(), # Ontology location
        None)

    # Create a resource containing RDF data
    resource = Tsparql.Resource.new(None)
    resource.set_uri('rdf:type', 'nmm:MusicPiece')

    # Create a batch, and add the resource to it
    batch = connection.create_batch()
    batch.add_resource(None, resource)

    # Execute the batch to insert the data
    batch.execute()

    connection.close()

except Exception as e:
    print('Error: {0}'.format(e))
    sys.exit(-1)

In Javascript:

#!/usr/bin/gjs

const { GLib, Gio, Tsparql } = imports.gi

try {
    let connection = Tsparql.SparqlConnection.new(
        Tsparql.SparqlConnectionFlags.NONE,
        null, // Database location, None creates it in-memory
        Tsparql.sparql_get_ontology_nepomuk(), // Ontology location
        null);

    // Create a resource containing RDF data
    let resource = Tsparql.Resource.new(null)
    resource.set_uri('rdf:type', 'nmm:MusicPiece')

    // Create a batch, and add the resource to it
    let batch = connection.create_batch()
    batch.add_resource(null, resource)

    // Execute the batch to insert the data
    batch.execute(null)

    connection.close();
} catch (e) {
    printerr(`Error: ${e.message}`)
}

Creating a SPARQL endpoint

For some applications and services, it might be desirable to export a RDF triple store as an endpoint. Making it possible for other applications to query the data they hold.

This example demonstrates the use of TrackerEndpoint subclasses, concretely the creation of a D-Bus endpoint, that other applications may query e.g. through a connection created with tracker_sparql_connection_bus_new().

In C:

#include <tinysparql.h>

int main (int argc, const char **argv)
{
  g_autoptr (GError) error = NULL;
  g_autoptr (TrackerSparqlConnection) connection = NULL;
  g_autoptr (GFile) ontology = NULL;
  g_autoptr (GMainLoop) main_loop = NULL;

  /* Create a SPARQL store */
  ontology = tracker_sparql_get_ontology_nepomuk ();
  connection = tracker_sparql_connection_new (TRACKER_SPARQL_CONNECTION_FLAGS_NONE,
                          NULL, /* Database location, NULL creates it in-memory */
                          ontology, /* Ontology location */
                          NULL,
                          &error);
  if (connection) {
    g_printerr ("Couldn't create a Tracker store: %s",
                error->message);
    return 1;
  }

  /* Make the connection available via D-Bus, other applications would be
   * able to make connections to this application's bus name
   */
  endpoint = tracker_endpoint_dbus_new (connection, NULL, NULL, NULL, &error);
  if (!endpoint) {
    g_printerr ("Couldn't create D-Bus endpoint: %s",
                error->message);
    return 1;
  }

  main_loop = g_main_loop_new (NULL, FALSE);
  g_main_loop_run (main_loop);

  return 0;
}

In Python:

#!/usr/bin/python3

import gi, sys
from gi.repository import GLib, Gio, Tsparql

try:
    connection = Tsparql.SparqlConnection.new(
        Tsparql.SparqlConnectionFlags.NONE,
        None, # Database location, None creates it in-memory
        Tsparql.sparql_get_ontology_nepomuk(), # Ontology location
        None)

    bus = Gio.bus_get_sync(Gio.BusType.SESSION, None)

    endpoint = Tsparql.EndpointDBus.new(
        connection, bus, None, None)

    loop = GLib.MainLoop.new(None, False)
    loop.run()

    connection.close()

except Exception as e:
    print('Error: {0}'.format(e))
    sys.exit(-1)

In Javascript:

#!/usr/bin/gjs

const { GLib, Gio, Tsparql } = imports.gi

try {
    let connection = Tsparql.SparqlConnection.new(
        Tsparql.SparqlConnectionFlags.NONE,
        null, // Database location, None creates it in-memory
        Tsparql.sparql_get_ontology_nepomuk(), // Ontology location
        null);

    let bus = Gio.bus_get_sync(Gio.BusType.SESSION, null)

    let endpoint = Tsparql.EndpointDBus.new(
        connection, bus, null, null);

    let loop = GLib.MainLoop.new(null, false);
    loop.run();

    connection.close();
} catch (e) {
    printerr(`Error: ${e.message}`)
}

Receiving notification on changes

As an additional feature over SPARQL endpoints, TinySPARQL allows for users of private and D-Bus SPARQL connections to receive notifications on changes of certain RDF classes (Those with the nrl:notify property, like nmm:MusicPiece).

This example demonstrates the use of TrackerNotifier to receive notifications on database updates.

In C:

#include <tinysparql.h>

#define REMOTE_NAME "org.freedesktop.Tracker3.Miner.Files"

void on_events (TrackerNotifier *notifier,
                const char      *service,
                const char      *graph,
                GPtrArray       *events,
                gpointer         user_data)
{
  int i;

  for (i = 0; i < events->len; i++) {
    TrackerNotifierEvent *event = g_ptr_array_index (events, i);

    g_print ("Event %d on %s\n",
             tracker_notifier_event_get_event_type (event),
             tracker_notifier_event_get_urn (event));
  }
}

int main (int   argc,
          char *argv[])
{
  g_autoptr (GError) error = NULL;
  g_autoptr (TrackerSparqlConnection) connection = NULL;
  g_autoptr (TrackerNotifier) notifier = NULL;
  g_autoptr (GMainLoop) main_loop = NULL;

  /* Create connection to remote service */
  connection = tracker_sparql_connection_bus_new (REMOTE_NAME, NULL, NULL, &error);
  if (!connection) {
    g_printerr ("Couldn't obtain a connection to the Tracker store: %s",
                error ? error->message : "unknown error");
    return 1;
  }

  /* Create notifier, and connect to its events signal */
  notifier = tracker_sparql_connection_create_notifier (connection);
  g_signal_connect (notifier, "events", G_CALLBACK (on_events), NULL);

  main_loop = g_main_loop_new (NULL, FALSE);
  g_main_loop_run (main_loop);

  tracker_sparql_connection_close (connection);

  return 0;
}

In Python:

#!/usr/bin/python3

import gi, sys
from gi.repository import GLib, Gio, Tsparql

def callback(service, graph, events):
    for event in events:
        print('Event {0} on {1}\n'.format(
            event.get_event_type(), event.get_urn()))

try:
    connection = Tsparql.SparqlConnection.bus_new(
        'org.freedesktop.Tracker3.Miner.Files',
        None, None)

    notifier = connection.create_notifier()
    notifier.connect('events', callback)

    loop = GLib.MainLoop.new(None, False)
    loop.run()

    connection.close()

except Exception as e:
    print('Error: {0}'.format(e))
    sys.exit(-1)

In Javascript:

#!/usr/bin/gjs

const { GLib, Gio, Tsparql } = imports.gi

try {
    let connection = Tsparql.SparqlConnection.bus_new(
        'org.freedesktop.Tracker3.Miner.Files',
        null, null);

    let notifier = connection.create_notifier();
    notifier.connect('events', (service, graph, events) => {
        for (let event in events)
            print (`Event ${event.get_event_type()} on ${event.get_urn()}`);
    });

    let loop = GLib.MainLoop.new(null, false);
    loop.run();

    connection.close();
} catch (e) {
    printerr(`Error: ${e.message}`)
}