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#FeedMessage
These 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; }