nautilus-3.6.3/libnautilus-private/nautilus-search-engine-tracker.c

No issues found

  1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
  2 /*
  3  * Copyright (C) 2005 Mr Jamie McCracken
  4  *
  5  * Nautilus is free software; you can redistribute it and/or
  6  * modify it under the terms of the GNU General Public License as
  7  * published by the Free Software Foundation; either version 2 of the
  8  * License, or (at your option) any later version.
  9  *
 10  * Nautilus is distributed in the hope that it will be useful,
 11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 13  * General Public License for more details.
 14  *
 15  * You should have received a copy of the GNU General Public
 16  * License along with this program; see the file COPYING.  If not,
 17  * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 18  * Boston, MA 02111-1307, USA.
 19  *
 20  * Author: Jamie McCracken <jamiemcc@gnome.org>
 21  *
 22  */
 23 
 24 #include <config.h>
 25 #include "nautilus-search-hit.h"
 26 #include "nautilus-search-provider.h"
 27 #include "nautilus-search-engine-tracker.h"
 28 #include <string.h>
 29 #include <gio/gio.h>
 30 
 31 #include <libtracker-sparql/tracker-sparql.h>
 32 
 33 struct NautilusSearchEngineTrackerDetails {
 34 	TrackerSparqlConnection *connection;
 35 	NautilusQuery *query;
 36 
 37 	gboolean       query_pending;
 38 	GQueue        *hits_pending;
 39 
 40 	GCancellable  *cancellable;
 41 };
 42 
 43 static void nautilus_search_provider_init (NautilusSearchProviderIface  *iface);
 44 
 45 G_DEFINE_TYPE_WITH_CODE (NautilusSearchEngineTracker,
 46 			 nautilus_search_engine_tracker,
 47 			 G_TYPE_OBJECT,
 48 			 G_IMPLEMENT_INTERFACE (NAUTILUS_TYPE_SEARCH_PROVIDER,
 49 						nautilus_search_provider_init))
 50 
 51 static void
 52 finalize (GObject *object)
 53 {
 54 	NautilusSearchEngineTracker *tracker;
 55 
 56 	tracker = NAUTILUS_SEARCH_ENGINE_TRACKER (object);
 57 
 58 	if (tracker->details->cancellable) {
 59 		g_cancellable_cancel (tracker->details->cancellable);
 60 		g_clear_object (&tracker->details->cancellable);
 61 	}
 62 
 63 	g_clear_object (&tracker->details->query);
 64 	g_clear_object (&tracker->details->connection);
 65 	g_queue_free_full (tracker->details->hits_pending, g_object_unref);
 66 
 67 	G_OBJECT_CLASS (nautilus_search_engine_tracker_parent_class)->finalize (object);
 68 }
 69 
 70 #define BATCH_SIZE 100
 71 
 72 static void
 73 check_pending_hits (NautilusSearchEngineTracker *tracker,
 74 		    gboolean force_send)
 75 {
 76 	GList *hits = NULL;
 77 	NautilusSearchHit *hit;
 78 
 79 	if (!force_send &&
 80 	    g_queue_get_length (tracker->details->hits_pending) < BATCH_SIZE) {
 81 		return;
 82 	}
 83 
 84 	while ((hit = g_queue_pop_head (tracker->details->hits_pending))) {
 85 		hits = g_list_prepend (hits, hit);
 86 	}
 87 
 88 	nautilus_search_provider_hits_added (NAUTILUS_SEARCH_PROVIDER (tracker), hits);
 89 	g_list_free_full (hits, g_object_unref);
 90 }
 91 
 92 static void
 93 search_finished (NautilusSearchEngineTracker *tracker,
 94 		 GError *error)
 95 {
 96 	if (error == NULL) {
 97 		check_pending_hits (tracker, TRUE);
 98 	} else {
 99 		g_queue_foreach (tracker->details->hits_pending, (GFunc) g_object_unref, NULL);
100 		g_queue_clear (tracker->details->hits_pending);
101 	}
102 
103 	tracker->details->query_pending = FALSE;
104 
105 	if (error && !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
106 		nautilus_search_provider_error (NAUTILUS_SEARCH_PROVIDER (tracker), error->message);
107 	} else {
108 		nautilus_search_provider_finished (NAUTILUS_SEARCH_PROVIDER (tracker));
109 	}
110 
111 	g_object_unref (tracker);
112 }
113 
114 static void cursor_callback (GObject      *object,
115 			     GAsyncResult *result,
116 			     gpointer      user_data);
117 
118 static void
119 cursor_next (NautilusSearchEngineTracker *tracker,
120              TrackerSparqlCursor    *cursor)
121 {
122 	tracker_sparql_cursor_next_async (cursor,
123 	                                  tracker->details->cancellable,
124 	                                  cursor_callback,
125 	                                  tracker);
126 }
127 
128 static void
129 cursor_callback (GObject      *object,
130                  GAsyncResult *result,
131                  gpointer      user_data)
132 {
133 	NautilusSearchEngineTracker *tracker;
134 	GError *error = NULL;
135 	TrackerSparqlCursor *cursor;
136 	NautilusSearchHit *hit;
137 	const char *uri;
138 	const char *mtime_str;
139 	const char *atime_str;
140 	GTimeVal tv;
141 	gdouble rank, match;
142 	gboolean success;
143 	gchar *basename;
144 
145 	tracker = NAUTILUS_SEARCH_ENGINE_TRACKER (user_data);
146 
147 	cursor = TRACKER_SPARQL_CURSOR (object);
148 	success = tracker_sparql_cursor_next_finish (cursor, result, &error);
149 
150 	if (!success) {
151 		search_finished (tracker, error);
152 
153 		g_clear_error (&error);
154 		g_clear_object (&cursor);
155 
156 		return;
157 	}
158 
159 	/* We iterate result by result, not n at a time. */
160 	uri = tracker_sparql_cursor_get_string (cursor, 0, NULL);
161 	rank = tracker_sparql_cursor_get_double (cursor, 1);
162 	mtime_str = tracker_sparql_cursor_get_string (cursor, 2, NULL);
163 	atime_str = tracker_sparql_cursor_get_string (cursor, 3, NULL);
164 	basename = g_path_get_basename (uri);
165 
166 	hit = nautilus_search_hit_new (uri);
167 	match = nautilus_query_matches_string (tracker->details->query, basename);
168 	nautilus_search_hit_set_fts_rank (hit, rank + match);
169 	g_free (basename);
170 
171 	if (g_time_val_from_iso8601 (mtime_str, &tv)) {
172 		GDateTime *dt;
173 		dt = g_date_time_new_from_timeval_local (&tv);
174 		nautilus_search_hit_set_modification_time (hit, dt);
175 		g_date_time_unref (dt);
176 	} else {
177 		g_warning ("unable to parse mtime: %s", mtime_str);
178 	}
179 	if (g_time_val_from_iso8601 (atime_str, &tv)) {
180 		GDateTime *dt;
181 		dt = g_date_time_new_from_timeval_local (&tv);
182 		nautilus_search_hit_set_access_time (hit, dt);
183 		g_date_time_unref (dt);
184 	} else {
185 		g_warning ("unable to parse atime: %s", atime_str);
186 	}
187 
188 	g_queue_push_head (tracker->details->hits_pending, hit);
189 	check_pending_hits (tracker, FALSE);
190 
191 	/* Get next */
192 	cursor_next (tracker, cursor);
193 }
194 
195 static void
196 query_callback (GObject      *object,
197                 GAsyncResult *result,
198                 gpointer      user_data)
199 {
200 	NautilusSearchEngineTracker *tracker;
201 	TrackerSparqlConnection *connection;
202 	TrackerSparqlCursor *cursor;
203 	GError *error = NULL;
204 
205 	tracker = NAUTILUS_SEARCH_ENGINE_TRACKER (user_data);
206 
207 	connection = TRACKER_SPARQL_CONNECTION (object);
208 	cursor = tracker_sparql_connection_query_finish (connection,
209 	                                                 result,
210 	                                                 &error);
211 
212 	if (error != NULL) {
213 		search_finished (tracker, error);
214 		g_error_free (error);
215 	} else {
216 		cursor_next (tracker, cursor);
217 	}
218 }
219 
220 static gboolean
221 search_finished_idle (gpointer user_data)
222 {
223 	NautilusSearchEngineTracker *tracker = user_data;
224 
225 	search_finished (tracker, NULL);
226 
227 	return FALSE;
228 }
229 
230 static void
231 nautilus_search_engine_tracker_start (NautilusSearchProvider *provider)
232 {
233 	NautilusSearchEngineTracker *tracker;
234 	gchar	*query_text, *search_text, *location_uri, *downcase;
235 	GString *sparql;
236 	GList *mimetypes, *l;
237 	gint mime_count;
238 
239 	tracker = NAUTILUS_SEARCH_ENGINE_TRACKER (provider);
240 
241 	if (tracker->details->query_pending) {
242 		return;
243 	}
244 
245 	g_object_ref (tracker);
246 	tracker->details->query_pending = TRUE;
247 
248 	if (tracker->details->connection == NULL) {
249 		g_idle_add (search_finished_idle, provider);
250 		return;
251 	}
252 
253 	query_text = nautilus_query_get_text (tracker->details->query);
254 	downcase = g_utf8_strdown (query_text, -1);
255 	search_text = tracker_sparql_escape_string (downcase);
256 	g_free (query_text);
257 	g_free (downcase);
258 
259 	location_uri = nautilus_query_get_location (tracker->details->query);
260 	mimetypes = nautilus_query_get_mime_types (tracker->details->query);
261 
262 	mime_count = g_list_length (mimetypes);
263 
264 	sparql = g_string_new ("SELECT DISTINCT nie:url(?urn) fts:rank(?urn) tracker:coalesce(nfo:fileLastModified(?urn), nie:contentLastModified(?urn)) AS ?mtime tracker:coalesce(nfo:fileLastAccessed(?urn), nie:contentAccessed(?urn)) AS ?atime "
265 			       "WHERE {"
266 			       "  ?urn a nfo:FileDataObject ;"
267 			       "  tracker:available true ; ");
268 
269 	if (mime_count > 0) {
270 		g_string_append (sparql, "nie:mimeType ?mime ;");
271 	}
272 
273 	g_string_append_printf (sparql,
274 				" fts:match '\"%s*\"' . FILTER ("
275 				" tracker:uri-is-descendant('%s', nie:url(?urn)) &&"
276 				" fn:contains(fn:lower-case(nfo:fileName(?urn)), '%s')",
277 				search_text, location_uri, search_text);
278 
279 	if (mime_count > 0) {
280 		g_string_append (sparql, " && (");
281 
282 		for (l = mimetypes; l != NULL; l = l->next) {
283 			if (l != mimetypes) {
284 				g_string_append (sparql, " || ");
285 			}
286 
287 			g_string_append_printf (sparql, "fn:contains(?mime, '%s')",
288 						(gchar *) l->data);
289 		}
290 		g_string_append (sparql, ")");
291 	}
292 
293 	g_string_append (sparql, ")} ORDER BY DESC (fts:rank(?urn))");
294 
295 	tracker->details->cancellable = g_cancellable_new ();
296 	tracker_sparql_connection_query_async (tracker->details->connection,
297 					       sparql->str,
298 					       tracker->details->cancellable,
299 					       query_callback,
300 					       tracker);
301 	g_string_free (sparql, TRUE);
302 
303 	g_free (search_text);
304 	g_free (location_uri);
305 	g_list_free_full (mimetypes, g_free);
306 }
307 
308 static void
309 nautilus_search_engine_tracker_stop (NautilusSearchProvider *provider)
310 {
311 	NautilusSearchEngineTracker *tracker;
312 
313 	tracker = NAUTILUS_SEARCH_ENGINE_TRACKER (provider);
314 	
315 	if (tracker->details->query_pending) {
316 		g_cancellable_cancel (tracker->details->cancellable);
317 		g_clear_object (&tracker->details->cancellable);
318 		tracker->details->query_pending = FALSE;
319 	}
320 }
321 
322 static void
323 nautilus_search_engine_tracker_set_query (NautilusSearchProvider *provider,
324 					  NautilusQuery *query)
325 {
326 	NautilusSearchEngineTracker *tracker;
327 
328 	tracker = NAUTILUS_SEARCH_ENGINE_TRACKER (provider);
329 
330 	g_object_ref (query);
331 	g_clear_object (&tracker->details->query);
332 	tracker->details->query = query;
333 }
334 
335 static void
336 nautilus_search_provider_init (NautilusSearchProviderIface *iface)
337 {
338 	iface->set_query = nautilus_search_engine_tracker_set_query;
339 	iface->start = nautilus_search_engine_tracker_start;
340 	iface->stop = nautilus_search_engine_tracker_stop;
341 }
342 
343 static void
344 nautilus_search_engine_tracker_class_init (NautilusSearchEngineTrackerClass *class)
345 {
346 	GObjectClass *gobject_class;
347 
348 	gobject_class = G_OBJECT_CLASS (class);
349 	gobject_class->finalize = finalize;
350 
351 	g_type_class_add_private (class, sizeof (NautilusSearchEngineTrackerDetails));
352 }
353 
354 static void
355 nautilus_search_engine_tracker_init (NautilusSearchEngineTracker *engine)
356 {
357 	GError *error = NULL;
358 
359 	engine->details = G_TYPE_INSTANCE_GET_PRIVATE (engine, NAUTILUS_TYPE_SEARCH_ENGINE_TRACKER,
360 						       NautilusSearchEngineTrackerDetails);
361 	engine->details->hits_pending = g_queue_new ();
362 
363 	engine->details->connection = tracker_sparql_connection_get (NULL, &error);
364 
365 	if (error) {
366 		g_warning ("Could not establish a connection to Tracker: %s", error->message);
367 		g_error_free (error);
368 	}
369 }
370 
371 
372 NautilusSearchEngineTracker *
373 nautilus_search_engine_tracker_new (void)
374 {
375 	return g_object_new (NAUTILUS_TYPE_SEARCH_ENGINE_TRACKER, NULL);
376 }