Tracker – 3.0: Examples
Description: Reference for Tracker-3.0: Examples

This document shows some real examples of usage of the Tracker SPARQL 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 <libtracker-sparql/tracker-sparql.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, Tracker

    try:
        connection = Tracker.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, Tracker } = imports.gi

    try {
        let connection = Tracker.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 <libtracker-sparql/tracker-sparql.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, Tracker

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

        # Create a resource containing RDF data
        resource = Tracker.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, Tracker } = imports.gi

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

        // Create a resource containing RDF data
        let resource = Tracker.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 <libtracker-sparql/tracker-sparql.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, Tracker

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

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

        endpoint = Tracker.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, Tracker } = imports.gi

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

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

        let endpoint = Tracker.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, Tracker 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 <libtracker-sparql/tracker-sparql.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, Tracker

    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 = Tracker.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, Tracker } = imports.gi

    try {
        let connection = Tracker.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}`)
    }