searchengine: Populate filesystem model in an idle
authorCarlos Garnacho <carlosg@gnome.org>
Tue, 7 Mar 2023 21:35:47 +0000 (22:35 +0100)
committerCarlos Garnacho <carlosg@gnome.org>
Wed, 8 Mar 2023 10:32:44 +0000 (11:32 +0100)
When starting a search over a very populated filesystem, it
is possible that typing the first chars will return a too
high number of results. Even though iterating through the
cursor is in itself very fast, extracting the GIO information
from those many files at once is not going to be as fast.

In order to increase interactivity (i.e. not make things
possibly sluggish) iterate the cursor in an idle function
and add search results to the filechooser model little by little.

If the user keeps typing (as it is likely will happen), there
will be better chances to cancel and proceed to the next
query timely. If not, the results will be there soon enough.

gtk/gtksearchenginetracker3.c

index ab3224d2acc1702b914f455b0b0d3ef315c76f99..f8dfd623ede5194a5a530c47ac38238adde49c4b 100644 (file)
@@ -34,6 +34,8 @@
 
 #include "gtksearchenginetracker3private.h"
 
+#define N_RESULT_BATCH_ITEMS 50
+
 #define MINER_FS_BUS_NAME "org.freedesktop.Tracker3.Miner.Files"
 
 #define SEARCH_QUERY_BASE(__PATTERN__)                                 \
@@ -63,6 +65,7 @@ struct _GtkSearchEngineTracker3
   TrackerSparqlStatement *search_recursive_query;
   TrackerSparqlStatement *search_location_query;
   GCancellable *cancellable;
+  guint idle_id;
   GtkQuery *query;
   gboolean query_pending;
 };
@@ -72,6 +75,13 @@ struct _GtkSearchEngineTracker3Class
   GtkSearchEngineClass parent_class;
 };
 
+typedef struct
+{
+  TrackerSparqlCursor *cursor;
+  GtkSearchEngineTracker3 *engine;
+  gboolean got_results;
+} CursorData;
+
 static void gtk_search_engine_tracker3_initable_iface_init (GInitableIface *iface);
 
 G_DEFINE_TYPE_WITH_CODE (GtkSearchEngineTracker3,
@@ -95,6 +105,8 @@ finalize (GObject *object)
       g_object_unref (engine->cancellable);
     }
 
+  g_clear_handle_id (&engine->idle_id, g_source_remove);
+
   g_clear_object (&engine->search_query);
   g_clear_object (&engine->search_location_query);
   if (engine->sparql_conn != NULL)
@@ -151,6 +163,59 @@ create_file_info (GFile               *file,
   return info;
 }
 
+static gboolean
+handle_cursor_idle_cb (gpointer user_data)
+{
+  CursorData *data = user_data;
+  GtkSearchEngineTracker3 *engine = data->engine;
+  TrackerSparqlCursor *cursor = data->cursor;
+  gboolean has_next;
+  GList *hits = NULL;
+  GtkSearchHit *hit;
+  int i = 0;
+
+  for (i = 0; i < N_RESULT_BATCH_ITEMS; i++)
+    {
+      const gchar *url;
+
+      has_next = tracker_sparql_cursor_next (cursor, NULL, NULL);
+      if (!has_next)
+        break;
+
+      url = tracker_sparql_cursor_get_string (cursor, 0, NULL);
+      hit = g_slice_new0 (GtkSearchHit);
+      hit->file = g_file_new_for_uri (url);
+      hit->info = create_file_info (hit->file, cursor);
+      hits = g_list_prepend (hits, hit);
+      data->got_results = TRUE;
+    }
+
+  _gtk_search_engine_hits_added (GTK_SEARCH_ENGINE (engine), hits);
+
+  g_list_free_full (hits, free_hit);
+
+  if (has_next)
+    return G_SOURCE_CONTINUE;
+  else
+    {
+      engine->idle_id = 0;
+      return G_SOURCE_REMOVE;
+    }
+}
+
+static void
+cursor_data_free (gpointer user_data)
+{
+  CursorData *data = user_data;
+
+  tracker_sparql_cursor_close (data->cursor);
+  _gtk_search_engine_finished (GTK_SEARCH_ENGINE (data->engine),
+                               data->got_results);
+  g_object_unref (data->cursor);
+  g_object_unref (data->engine);
+  g_free (data);
+}
+
 static void
 query_callback (TrackerSparqlStatement *statement,
                 GAsyncResult           *res,
@@ -158,9 +223,8 @@ query_callback (TrackerSparqlStatement *statement,
 {
   GtkSearchEngineTracker3 *engine;
   TrackerSparqlCursor *cursor;
-  GList *hits = NULL;
   GError *error = NULL;
-  GtkSearchHit *hit;
+  CursorData *data;
 
   engine = GTK_SEARCH_ENGINE_TRACKER3 (user_data);
 
@@ -176,25 +240,14 @@ query_callback (TrackerSparqlStatement *statement,
       return;
     }
 
-  while (tracker_sparql_cursor_next (cursor, NULL, NULL))
-    {
-      const char *url;
-
-      url = tracker_sparql_cursor_get_string (cursor, 0, NULL);
-      hit = g_slice_new0 (GtkSearchHit);
-      hit->file = g_file_new_for_uri (url);
-      hit->info = create_file_info (hit->file, cursor);
-      hits = g_list_prepend (hits, hit);
-    }
-
-  tracker_sparql_cursor_close (cursor);
-
-  _gtk_search_engine_hits_added (GTK_SEARCH_ENGINE (engine), hits);
-  _gtk_search_engine_finished (GTK_SEARCH_ENGINE (engine), hits != NULL);
+  data = g_new0 (CursorData, 1);
+  data->cursor = cursor;
+  data->engine = engine;
 
-  g_list_free_full (hits, free_hit);
-  g_object_unref (engine);
-  g_object_unref (cursor);
+  engine->idle_id =
+    g_idle_add_full (G_PRIORITY_DEFAULT_IDLE,
+                     handle_cursor_idle_cb,
+                     data, cursor_data_free);
 }
 
 static void
@@ -273,6 +326,8 @@ gtk_search_engine_tracker3_stop (GtkSearchEngine *engine)
       g_cancellable_cancel (tracker->cancellable);
       tracker->query_pending = FALSE;
     }
+
+  g_clear_handle_id (&tracker->idle_id, g_source_remove);
 }
 
 static void