1. Following Data Updates
2. Tracker 0.9.x and 0.10.x
2.1. Introduction
If your application is interested in changes in Tracker's RDF store you need to subscribe to the appropriate D-Bus signal from the Tracker service.
The daemon tracker-store is responsible for publishing the D-Bus interface:
- D-Bus service : org.freedesktop.Tracker1
- D-Bus interface : org.freedesktop.Tracker1.Resources
Signal name : GraphUpdated
2.2. org.freedesktop.Tracker1.Resources
<node name="/org/freedesktop/Tracker1/Resources">
<interface name="org.freedesktop.Tracker1.Resources">
<signal name="GraphUpdated">
<arg type="s" name ="class_name" />
<arg type="a(iiii)" name="deletes" />
<arg type="a(iiii)" name="inserts" />
</signal>
</interface>
</node>Detail of the arrays:
- The first i is the graph-ID
- The second i is the subject-ID
- The third i is the predicate-ID
- The forth i is the object-ID if the object isn't a literal, else its value is 0
The arrays are sorted on subject-ID.
2.3. Which classes
The signaling system isn't enabled for all classes. Only the classes that have tracker:notify set to true have support. This is defined in the ontology. If it is not, the class will not be signalled when data changes for that class.
You can see which classes qualify for notifications using the following SPARQL command:
$ tracker-sparql -q "select ?class where { ?class tracker:notify true }"
Result: 19
http://www.semanticdesktop.org/ontologies/2007/08/15/nao#Tag
http://www.semanticdesktop.org/ontologies/2007/03/22/nco#Contact
http://www.semanticdesktop.org/ontologies/2007/03/22/nco#IMAccount
http://www.semanticdesktop.org/ontologies/2007/03/22/nco#PersonContact
http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#Image
http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#Audio
http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#BookmarkFolder
http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#Bookmark
http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#WebHistory
http://www.semanticdesktop.org/ontologies/2007/03/22/nmo#Message
http://www.semanticdesktop.org/ontologies/2007/04/02/ncal#Event
http://www.semanticdesktop.org/ontologies/2007/04/02/ncal#Todo
http://www.semanticdesktop.org/ontologies/2007/04/02/ncal#Journal
http://www.tracker-project.org/temp/nmm#MusicPiece
http://www.tracker-project.org/temp/nmm#Video
http://www.tracker-project.org/temp/mto#TransferElement
http://www.tracker-project.org/temp/mto#Transfer
http://www.tracker-project.org/temp/mfo#FeedChannel
http://www.tracker-project.org/temp/mfo#FeedMessage
2.4. Interesting predicates
A interesting predicate to consider is tracker:available. This single value boolean property will be set on resources that are available to true and will either be unset or will be set to false for resources that aren't available. For example with the removable volume support you can detect this way which resources disappear as a result of a removable volume being unmounted.
2.5. SPARQL features that will help you
2.5.1. tracker:id() combined with IN
SELECT ?t { ?r nie:title ?t .
FILTER (tracker:id(?r) IN (800, 801, 802, 807)) }Where 800, 801, 802 and 807 will be the IDs that you receive in the class signal. And with tracker:uri(int id) it goes like:
2.5.2. tracker:subject()
SELECT tracker:uri (800) tracker:uri (801)
tracker:uri (802) tracker:uri (807) { }
2.5.3. Deletes and creates
Note that if you want to track deletes of resources, that you'll need a local table with ID -> for example URI. As you can't query on the ID anymore by the time the resource is deleted. Resource deletes and creates have the predicate ID for rdf:type in the array.
2.5.4. More info on SPARQL features like IN
2.6. Examples
2.6.1. GDBus
#include <gio/gio.h>
#include <libtracker-sparql/tracker-sparql.h>
static TrackerSparqlConnection *con;
static void
handle_statement (gint subject, gint predicate)
{
gchar *query, *pred;
TrackerSparqlCursor *cursor;
query = g_strdup_printf ("SELECT tracker:uri (%d) tracker:uri(%d) {}",
subject, predicate);
cursor = tracker_sparql_connection_query (con, query, NULL, NULL);
g_free (query);
tracker_sparql_cursor_next (cursor, NULL, NULL);
pred = g_strdup (tracker_sparql_cursor_get_string (cursor, 1, NULL));
query = g_strdup_printf ("SELECT ?t { <%s> <%s> ?t }",
tracker_sparql_cursor_get_string (cursor, 0, NULL),
pred);
g_object_unref (cursor);
cursor = tracker_sparql_connection_query (con, query, NULL, NULL);
g_free (query);
while (tracker_sparql_cursor_next (cursor, NULL, NULL))
g_print ("\t%s = %s\n", pred, tracker_sparql_cursor_get_string (cursor, 0, NULL));
g_print ("\n");
g_free (pred);
g_object_unref (cursor);
}
static void
class_signal_cb (GDBusConnection *connection,
const gchar *sender_name,
const gchar *object_path,
const gchar *interface_name,
const gchar *signal_name,
GVariant *parameters,
gpointer user_data)
{
GVariantIter *iter1, *iter2;
gchar *class_name;
gint graph = 0, subject = 0, predicate = 0, object = 0;
g_variant_get (parameters, "(&sa(iiii)a(iiii))", &class_name, &iter1, &iter2);
g_print ("%s:\n", class_name);
while (g_variant_iter_loop (iter1, "(iiii)", &graph, &subject, &predicate, &object))
handle_statement (subject, predicate);
while (g_variant_iter_loop (iter2, "(iiii)", &graph, &subject, &predicate, &object))
handle_statement (subject, predicate);
g_variant_iter_free (iter1);
g_variant_iter_free (iter2);
}
gint
main (gint argc, gchar *argv[])
{
GMainLoop *loop;
GError *error = NULL;
GDBusConnection *connection;
guint signal_id;
g_type_init ();
loop = g_main_loop_new (NULL, FALSE);
con = tracker_sparql_connection_get (NULL, &error);
connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
signal_id = g_dbus_connection_signal_subscribe (connection,
TRACKER_DBUS_SERVICE,
TRACKER_DBUS_INTERFACE_RESOURCES,
"GraphUpdated",
TRACKER_DBUS_OBJECT_RESOURCES,
NULL, /* Use class-name here */
G_DBUS_SIGNAL_FLAGS_NONE,
class_signal_cb,
NULL,
NULL);
g_main_loop_run (loop);
g_dbus_connection_signal_unsubscribe (connection, signal_id);
g_main_loop_unref (loop);
g_object_unref (con);
g_object_unref (connection);
return 0;
}
2.6.2. dbus-1
#include <dbus/dbus-glib-bindings.h>
#include <dbus/dbus-glib-lowlevel.h>
#include <libtracker-sparql/tracker-sparql.h>
#define TRACKER_SERVICE "org.freedesktop.Tracker1"
#define TRACKER_RESOURCES_OBJECT "/org/freedesktop/Tracker1/Resources"
#define TRACKER_INTERFACE_RESOURCES "org.freedesktop.Tracker1.Resources"
#define DBUS_MATCH_STR "type='signal', " \
"sender='" TRACKER_SERVICE "', " \
"path='" TRACKER_RESOURCES_OBJECT "', " \
"interface='" TRACKER_INTERFACE_RESOURCES "'"
static TrackerSparqlConnection *con;
static void
handle_statement (gint subject, gint predicate)
{
gchar *query, *pred;
TrackerSparqlCursor *cursor;
query = g_strdup_printf ("SELECT tracker:uri (%d) tracker:uri (%d) {}",
subject, predicate);
cursor = tracker_sparql_connection_query (con, query, NULL, NULL);
g_free (query);
tracker_sparql_cursor_next (cursor, NULL, NULL);
pred = g_strdup (tracker_sparql_cursor_get_string (cursor, 1, NULL));
query = g_strdup_printf ("SELECT ?t { <%s> <%s> ?t }",
tracker_sparql_cursor_get_string (cursor, 0, NULL),
pred);
g_object_unref (cursor);
cursor = tracker_sparql_connection_query (con, query, NULL, NULL);
g_free (query);
while (tracker_sparql_cursor_next (cursor, NULL, NULL))
g_print ("\t%s = %s\n", pred, tracker_sparql_cursor_get_string (cursor, 0, NULL));
g_print ("\n");
g_free (pred);
g_object_unref (cursor);
}
static void
class_signal_cb (DBusMessage *message)
{
DBusMessageIter iter, arr;
gchar *class_name;
gint arg_type, i;
dbus_message_iter_init (message, &iter);
dbus_message_iter_get_basic (&iter, &class_name);
g_print ("%s:\n", class_name);
for (i = 0; i < 2; i++) {
dbus_message_iter_next (&iter);
dbus_message_iter_recurse (&iter, &arr);
while ((arg_type = dbus_message_iter_get_arg_type (&arr)) != DBUS_TYPE_INVALID) {
DBusMessageIter strct;
gint graph = 0, subject = 0, predicate = 0, object = 0;
dbus_message_iter_recurse (&arr, &strct);
dbus_message_iter_get_basic (&strct, &graph);
dbus_message_iter_next (&strct);
dbus_message_iter_get_basic (&strct, &subject);
dbus_message_iter_next (&strct);
dbus_message_iter_get_basic (&strct, &predicate);
dbus_message_iter_next (&strct);
dbus_message_iter_get_basic (&strct, &object);
handle_statement (subject, predicate);
dbus_message_iter_next (&arr);
}
}
}
static DBusHandlerResult
message_filter (DBusConnection *connection, DBusMessage *message, gpointer ud)
{
if (dbus_message_is_signal (message, TRACKER_INTERFACE_RESOURCES, "GraphUpdated")) {
class_signal_cb (message);
return DBUS_HANDLER_RESULT_HANDLED;
}
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
gint
main (gint argc, gchar *argv[])
{
GMainLoop *loop;
GError *error = NULL;
DBusConnection *connection;
g_type_init ();
loop = g_main_loop_new (NULL, FALSE);
con = tracker_sparql_connection_get (&error);
connection = dbus_bus_get_private (DBUS_BUS_SESSION, NULL);
dbus_bus_request_name (connection, TRACKER_SERVICE, 0, NULL);
dbus_connection_add_filter (connection, message_filter, NULL, NULL);
dbus_bus_add_match (connection, DBUS_MATCH_STR, NULL);
dbus_connection_setup_with_g_main (connection, NULL);
g_main_loop_run (loop);
g_main_loop_unref (loop);
g_object_unref (con);
dbus_connection_unref (connection);
return 0;
}
3. Tracker 0.8
3.1. Introduction
If your application is interested in changes in Tracker's RDF store you need to subscribe to the appropriate D-Bus signals from the Tracker service.
The daemon tracker-store is responsible for publishing these D-Bus interfaces:
- D-Bus service : org.freedesktop.Tracker1
- D-Bus interface : org.freedesktop.Tracker1.Resources.Class
3.2. org.freedesktop.Tracker1.Resources.Class
<node name="/org/freedesktop/Tracker1/Resources/Classes/$pfx/$class_name">
<interface name="org.freedesktop.Tracker1.Resources.Class">
<signal name="SubjectsChanged">
<arg type="as" name="subject" />
<arg type="as" name="predicate" />
</signal>
<signal name="SubjectsAdded">
<arg type="as" name="subject" />
</signal>
<signal name="SubjectsRemoved">
<arg type="as" name="subject" />
</signal>
</interface>
</node>
3.3. D-Bus Objects
The allowance filter is a query that matches every class that has tracker:notify set to true. This is defined in the ontology. If it is not, the class will not be signalled when data changes for that class.
allowance_filter = SELECT ?class WHERE { ?class tracker:notify true }
foreach $pfx_colon_class_name in $allowance_filter.exec ():
/org/freedesktop/Tracker1/Resources/Classes/$pfx_colon_class_name
(with : replaced by / in $pfx_colon_class_name)You can see which classes qualify for notifications using the following SPARQL command:
$ tracker-sparql -q "select ?class where { ?class tracker:notify true }"
Result: 19
http://www.semanticdesktop.org/ontologies/2007/08/15/nao#Tag
http://www.semanticdesktop.org/ontologies/2007/03/22/nco#Contact
http://www.semanticdesktop.org/ontologies/2007/03/22/nco#IMAccount
http://www.semanticdesktop.org/ontologies/2007/03/22/nco#PersonContact
http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#Image
http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#Audio
http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#BookmarkFolder
http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#Bookmark
http://www.semanticdesktop.org/ontologies/2007/03/22/nfo#WebHistory
http://www.semanticdesktop.org/ontologies/2007/03/22/nmo#Message
http://www.semanticdesktop.org/ontologies/2007/04/02/ncal#Event
http://www.semanticdesktop.org/ontologies/2007/04/02/ncal#Todo
http://www.semanticdesktop.org/ontologies/2007/04/02/ncal#Journal
http://www.tracker-project.org/temp/nmm#MusicPiece
http://www.tracker-project.org/temp/nmm#Video
http://www.tracker-project.org/temp/mto#TransferElement
http://www.tracker-project.org/temp/mto#Transfer
http://www.tracker-project.org/temp/mfo#FeedChannel
http://www.tracker-project.org/temp/mfo#FeedMessageThese classes are used in the following form:
/org/freedesktop/Tracker1/Resources/Classes/nfo/Document /org/freedesktop/Tracker1/Resources/Classes/nco/Contact
Application developers can use the SPARQL query, or the DBus method ListNames to get all classes that can be monitored.
3.4. Example
#include <dbus/dbus-glib-bindings.h>
#include <dbus/dbus-glib-lowlevel.h>
static void
subjects_changed_cb (DBusGProxy *proxy, const gchar **subjects, const gchar **predicates)
{
int i;
for (i = 0; subjects[i] != NULL && predicates[i] != NULL; i++) {
g_print ("<%s>'s <%s> changed", subjects[i], predicates[i]);
}
}
int main (int **argc, char **argv)
{
GMainLoop *loop;
GError *error = NULL;
DBusGConnection *connection;
DBusGProxy *dbus_proxy;
loop = g_main_loop_new (NULL, FALSE);
connection = dbus_g_bus_get (DBUS_BUS_SESSION, &error);
dbus_proxy = dbus_g_proxy_new_for_name (connection,
"org.freedesktop.Tracker1",
"/org/freedesktop/Tracker1/Resources/Classes/nco/Contact",
"org.freedesktop.Tracker1.Resources.Class");
dbus_g_proxy_add_signal (dbus_proxy, "SubjectsChanged",
G_TYPE_STRV, G_TYPE_STRV
G_TYPE_INVALID);
dbus_g_proxy_connect_signal (dbus_proxy, "SubjectsChanged",
G_CALLBACK (subjects_changed_cb),
NULL, NULL);
g_main_loop_run (loop);
g_object_unref (dbus_proxy);
g_main_loop_unref (loop);
return 0;
}